Author: tack
Date: Wed Oct 11 20:36:53 2006
New Revision: 1917

Modified:
   trunk/WIP/candy/src/object.py

Log:
Stuff.


Modified: trunk/WIP/candy/src/object.py
==============================================================================
--- trunk/WIP/candy/src/object.py       (original)
+++ trunk/WIP/candy/src/object.py       Wed Oct 11 20:36:53 2006
@@ -9,10 +9,12 @@
 
 log = logging.getLogger('candy')
 
-SYNC_NOOP         = 0
-SYNC_NEEDS_RENDER = 1
-SYNC_NOT_FINISHED = 2
-SYNC_SIZE_CHANGED = 4
+SYNC_NOOP         = 0x000
+SYNC_NEEDS_RENDER = 0x001
+SYNC_NOT_FINISHED = 0x002
+SYNC_SIZE_CHANGED = 0x004
+SYNC_POS_CHANGED  = 0x008
+
 
 class CanvasError(Exception):
     pass
@@ -25,6 +27,11 @@
         print
 
 class Object(object):
+    """
+    Base class for all canvas objects.  Implements properties common to all
+    objects (color, padding, pos, etc.) and logic necesary for synchronizing
+    properties, as well as basic layout logic.
+    """
 
     @classmethod
     def _add_properties(cls, prepend = (), append = ()):
@@ -32,11 +39,11 @@
         Adds new properties to the class object, appending or prepending to
         the current list (of the superclass), removing any duplicates.  This
         is done as a class method because properties are constant for all
-        instances of a clsas, so there's no sense in incurring the overhead
+        instances of a class, so there's no sense in incurring the overhead
         of allocating the property list during instantiation.
         """
         cur_properties = ()
-        if hasattr(cls, "_property_list"):
+        if hasattr(cls, '_property_list'):
             cur_properties = cls._property_list
 
         # Current property list with new prepend/append properties removed.
@@ -79,22 +86,25 @@
         # Weak reference to our parent.
         self._parent = None
 
-        self['name'] = kwargs.get('name')
+        self['name']    = kwargs.get('name')
+        self['display'] = kwargs.get('display', True)
         self['visible'] = kwargs.get('visible', True)
         self['opacity'] = kwargs.get('opacity', 1.0)
-        self['color'] = kwargs.get('color', (255, 255, 255))
+        self['color']   = kwargs.get('color', (255, 255, 255))
         self['passive'] = kwargs.get('passive', False)
-        self['margin'] = kwargs.get('margin', (0, 0, 0, 0))
-        self['padding'] = kwargs.get('padding', (0, 0, 0, 0))
-        self['size'] = kwargs.get('width', 'auto'), kwargs.get('height', 
'auto')
-        if 'size' in kwargs:
-            self['size'] = kwargs.get('size', ('auto', 'auto'))
-
-        self['pos'] = kwargs.get('left', 0), kwargs.get('top', 0), \
-                      kwargs.get('right'), kwargs.get('bottom'), \
-                      kwargs.get('hcenter'), kwargs.get('vcenter')
+        self['margin']  = kwargs.get('margin_top', 0), 
kwargs.get('margin_right', 0), \
+                          kwargs.get('margin_bottom', 0), 
kwargs.get('margin_left', 0)
+        self['margin']  = kwargs.get('margin', self['margin'])
+        self['padding'] = kwargs.get('padding_top', 0), 
kwargs.get('padding_right', 0), \
+                          kwargs.get('padding_bottom', 0), 
kwargs.get('padding_left', 0)
+        self['padding'] = kwargs.get('padding', self['padding'])
+        self['size']    = kwargs.get('width', 'auto'), kwargs.get('height', 
'auto')
+        self['size']    = kwargs.get('size', self['size'])
+        self['pos']     = kwargs.get('left', 0), kwargs.get('top', 0), \
+                          kwargs.get('right'), kwargs.get('bottom'), \
+                          kwargs.get('hcenter'), kwargs.get('vcenter')
         if 'pos' in kwargs:
-            self['pos'] = tuple(kwargs.get('pos')) + (None, None, None, None)
+            self['pos'] = tuple(kwargs['pos']) + (None, None, None, None)
 
 
     def __repr__(self):
@@ -116,17 +126,31 @@
 
 
     def __setitem__(self, key, value):
+        """
+        Interface to property setters.  Calls _set_property_foo method (where
+        foo is the name of the property) if it exists, otherwise it calls
+        _set_property_generic.  If the property-specific setter returns non-
+        None, then the value returned is passed to _set_property_generic.
+
+        Property setters are passed both key and value as arguments.  This is
+        an implementation idiosynchrasy and is for slight performance gains.
+        """
+
         key = key.replace('-', '_')
         if self._properties.get(key) == value:
             return
 
-        set_func = getattr(self, "_set_property_" + key, 
self._set_property_generic)
+        set_func = getattr(self, '_set_property_' + key, 
self._set_property_generic)
         value = set_func(key, value)
         if value is not None:
             self._set_property_generic(key, value)
 
 
     def _set_property_generic(self, key, value):
+        """
+        Generic property setter.  Stores the value in the _properties dict and
+        marks that property as dirty for next resync.
+        """
         self._properties[key] = value
         if key not in ('name',):
             self._dirty_property(key)
@@ -135,10 +159,13 @@
     def _dirty_property(self, prop):
         """
         Marks a property as dirty, meaning the property value we have is out-
-        of-sync with the property on the canvas.  Next mainloop step, the 
-        property will be synced and the canvas rerendered if necessary.  Any
-        cached computed values for this property will also be deleted, forcing
-        them to be recalculated next time they are needed.
+        of-sync with the property on the canvas.  At the end of the current
+        mainloop step, the property will be synced and the canvas rerendered if
+        necessary.  Any cached computed values for this property will also be
+        deleted, forcing them to be recalculated next time they are needed.
+        Before a computed property is deleted, it is saved as the property name
+        prefixed with last-.  e.g. if pos is deleted, last-pos and last-pos-abs
+        will hold the old values.
         """
         if prop in self._dirty_properties:
             return
@@ -148,9 +175,15 @@
         except KeyError:
             raise ValueError, "Unknown property '%s'" % prop
 
+        # Remove the property from _computed_properties dict if it exists 
+        # there.
         if prop in self._computed_properties:
             self._computed_properties['last-' + prop] = 
self._computed_properties[prop]
             del self._computed_properties[prop]
+
+        # Properties suffixed with -abs are the absolute computed value: that
+        # is, the value we give to evas.  Delete these computed values too,
+        # again saving a copy as last-prop-abs.
         prop_abs = prop + '-abs'
         if prop_abs in self._computed_properties:
             self._computed_properties['last-' + prop_abs] = 
self._computed_properties[prop_abs]
@@ -174,14 +207,17 @@
         """
         Creates the underlying Evas object and returns it.  To be implemented 
         by subclasses.
+
+        This method is called as soon as we're canvased, which means it is
+        not called implicitly during a canvas sync.
         """
         pass
 
 
     def _queue_sync(self):
         """
-        Requests from our parent that _sync_dirty_properties be called on the 
next
-        main loop step.
+        Requests from our parent that our _sync_dirty_properties method be
+        called on the next main loop step.
         """
         if not self._is_sync_queued and self._parent:
             #print "queue_sync", self, self._parent, self._is_sync_queued
@@ -191,7 +227,9 @@
 
     def _adopted(self, parent):
         """
-        Called by our parent when we get adopted.
+        Called by our parent when it adopts us.  Here we grab a weakref to
+        our parent, and mark all our properties as dirty so that they get
+        recomputed against our new parent.
         """
         if self._parent == parent:
             return
@@ -204,18 +242,40 @@
         self._dirty_all_properties()
 
 
+    def _orphaned(self):
+        """
+        Called by our parent when we are removed and left without a parent.
+        Deletes the underlying evas object as we're no longer part of a 
+        canvas.
+        """
+        # Our parent doesn't want us anymore.  So dirty our display property.
+        # This indirectly dirties visible, which will compute to False if
+        # self._parent is None.   We dirty display instead of visible because
+        # we want to propagate SYNC_SIZE_CHANGED up to our parent on the next
+        # sync.
+        self._dirty_property('display')
+
+        # No parent means no canvas.  Uncanvasing will also delete our 
+        # underlying evas object.
+        self._uncanvased()
+
+        # Now set parent to None.  We do this after _dirty_property() since
+        # we need a reference so the queue sync will bubble up.
+        self._parent = None
+
+
     def _canvased(self, canvas):
         """
         Called by our parent container when the top-most ancestor is a Canvas
-        object.
+        object, or when the canvas object changes (e.g. we're moved to a new
+        canvas).
         """
-        if canvas == self._canvas:
-            return
-
-        # Hold a weakref to the canvas.
-        self._canvas = weakref(canvas)
-        if self["name"]:
-            self._canvas._register_object_name(self["name"], self)
+        if canvas != self._canvas:
+            # Hold a weakref to the canvas.
+            self._canvas = weakref(canvas)
+            if self['name']:
+                # Register our name with the new canvas.
+                self._canvas._register_object_name(self['name'], self)
 
         evas = canvas.get_evas()
         if not self._o and evas:
@@ -226,10 +286,11 @@
 
     def _uncanvased(self):
         """
-        Called by our parent when we're detached from our current canvas.
+        Called when we're detached from our current canvas.  Unregisters our
+        name with the canvas and deletes the evas object.
         """
-        if self["name"] and self._canvas:
-            self._canvas._unregister_object_name(self["name"])
+        if self['name'] and self._canvas:
+            self._canvas._unregister_object_name(self['name'])
         self._wrap(None)
         self._canvas = None
 
@@ -262,7 +323,7 @@
         Called just before we are about to sync a property; if this method
         returns False then we defer the sync.
         """
-        return self._o != None or property == "name"
+        return self._o != None or property == 'name'
 
 
     def _sync_dirty_properties(self, debug_prefix = ''):
@@ -283,7 +344,7 @@
         assert(not self._is_currently_syncing)
         self._is_currently_syncing = True
         return_value = 0
-        debug(debug_prefix, "* sync:", self, " 
".join(self._dirty_properties.keys()))
+        debug(debug_prefix, '* sync:', self, ' 
'.join(self._dirty_properties.keys()))
         # Keep syncing dirty properties until we either have no more dirty
         # properties left, or all the properties we tried to sync either 
         # aren't ready to be synced yet (_can_sync_property() return False)
@@ -303,17 +364,19 @@
             # scaling independently of the number of properties in the property
             # list.
             for prop in sorted(self._dirty_properties.keys(), 
key=self._dirty_properties.get):
-                debug(debug_prefix, "   prop=%s can_sync=%s ->" % (prop, 
self._can_sync_property(prop)), lf=False)
+                debug(debug_prefix, '   prop=%s can_sync=%s ->' % (prop, 
self._can_sync_property(prop)), lf=False)
                 if self._can_sync_property(prop):
                     try:
-                        result = getattr(self, "_sync_property_" + prop)()
+                        result = getattr(self, '_sync_property_' + prop)()
                     except:
                         log.exception("Exception when syncing property '%s' 
for %s" % (prop, self))
                         result = 0
 
-                    # If property indicates canvas needs rendering, propagate
-                    # this flag back up to our parent.
-                    return_value |= result & (SYNC_NEEDS_RENDER | 
SYNC_SIZE_CHANGED)
+                    debug(debug_prefix, ' ret=%d' % result, lf=False)
+
+                    # Propagate all sync flags back up to parent except for 
+                    # SYNC_NOT_FINISHED, which we handle ourselves.
+                    return_value |= result & ~SYNC_NOT_FINISHED
 
                     if not result & SYNC_NOT_FINISHED:
                         # Property sync successful, so remove this property 
from the
@@ -349,10 +412,10 @@
         against max.
         """
         if isinstance(val, str):
-            if val.replace("-", "").isdigit():
+            if val.replace('-', '').isdigit():
                 return int(val)
-            elif "%" in val:
-                return int(float(val.replace("%", "")) / 100.0 * max)
+            elif '%' in val:
+                return int(float(val.replace('%', '')) / 100.0 * max)
             else:
                 raise ValueError, "Invalid relative value '%s'" % val
         return val
@@ -360,8 +423,21 @@
 
     def _get_extents(self):
         """
-        Returns a 2-tuple containing the numeric width and height that
-        defines the maximum size this object can hold.  
+        Returns a 2-tuple containing the numeric width and height that defines
+        the maximum size this object can hold, as constrained by our parent.
+
+        For a non-passive child (passive property is False [default]), the
+        extent for a dimension is the size for that dimension from the nearest
+        ancestor whose value is non-auto, less the cumulative padding of that
+        ancestor and all its children up to and including our parent.  For
+        example, if our parent's size is (50%,auto), parent's padding is 10% in
+        all directions, and our grandparent's size is (640,480), our extents
+        are (192,384).  
+
+        For a passive child (passive property is True), the extents are the
+        parent's computed inner size after all non-passive children have been
+        synced.  In the above example, if we have a single sibling object whose
+        computed size is (150,150), then our extents are (192, 150).
         """
         return self._parent._get_computed_property_for_child('extents', self)
 
@@ -385,11 +461,10 @@
 
     def _get_computed_size(self):
         """
-        Returns the object's computed size.  The computed size is the size
-        of the object as it appears on the canvas, including padding.  (This
-        therefore implements a traditional box model and not the W3C box
-        model.)  Relative (percentage) values are resolved into numeric 
-        values.
+        Returns the object's computed border size.  The computed border size is
+        the size of the object as it appears on the canvas, including padding.
+        (This therefore implements a traditional box model and not the W3C box
+        model.)  Relative (percentage) values are resolved into numeric values.
 
         If a dimension is not able to be calculated when this method is 
         called, then 0 will be returned for that dimension.  
@@ -400,7 +475,7 @@
         if size:
             return size
 
-        size = list(self["size"])
+        size = list(self['size'])
         extents = self._get_extents()
 
         if 'auto' in size:
@@ -415,7 +490,7 @@
             padding_xy = intrinsic_size = None
 
         for index in range(2):
-            if size[index] == "auto":
+            if size[index] == 'auto':
                 # 'auto' dimensions are intrinsic size plus padding.
                 size[index] = intrinsic_size[index] + padding_xy[index]
             else:
@@ -504,14 +579,31 @@
     # Properties
     ########################################################################
 
+    # --  name  ------------------------------------------------------------
 
     def _sync_property_name(self):
         return SYNC_NOOP
 
 
+
+    # --  display  ---------------------------------------------------------
+
+    def _sync_property_display(self):
+        self._dirty_property('visible')
+        return SYNC_SIZE_CHANGED
+
+
+
+    # --  visible  ---------------------------------------------------------
+
     def _compute_absolute_visible(self):
-        parent_visible = 
self._parent._get_computed_property_for_child('visible-abs', self, True)
-        visible = parent_visible and self['visible']
+        # If we have no parent, it's probably because we've just been 
+        # orphaned.  In this case, we hide ourselves on the canvas.
+        visible = self['visible'] and self['display'] and self._parent != None
+        if visible:
+            # We only need to check parent's visibility if we are visible.
+            if not 
self._parent._get_computed_property_for_child('visible-abs', self, True):
+                visible = False
         self._computed_properties['visible-abs'] = visible
         return visible
 
@@ -524,6 +616,9 @@
         return SYNC_NEEDS_RENDER
 
 
+
+    # --  opacity  ---------------------------------------------------------
+
     def _compute_absolute_opacity(self):
         parent_opacity = 
self._parent._get_computed_property_for_child('opacity-abs', self, 1.0)
         opacity = parent_opacity * self['opacity']
@@ -541,6 +636,9 @@
         return self._sync_property_color()
 
 
+
+    # --  color  -----------------------------------------------------------
+
     def _set_property_color(self, dummy, color):
         """
         Parse a color that is either a 3-tuple of integers specifying red,
@@ -548,18 +646,20 @@
         or #rgb.
         """
         if isinstance(color, basestring) and color[0] == '#' and len(color) in 
(4, 7):
+            # Handle the #rrggbb or #rgb case.
             if len(color) == 4:
                 return int(color[1], 16) * 17, int(color[2], 16) * 17, 
int(color[3], 16) * 17
             else:
                 return int(color[1:3], 16), int(color[3:5], 16), 
int(color[5:7], 16)
 
-        elif isinstance(color, (list, tuple)):
-            # If any color element is none, us the existing value.
+        elif isinstance(color, (list, tuple)) and len(color) == 3:
+            # Handle the (r, g, b) case.
             if None in color:
-                color = tuple(map(lambda x, y: (x,y)[x==None], color, 
self["color"]))
+                # If any color element is none, us the existing value.
+                color = tuple(map(lambda x, y: (x,y)[x==None], color, 
self['color']))
             return color
         else:
-            raise ValueError, "Color must be a 3-tuple or html-style #rrggbb"
+            raise ValueError, 'Color must be a 3-tuple or html-style #rrggbb'
 
             
     def _compute_absolute_color(self):
@@ -586,50 +686,130 @@
 
 
     def _sync_property_color(self):
-        opacity = self._computed_properties['opacity-abs']
-        color = self._compute_absolute_color()
-        color = color + (int(opacity * 255),)
-        self._o.color_set(*color)
+        r, g, b = self._compute_absolute_color()
+        a = int(self._computed_properties['opacity-abs'] * 255)
+        self._o.color_set(r, g, b, a)
         return SYNC_NEEDS_RENDER
 
 
+
+    # --  passive  ---------------------------------------------------------
+
     def _sync_property_passive(self):
         # FIXME: only do properties that are affected by passive property;
         # a property is affected if calls get_extents (i.e. uses a relative
         # value in the property.
-        self._dirty_property("margin")
-        self._dirty_property("padding")
-        self._dirty_property("size")
-        self._dirty_property("pos")
+        self._dirty_property('margin')
+        self._dirty_property('padding')
+        self._dirty_property('size')
+        self._dirty_property('pos')
 
         return SYNC_NOOP
 
 
+
+    # --  margin  ----------------------------------------------------------
+
+    # margin-left, margin-top, etc. aren't real properties in the sense
+    # that they aren't individually synced.  We intercept the setter for
+    # these properties, modify the relevant component in the actual
+    # margin property, and then set the margin property.
+
+    def _set_property_margin_top(self, dummy, value):
+        top, right, bottom, left = self['margin']
+        self._set_property_generic('margin', (value, right, bottom, left))
+
+    def _set_property_margin_right(self, dummy, value):
+        top, right, bottom, left = self['margin']
+        self._set_property_generic('margin', (top, value, bottom, left))
+
+    def _set_property_margin_bottom(self, dummy, value):
+        top, right, bottom, left = self['margin']
+        self._set_property_generic('margin', (top, right, value, left))
+
+    def _set_property_margin_left(self, dummy, value):
+        top, right, bottom, left = self['margin']
+        self._set_property_generic('margin', (top, right, bottom, left))
+
     def _sync_property_margin(self):
         # Margin affects outer pos, and therefore also inner pos.  Need
         # to resync pos.
-        self._dirty_property("pos")
+        self._dirty_property('pos')
         return SYNC_NOOP
 
 
+    # --  padding  ---------------------------------------------------------
+
+    def _set_property_padding_top(self, dummy, value):
+        top, right, bottom, left = self['padding']
+        self._set_property_generic('padding', (value, right, bottom, left))
+
+    def _set_property_padding_right(self, dummy, value):
+        top, right, bottom, left = self['padding']
+        self._set_property_generic('padding', (top, value, bottom, left))
+
+    def _set_property_padding_bottom(self, dummy, value):
+        top, right, bottom, left = self['padding']
+        self._set_property_generic('padding', (top, right, value, left))
+
+    def _set_property_padding_left(self, dummy, value):
+        top, right, bottom, left = self['padding']
+        self._set_property_generic('padding', (top, right, bottom, value))
+
     def _sync_property_padding(self):
         # Padding affects inner size, which is the canvas size.
-        self._dirty_property("size")
+        self._dirty_property('size')
         # Padding also changes inner position, so we must resync pos as well.
-        self._dirty_property("pos")
+        self._dirty_property('pos')
         return SYNC_NOOP
 
 
+
+    # --  size  ------------------------------------------------------------
+
+    def _set_property_width(self, dummy, value):
+        self._set_property_generic('size', (value, self['size'][1]))
+
+    def _set_property_height(self, dummy, value):
+        self._set_property_generic('size', (self['size'][0], value))
+
     def _sync_property_size(self):
         if self['pos'][2:] != (None, None, None, None) and self['passive']:
             # Our position depends on our size, so cause pos resync.
             self._dirty_property('pos')
         size = self.get_computed_inner_size()
-        debug("inner size", size, lf = False)
+        debug('inner size', size, lf = False)
         self._o.resize(size)
         return SYNC_NEEDS_RENDER | SYNC_SIZE_CHANGED
 
 
+
+    # --  pos  -------------------------------------------------------------
+
+    def _set_property_left(self, dummy, value):
+        left, top, right, bottom, hcenter, vcenter = self['pos']
+        self._set_property_generic('pos', (value, top, right, bottom, None, 
vcenter))
+
+    def _set_property_top(self, dummy, value):
+        left, top, right, bottom, hcenter, vcenter = self['pos']
+        self._set_property_generic('pos', (left, value, right, bottom, 
hcenter, None))
+
+    def _set_property_right(self, dummy, value):
+        left, top, right, bottom, hcenter, vcenter = self['pos']
+        self._set_property_generic('pos', (left, top, value, bottom, None, 
vcenter))
+
+    def _set_property_bottom(self, dummy, value):
+        left, top, right, bottom, hcenter, vcenter = self['pos']
+        self._set_property_generic('pos', (left, top, right, value, hcenter, 
None))
+
+    def _set_property_hcenter(self, dummy, value):
+        left, top, right, bottom, hcenter, vcenter = self['pos']
+        self._set_property_generic('pos', (None, top, None, bottom, value, 
vcenter))
+
+    def _set_property_vcenter(self, dummy, value):
+        left, top, right, bottom, hcenter, vcenter = self['pos']
+        self._set_property_generic('pos', (left, None, right, None, hcenter, 
value))
+
     def _set_property_pos(self, dummy, pos):
         return tuple(pos)
 
@@ -652,9 +832,10 @@
     def _sync_property_pos(self):
         # Sync the object to the absolute inner position.
         abs_pos = self._compute_absolute_pos()
-        debug("abs pos", abs_pos, lf = False)
+        debug('abs pos', abs_pos, lf = False)
         self._o.move(abs_pos)
-        return SYNC_NEEDS_RENDER
+        return SYNC_NEEDS_RENDER | SYNC_POS_CHANGED
+
 
 
     ########################################################################
@@ -665,13 +846,19 @@
     def get_computed_size(self):
         """
         Public wrapper for _get_computed_size().  If caller asks for computed
-        size of the object before we've synced properties, we implicitly
+        border size of the object before we've synced properties, we implicitly
         sync the canvas, which calculates layout, which is necessary in order
-        to get the computed size.
+        to get the computed border size.
+
         """
         if not self._canvas:
             return 0, 0
+
         if not self._is_currently_syncing and self._is_sync_queued:
+            # User has requested our computed size before we've been synced.
+            # We must sync (reflow) everything now in order to return a
+            # correct result.
+
             # TODO: this syncs the whole canvas which might not be needed.  
             # Only need to sync objects that would affect our size.
             self._canvas._sync_dirty_properties()
@@ -712,17 +899,22 @@
         objects affecting our position must first be synced before we can
         correctly compute position.
         """
+        if not self._canvas:
+            return 0, 0
+
         if not self._is_currently_syncing and self._is_sync_queued:
-            if not self._canvas:
-                return 0, 0
+            # User has requested our computed pos before we've been synced.
+            # We must sync (reflow) everything now in order to return a correct
+            # result.
 
             # TODO: this syncs the whole canvas which might not be needed.  
-            # Only need to sync objects that would affect our size.
+            # Only need to sync objects that would affect our pos.
             self._canvas._sync_dirty_properties()
 
         return self._get_computed_position()
 
 
+
     def get_computed_inner_position(self):
         pos = self.get_computed_position()
         padding = self._get_computed_padding()
@@ -736,7 +928,7 @@
 
 
     def resize(self, size):
-        self["size"] = size
+        self['size'] = size
 
 
     def move(self, pos):
@@ -746,19 +938,19 @@
     def get_canvas(self):
         return self._canvas
 
+    def hide(self):
+        self['visible'] = False
+
+    def show(self):
+        self['visible'] = True
+
+
 
 Object._add_properties(
     # Properties will be synchronized in the order they appear in
     # this list.
-            #"name", "expand", "visible", "display", "layer",
-            #"color", "size", "pos", "clip", "margin", "padding"
-    ('name', 'visible', 'opacity', 'color', 'passive', 
-    'margin', 'padding', 'size', 'pos',
-#    'width', 'height', 'left', 'top', 'right', 'bottom', 'vcenter', 'hcenter',
-#    'margin_left', 'margin_top', 'margin_bottom', 'margin_right',
-#    'padding_left', 'padding_top', 'padding_bottom', 'padding_right'
-    
-    )
+    ('name', 'display', 'visible', 'opacity', 'color', 'passive', 
+    'margin', 'padding', 'size', 'pos')
 )
 
 
@@ -782,12 +974,19 @@
         super(Image, self).__init__(**kwargs)
 
 
+        self['image']     = kwargs.get('image')
+        self['filename']  = kwargs.get('filename')
+        self['pixels']    = kwargs.get('pixels')
+        self['data']      = kwargs.get('data')
+        self['dirty']     = False
+        self['aspect']    = kwargs.get('aspect', 'auto')
+        self['has_alpha'] = kwargs.get('has_alpha', True)
+
         self._loaded = False
         if image_or_file:
             self.set_image(image_or_file)
 
 
-
     def _create_evas_object(self, evas):
         """
         Creates the actual evas Image object.  Called when canvased.
@@ -803,10 +1002,89 @@
         return self._o.image_size_get()
 
 
+    def _get_native_aspect(self):
+        size = self._get_intrinsic_size()
+        if 0 in size:
+            return 1.0
+        return size[0] / float(size[1])
+
+
+    def _get_computed_size(self):
+        """
+        Returns the computed border size according to the current aspect
+        property.  Aspect property can be either a numeric value, or one of the
+        literal strings 'preserve', 'ignore', or 'auto'.
+
+        In the case of a numeric value, the image's computed size will conform
+        to the specified aspect.  If only one of the dimensions are specified
+        in the size property (i.e. one of them is 'auto'), the non-auto
+        dimension will be computed from the other dimension and the aspect
+        ratio.  If both dimensions are specified, then the size is considered a
+        maximum, and the computed size will be the largest size that can fit
+        within the area and still conform to the specified aspect.
+
+        If aspect is 'preserve', behave as though a numeric value was given,
+        but use the image's native aspect.
+
+        If aspect is 'ignore', stretch the image to fit the size property.  If
+        one of the size dimensions is 'auto', use the image's intrinsic size
+        for that dimension.
+
+        If aspect is 'auto', behave like 'preserve' if one of the size
+        dimensions is 'auto'.  Otherwise, if both dimensions are specified,
+        behave like 'ignore'.  This is the default aspect.
+        """
+        if 'size' in self._computed_properties:
+            return self._computed_properties['size']
+
+        size = super(Image, self)._get_computed_size()
+        aspect = self['aspect']
+        if aspect == 'ignore' or (aspect == 'auto' and 'auto' not in 
self['size']) or \
+           (aspect in ('preserve', 'auto') and self['size'] == ('auto', 
'auto')):
+            return size
+
+        # Computed size includes padding, so remove it so we can compute the
+        # aspect-corrected size.
+        padding = self._get_computed_padding()
+        size = list((size[0] - padding[1] - padding[3], size[1] - padding[0] - 
padding[2]))
+        if type(aspect) in (float, int):
+            target_aspect = float(aspect)
+        else:
+            target_aspect = self._get_native_aspect()
+
+        # Handle the case where one of the dimensions is auto.
+        if self['size'][0] == 'auto':
+            # Width is auto, so compute it against the computed height.
+            size[0] = int(size[1] * target_aspect)
+        elif self['size'][1] == 'auto':
+            # Height is auto, so compute it against the computed width.
+            size[1] = int(size[0] / target_aspect)
+
+        # Here both are fixed but we have a target aspect, so treat the
+        # size as a maximum area, and figure out which dimension we want
+        # to discard.
+        elif int(size[0] / target_aspect) > size[1]:
+            # Aspect-corrected height is greater than the specified height,
+            # so aspect-correct width instead.
+            size[0] = int(size[1] * target_aspect)
+        else:
+            # Aspect-corrected width is greater than the specified width,
+            # so aspect-correct heightinstead.
+            size[1] = int(size[0] / target_aspect)
+
+        size = size[0] + padding[1] + padding[3], size[1] + padding[0] + 
padding[2]
+        print "IMAGE SIZE %s aspect=%s intrinsic=%s computed old=%s new=%s" % 
(self, target_aspect, self._get_intrinsic_size(), 
self._computed_properties['size'], size)
+        self._computed_properties['size'] = size
+        return size
+
+
+
     ########################################################################
     # Properties
     ########################################################################
 
+    # --  size -------------------------------------------------------------
+
     def _sync_property_size(self):
         super(Image, self)._sync_property_size()
         size = self.get_computed_inner_size()
@@ -815,32 +1093,66 @@
         return SYNC_NEEDS_RENDER
 
 
+    # --  image ------------------------------------------------------------
+
     def _sync_property_image(self):
         return SYNC_NOOP
 
 
+    # --  filename ---------------------------------------------------------
+
     def _sync_property_filename(self):
-        debug(self["filename"], lf = False)
-        self._o.image_file_set(self["filename"])
+        debug(self['filename'], lf = False)
+        self._o.image_file_set(self['filename'])
         err = self._o.image_load_error_get()
         if err:
-            raise evas.LoadError, (err, "Unable to load image", 
self["filename"])
+            raise evas.LoadError, (err, 'Unable to load image', 
self['filename'])
 
         self._loaded = True
-        self._dirty_property("size")
+        self._dirty_property('size')
         return SYNC_NEEDS_RENDER
 
 
+    # --  pixels  ----------------------------------------------------------
+
     def _sync_property_pixels(self):
         return SYNC_NOOP
 
+
+    # --  data  ------------------------------------------------------------
+
     def _sync_property_data(self):
         return SYNC_NOOP
 
+
+    # --  aspect  ----------------------------------------------------------
+
+    def _set_propery_aspect(self, dummy, value):
+        """
+        Aspect property can be either a numeric value, or one of the literal
+        strings 'preserve', 'ignore', or 'auto'.  The computed size of the
+        image will reflect this property.
+        """
+        if value not in ('auto', 'preserve', 'ignore') and type(value) not in 
(int, float):
+            raise ValueError, "Aspect property must be 'preserve', 'ignore', 
'aspect', or numeric."
+        return value
+
+
     def _sync_property_aspect(self):
+        self._dirty_property('size')
         return SYNC_NOOP
 
 
+    # --  has_alpha  -------------------------------------------------------
+
+    def _sync_property_has_alpha(self):
+        return SYNC_NOOP
+
+
+    # --  dirty  -----------------------------------------------------------
+
+    def _sync_property_dirty(self):
+        return SYNC_NOOP
 
     ########################################################################
     # Public API
@@ -854,19 +1166,19 @@
 
         if isinstance(image_or_file, basestring):
             # Load image from file.
-            self["filename"] = image_or_file
-            del self["image"], self["pixels"]
+            self['filename'] = image_or_file
+            del self['image'], self['pixels']
         elif imlib2 and isinstance(image_or_file, imlib2.Image):
             # Use Imlib2 image as source for the evas image.
-            self["image"] = image_or_file
-            #self["image"].signals["changed"].connect_weak(self.set_dirty)
+            self['image'] = image_or_file
+            #self['image'].signals['changed'].connect_weak(self.set_dirty)
         else:
-            raise ValueError, "Unsupported argument to set_image: " + 
repr(type(image_or_file))
+            raise ValueError, 'Unsupported argument to set_image: ' + 
repr(type(image_or_file))
 
         self._loaded = False
 
 
-Image._add_properties(('image', 'filename', 'pixels', 'data'), ('aspect', ))
+Image._add_properties(('image', 'filename', 'pixels', 'data', 'dirty', 
'aspect'), ('has_alpha', ))
 
 
 
@@ -949,8 +1261,10 @@
     def _can_sync_property(self, property):
         # Containers don't actually have an evas object, so we override this
         # method to allow properties to be synced as long as we belong to a
-        # canvas.
-        return self._canvas != None
+        # canvas.  Exception: if property is display or visible and we have
+        # no parent, it likely means we were just deleted.  We allow a sync 
+        # in this case.
+        return self._canvas != None or property in ('display', 'visible')
 
 
     def _get_computed_property_for_child(self, prop, child, default = None):
@@ -958,12 +1272,15 @@
         Returns the computed property (specified by prop) within the context of
         the child requesting the property (specified by child).  If the
         computed property doesn't exist, return default.  Subclasses of
-        Container may wish to return different values depending on the child
-        requesting.
+        Container may wish to return different values depending on the 
+        requesting child, in order to implement custom layout policies.
         """
         if default is None and prop not in self._computed_properties:
             raise ValueError, "Property '%s' not computed and no default 
specified" % prop
+
         if prop == 'extents' and child['passive']:
+            # Passive child is asking for its extents, so we return our inner
+            # size.
             prop = 'size-inner'
 
         value = self._computed_properties.get(prop, default)
@@ -1004,22 +1321,21 @@
                 continue
 
             result = child._sync_dirty_properties(debug_prefix)
+            print ' - sync child', child, result
             if not result & SYNC_NOT_FINISHED:
                 # Sync of this child is complete, so remove pending resync
                 # request.
                 del self._queued_children[child]
                 child._is_sync_queued = False
 
-            if result & SYNC_SIZE_CHANGED and not do_passive and 'auto' in 
self['size']:
+            if result & (SYNC_SIZE_CHANGED | SYNC_POS_CHANGED) and not 
do_passive and 'auto' in self['size']:
                 # This child's size has changed, so our size may also have 
changed.
-                # FIXME: our size can change if our child's pos also changes;
-                # need to check for this.
 
                 # If both our dimensions are specified, child cannot
                 # affect our size, so ignore.
                 # Otherwise dirty size of all passive children and delete
                 # our cached computed size.  (Will be recalculated below.)
-                #print "Child %s size changed, force recomputation of our (%s) 
size" % (child, self)
+                print 'Child %s size changed, force recomputation of our (%s) 
size' % (child, self)
                 if 'size' in self._computed_properties:
                     del self._computed_properties['size']
                 for child in self._children:
@@ -1058,7 +1374,7 @@
 
             return_value |= result & SYNC_NEEDS_RENDER
 
-        if self["debug"]:
+        if self['debug']:
             return_value |= self._update_debug_rect()
 
         self._is_currently_syncing = False
@@ -1078,7 +1394,7 @@
         visible = self._computed_properties['visible-abs']
         if (pos, size) != self._debug_rect.geometry_get() or visible != 
self._debug_rect.visible_get() or \
            (pos_inner, size_inner) != self._debug_rect_inner.geometry_get():
-            #print "SET debug rect %s: pos=%s size=%s" % (self, repr(pos), 
repr(size))
+            #print 'SET debug rect %s: pos=%s size=%s' % (self, repr(pos), 
repr(size))
             self._debug_rect.move(pos)
             self._debug_rect.resize(size)
             self._debug_rect_inner.move(pos_inner)
@@ -1105,7 +1421,7 @@
             if child['passive']:
                 continue
 
-            child_size = child._get_computed_size()
+            child_size = child.get_computed_outer_size()
             child_pos = child.get_computed_position()
             sum = child_pos[0] + child_size[0], child_pos[1] + child_size[1]
             if sum[0] > size[0]:
@@ -1121,12 +1437,16 @@
     # Properties
     ########################################################################
 
+    # --  pos  -------------------------------------------------------------
+
     def _sync_property_pos(self):
         self._compute_absolute_pos()
         self._dirty_property_children('pos')
         return SYNC_NOOP
 
 
+    # --  visible  ---------------------------------------------------------
+
     def _sync_property_visible(self):
         # Compute absolute visibility; children will use this value.
         self._compute_absolute_visible()
@@ -1134,14 +1454,18 @@
         return SYNC_NOOP
 
 
+
+    # --  margin  ----------------------------------------------------------
+
     def _sync_property_margin(self):
         # Margin affects our position.  If we move, so must our children.  So
         # dirty both our pos and our children's pos.
         self._dirty_property('pos')
-        #self._dirty_property_children('pos')
         return SYNC_NOOP
 
 
+    # --  padding  ---------------------------------------------------------
+
     def _sync_property_padding(self):
         # Padding will affect our inner position, so we must dirty our pos
         # in order to recompute it, which will in turn dirty our children's 
@@ -1173,6 +1497,9 @@
         return SYNC_NOOP
 
 
+
+    # --  size  ------------------------------------------------------------
+
     def _compute_extents_for_children(self):
         extents = list(self._get_extents())
         for i, val in enumerate(self['size']):
@@ -1204,20 +1531,26 @@
         return SYNC_NOOP
 
 
+    # --  opacity  ---------------------------------------------------------
+
     def _sync_property_opacity(self):
         self._compute_absolute_opacity()
         self._dirty_property_children('opacity')
         return SYNC_NOOP
 
 
+    # --  color  -----------------------------------------------------------
+
     def _sync_property_color(self):
         self._compute_absolute_color()
         self._dirty_property_children('color')
         return SYNC_NOOP
 
 
+    # --  debug  -----------------------------------------------------------
+
     def _sync_property_debug(self):
-        if self["debug"]:
+        if self['debug']:
             if not self._debug_rect:
                 colors = [ random.randint(0, 255) for x in range(3) ]
                 self._debug_rect = 
self.get_canvas().get_evas().object_rectangle_add()
@@ -1227,7 +1560,7 @@
 
             return SYNC_NEEDS_RENDER
 
-        elif not self["debug"] and self._debug_rect:
+        elif not self['debug'] and self._debug_rect:
             self._debug_rect.hide()
             return SYNC_NEEDS_RENDER
 
@@ -1241,13 +1574,22 @@
 
     def add_child(self, child):
         if child._parent:
-            raise CanvasError, "Attempt to parent an adopted child."
+            raise CanvasError, 'Attempt to parent an adopted child.'
 
         self._children.append(child)
         child._adopted(self)
         if self.get_canvas():
             child._canvased(self.get_canvas())
 
+
+    def remove_child(self, child):
+        if child not in self._children:
+            return
+        self._children.remove(child)
+        child._orphaned()
+
+
+
 Container._add_properties(append = ('debug',))
 
 
@@ -1262,7 +1604,7 @@
 
     def _wrap(self, evas_object):
         super(Canvas, self)._wrap(evas_object)
-        kaa.signals["step"].connect_weak(self._sync_queued)
+        kaa.signals['step'].connect_weak(self._sync_queued)
 
 
     def _sync_dirty_properties(self, debug_prefix=''):
@@ -1273,11 +1615,15 @@
 
 
     def _sync_queued(self):
-        if len(self._dirty_properties) == len(self._queued_children) == 0:
+        sync_needed = len(self._queued_children) > 0 or 
len(self._dirty_properties) > 0
+        if not sync_needed and not self._render_needed:
             return
+
         kaa.evas.benchmark_reset()
         t0=time.time()
-        self._sync_dirty_properties()
+        if sync_needed:
+            self._sync_dirty_properties()
+
         t1=time.time()
         if self._render_needed:
             self._render()
@@ -1290,7 +1636,7 @@
         all=(t2-t0) - _bench_subtract
 
         evas=kaa.evas.benchmark_get()
-        print " @ Canvas sync=%.05f (%.2f%%), render=%.5f (%.2f%%); all=%.05f 
evas=%f;\n   OVERHEAD=%.05f (%.2f%%)" % \
+        print ' @ Canvas sync=%.05f (%.2f%%), render=%.5f (%.2f%%); all=%.05f 
evas=%f;\n   OVERHEAD=%.05f (%.2f%%)' % \
             (sync, sync/all*100, render, render/all*100, all, evas, all-evas, 
(all-evas)/all*100)
 
 
@@ -1298,16 +1644,47 @@
         return Object._can_sync_property(self, property)
 
 
+
+    def _render(self):
+        return self._o.render()
+
+
+    def _register_object_name(self, name, object):
+        self._names[name] = weakref(object)
+
+
+    def _unregister_object_name(self, name):
+        if name in self._names:
+            del self._names[name]
+
+
+    def _get_extents(self):
+        return self['size']
+
+
+    def _get_computed_size(self):
+        return self['size']
+
+
+
+    ########################################################################
+    # Properties
+    ########################################################################
+
+    # --  size  ------------------------------------------------------------
+
     def _set_property_size(self, dummy, size):
         if size != ('auto', 'auto'):
             output_size = self._o.output_size_get()
             if size != output_size:
-                raise ValueError, "Canvas with size %s cannot be resized to 
%s" % (output_size, size)
+                raise ValueError, 'Canvas with size %s cannot be resized to 
%s' % (output_size, size)
 
             self._computed_properties['size'] = size
         return size
 
 
+    # --  pos  -------------------------------------------------------------
+
     def _sync_property_pos(self):
         padding = self._get_computed_padding()
         self._computed_properties['pos-abs'] = padding[3], padding[0]
@@ -1316,45 +1693,38 @@
         return SYNC_NOOP
 
 
+    # --  visible  ---------------------------------------------------------
+
     def _sync_property_visible(self):
         self._computed_properties['visible-abs'] = self['visible']
         self._dirty_property_children('visible')
         return SYNC_NOOP
 
 
+    # --  opacity  ---------------------------------------------------------
+
     def _sync_property_opacity(self):
         self._computed_properties['opacity-abs'] = self['opacity']
         return SYNC_NOOP
 
+
+    # --  color  -----------------------------------------------------------
+
     def _sync_property_color(self):
         self._computed_properties['color-abs'] = self['color']
         return SYNC_NOOP
 
 
-    def _render(self):
-        return self._o.render()
-
-
-    def _register_object_name(self, name, object):
-        self._names[name] = weakref(object)
-
-
-    def _unregister_object_name(self, name):
-        if name in self._names:
-            del self._names[name]
-
-
-    def _get_extents(self):
-        return self['size']
 
-    def _get_computed_size(self):
-        return self['size']
+    ########################################################################
+    # Public API
+    ########################################################################
 
     def get_computed_size(self):
         return self['size']
 
     def get_evas(self):
-        return None
+        return self._o
 
 
     def get_canvas(self):
@@ -1366,11 +1736,11 @@
 from kaa import display
 
 class X11Canvas(Canvas):
-    def __init__(self, size, use_gl = None, title = "Canvas", **kwargs):
+    def __init__(self, size, use_gl = None, title = 'Canvas', **kwargs):
         self._window = display.X11Window(size = size, title = title)
 
         if use_gl == None:
-            use_gl = "gl_x11" in evas.render_method_list() and \
+            use_gl = 'gl_x11' in evas.render_method_list() and \
                      self._window.get_display().glx_supported()
 
         self._canvas_window = display.EvasX11Window(use_gl, size = size, 
parent = self._window)
@@ -1381,12 +1751,7 @@
         self._wrap(self._canvas_window.get_evas()._evas)
         self._canvas_window.set_cursor_hide_timeout(1)
 
-        self["size"] = size
-
-
-    def _set_property_visible(self, dummy, visible):
-        self._visibility_on_next_render = visible
-        return visible
+        self['size'] = size
 
 
     def _render(self):
@@ -1403,5 +1768,21 @@
             _bench_subtract = 0#time.time()-t0
         return regions
 
-    def get_evas(self):
-        return self._o
+
+
+    ########################################################################
+    # Properties
+    ########################################################################
+
+    # --  visible  ---------------------------------------------------------
+
+    def _set_property_visible(self, dummy, visible):
+        self._visibility_on_next_render = visible
+        return visible
+
+
+
+    ########################################################################
+    # Public API
+    ########################################################################
+

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to