[Sugar-devel] [PATCH] refactoring of Homeview layouts to simplify Spiral option
Walter Bender
walter at sugarlabs.org
Wed Feb 9 14:45:18 EST 2011
---
src/jarabe/desktop/favoriteslayout.py | 233 ++++++++++++++++++++-------------
1 files changed, 145 insertions(+), 88 deletions(-)
diff --git a/src/jarabe/desktop/favoriteslayout.py b/src/jarabe/desktop/favoriteslayout.py
index 360c147..22f729c 100644
--- a/src/jarabe/desktop/favoriteslayout.py
+++ b/src/jarabe/desktop/favoriteslayout.py
@@ -29,17 +29,10 @@ from sugar.graphics import style
from jarabe.model import bundleregistry
from jarabe.desktop.grid import Grid
-
_logger = logging.getLogger('FavoritesLayout')
_CELL_SIZE = 4
_BASE_SCALE = 1000
-_INTERMEDIATE_B = (style.STANDARD_ICON_SIZE + style.SMALL_ICON_SIZE) / 2
-_INTERMEDIATE_A = (style.STANDARD_ICON_SIZE + _INTERMEDIATE_B) / 2
-_INTERMEDIATE_C = (_INTERMEDIATE_B + style.SMALL_ICON_SIZE) / 2
-_ICON_SIZES = [style.MEDIUM_ICON_SIZE, style.STANDARD_ICON_SIZE,
- _INTERMEDIATE_A, _INTERMEDIATE_B, _INTERMEDIATE_C,
- style.SMALL_ICON_SIZE]
class FavoritesLayout(gobject.GObject, hippo.CanvasLayout):
@@ -178,7 +171,7 @@ class RandomLayout(FavoritesLayout):
def do_allocate(self, x, y, width, height, req_width, req_height,
origin_changed):
for child in self.box.get_layout_children():
- # We need to always get requests to not confuse hippo
+ # We need to always get requests to not confuse hippo.
min_w_, child_width = child.get_width_request()
min_h_, child_height = child.get_height_request(child_width)
@@ -197,19 +190,24 @@ _MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING + \
style.STANDARD_ICON_SIZE * 2
_MAXIMUM_RADIUS = (gtk.gdk.screen_height() - style.GRID_CELL_SIZE) / 2 - \
style.STANDARD_ICON_SIZE - style.DEFAULT_SPACING
-_ICON_SPACING_FACTORS = [1.5, 1.4, 1.3, 1.2, 1.1, 1.0]
-_SPIRAL_SPACING_FACTORS = [1.5, 1.5, 1.5, 1.4, 1.3, 1.2]
-_MIMIMUM_RADIUS_ENCROACHMENT = 0.75
-_INITIAL_ANGLE = math.pi
+_INTERMEDIATE_C = (style.STANDARD_ICON_SIZE + style.SMALL_ICON_SIZE) / 2
+_INTERMEDIATE_A = (style.STANDARD_ICON_SIZE * 2 + _INTERMEDIATE_C) / 3
+_INTERMEDIATE_E = (_INTERMEDIATE_C + style.SMALL_ICON_SIZE * 2) / 3
+_INTERMEDIATE_B = (_INTERMEDIATE_A + _INTERMEDIATE_C) / 2
+_INTERMEDIATE_D = (_INTERMEDIATE_C + _INTERMEDIATE_E) / 2
+_ICON_SIZES = [style.MEDIUM_ICON_SIZE, style.STANDARD_ICON_SIZE,
+ _INTERMEDIATE_A, _INTERMEDIATE_B, _INTERMEDIATE_C,
+ _INTERMEDIATE_D, _INTERMEDIATE_E, style.SMALL_ICON_SIZE]
+_ICON_SPACING_FACTORS = [1.5, 1.4, 1.3, 1.2, 1.15, 1.1, 1.05, 1.0]
-class RingLayout(FavoritesLayout):
- """Lay out icons in a ring or spiral around the XO man."""
+class BasicRingLayout(FavoritesLayout):
+ """Lay out icons in a ring around the XO man."""
- __gtype_name__ = 'RingLayout'
+ __gtype_name__ = 'BasicRingLayout'
icon_name = 'view-radial'
"""Name of icon used in home view dropdown palette."""
- key = 'ring-layout'
+ key = 'basic-ring-layout'
"""String used in profile to represent this view."""
# TRANS: label for the ring layout in the favorites view
palette_name = _('Ring')
@@ -218,7 +216,6 @@ class RingLayout(FavoritesLayout):
def __init__(self):
FavoritesLayout.__init__(self)
self._locked_children = {}
- self._spiral_mode = False
def append(self, icon, locked=False):
FavoritesLayout.append(self, icon, locked)
@@ -239,8 +236,8 @@ class RingLayout(FavoritesLayout):
self._locked_children[child] = (x, y)
def _calculate_radius_and_icon_size(self, children_count):
- """ Adjust the ring or spiral radius and icon size as needed. """
- self._spiral_mode = False
+ """ Adjust the ring radius and icon size as needed. """
+ # Begin by increasing the radius.
distance = style.MEDIUM_ICON_SIZE + style.DEFAULT_SPACING * \
_ICON_SPACING_FACTORS[_ICON_SIZES.index(style.MEDIUM_ICON_SIZE)]
radius = max(children_count * distance / (2 * math.pi),
@@ -248,13 +245,118 @@ class RingLayout(FavoritesLayout):
if radius < _MAXIMUM_RADIUS:
return radius, style.MEDIUM_ICON_SIZE
- distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING * \
- _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)]
+ # Continue by shrinking the icon size to STANDARD_ICON_SIZE.
+ radius = _MAXIMUM_RADIUS
+ distance = radius * (2 * math.pi) / children_count
+ icon_size = int(distance - style.DEFAULT_SPACING * \
+ _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)])
+ if icon_size >= style.STANDARD_ICON_SIZE:
+ return radius, icon_size
+
+ # Continue by shrinking the icon size to SMALL_ICON_SIZE.
+ icon_size = max(int(distance - style.DEFAULT_SPACING * \
+ _ICON_SPACING_FACTORS[_ICON_SIZES.index(
+ style.SMALL_ICON_SIZE)]), style.SMALL_ICON_SIZE)
+ return radius, icon_size
+
+ def _calculate_position(self, radius, icon_size, icon_index,
+ children_count, sin=math.sin, cos=math.cos):
+ """ Calculate an icon position on a circle. """
+ width, height = self.box.get_allocation()
+ angle = icon_index * (2 * math.pi / children_count) - math.pi / 2
+ x = radius * cos(angle) + (width - icon_size) / 2
+ y = radius * sin(angle) + (height - icon_size - \
+ (style.GRID_CELL_SIZE / 2)) / 2
+ return x, y
+
+ def _get_children_in_ring(self):
+ children_in_ring = [child for child in self.box.get_layout_children() \
+ if child not in self._locked_children]
+ return children_in_ring
+
+ def do_allocate(self, x, y, width, height, req_width, req_height,
+ origin_changed):
+ children_in_ring = self._get_children_in_ring()
+ if children_in_ring:
+ radius, icon_size = \
+ self._calculate_radius_and_icon_size(len(children_in_ring))
+
+ for n in range(len(children_in_ring)):
+ child = children_in_ring[n]
+
+ x, y = self._calculate_position(radius, icon_size, n,
+ len(children_in_ring))
+
+ # We need to always get requests to not confuse hippo.
+ min_w_, child_width = child.get_width_request()
+ min_h_, child_height = child.get_height_request(child_width)
+
+ child.allocate(int(x), int(y), child_width, child_height,
+ origin_changed)
+ child.item.props.size = icon_size
+
+ for child in self._locked_children.keys():
+ x, y = self._locked_children[child]
+
+ # We need to always get requests to not confuse hippo.
+ min_w_, child_width = child.get_width_request()
+ min_h_, child_height = child.get_height_request(child_width)
+
+ if child_width <= 0 or child_height <= 0:
+ return
+
+ child.allocate(int(x), int(y), child_width, child_height,
+ origin_changed)
+
+ def compare_activities(self, icon_a, icon_b):
+ if hasattr(icon_a, 'installation_time') and \
+ hasattr(icon_b, 'installation_time'):
+ return icon_b.installation_time - icon_a.installation_time
+ else:
+ return 0
+
+
+_MIMIMUM_RADIUS_ENCROACHMENT = 0.75
+_INITIAL_ANGLE = math.pi
+_SPIRAL_SPACING_FACTORS = [1.5, 1.5, 1.5, 1.4, 1.35, 1.3, 1.25, 1.2]
+
+
+class RingLayout(BasicRingLayout):
+ """ Variation of Basic Ring that morphs into a spiral as
+ the number of icons increases beyond the capacity of the
+ STANDARD_ICON_SIZE. """
+
+ __gtype_name__ = 'RingLayout'
+ icon_name = 'view-radial'
+ """Name of icon used in home view dropdown palette."""
+ key = 'ring-layout'
+ """String used in profile to represent this view."""
+
+ def __init__(self):
+ BasicRingLayout.__init__(self)
+ self._locked_children = {}
+ self._spiral_mode = False
+
+ def _calculate_radius_and_icon_size(self, children_count):
+ """ Adjust the ring or spiral radius and icon size as needed. """
+ self._spiral_mode = False
+ # Begin by increasing the radius.
+ distance = style.MEDIUM_ICON_SIZE + style.DEFAULT_SPACING * \
+ _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.MEDIUM_ICON_SIZE)]
radius = max(children_count * distance / (2 * math.pi),
_MINIMUM_RADIUS)
if radius < _MAXIMUM_RADIUS:
- return radius, style.STANDARD_ICON_SIZE
+ return radius, style.MEDIUM_ICON_SIZE
+
+ # Continue by shrinking the icon size.
+ radius = _MAXIMUM_RADIUS
+ distance = radius * (2 * math.pi) / children_count
+ icon_size = int(distance - style.DEFAULT_SPACING * \
+ _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)])
+ if icon_size >= style.STANDARD_ICON_SIZE:
+ return radius, icon_size
+ # Finally, switch to a spiral.
self._spiral_mode = True
icon_size = style.STANDARD_ICON_SIZE
angle_, radius = self._calculate_angle_and_radius(children_count,
@@ -311,52 +413,6 @@ class RingLayout(FavoritesLayout):
radius += (float(icon_spacing) * spiral_spacing / n)
return angle, radius
- def _get_children_in_ring(self):
- children_in_ring = [child for child in self.box.get_layout_children() \
- if child not in self._locked_children]
- return children_in_ring
-
- def do_allocate(self, x, y, width, height, req_width, req_height,
- origin_changed):
- children_in_ring = self._get_children_in_ring()
- if children_in_ring:
- radius, icon_size = \
- self._calculate_radius_and_icon_size(len(children_in_ring))
-
- for n in range(len(children_in_ring)):
- child = children_in_ring[n]
-
- x, y = self._calculate_position(radius, icon_size, n,
- len(children_in_ring))
-
- # We need to always get requests to not confuse hippo
- min_w_, child_width = child.get_width_request()
- min_h_, child_height = child.get_height_request(child_width)
-
- child.allocate(int(x), int(y), child_width, child_height,
- origin_changed)
- child.item.props.size = icon_size
-
- for child in self._locked_children.keys():
- x, y = self._locked_children[child]
-
- # We need to always get requests to not confuse hippo
- min_w_, child_width = child.get_width_request()
- min_h_, child_height = child.get_height_request(child_width)
-
- if child_width <= 0 or child_height <= 0:
- return
-
- child.allocate(int(x), int(y), child_width, child_height,
- origin_changed)
-
- def compare_activities(self, icon_a, icon_b):
- if hasattr(icon_a, 'installation_time') and \
- hasattr(icon_b, 'installation_time'):
- return icon_b.installation_time - icon_a.installation_time
- else:
- return 0
-
_SUNFLOWER_CONSTANT = style.STANDARD_ICON_SIZE * .75
"""Chose a constant such that STANDARD_ICON_SIZE icons are nicely spaced."""
@@ -384,7 +440,7 @@ Calculation: math.radians(360) / ( _GOLDEN_RATIO * _GOLDEN_RATIO )
"""
-class SunflowerLayout(RingLayout):
+class SunflowerLayout(BasicRingLayout):
"""Spiral layout based on Fibonacci ratio in phyllotaxis.
See http://algorithmicbotany.org/papers/abop/abop-ch4.pdf
@@ -403,7 +459,7 @@ class SunflowerLayout(RingLayout):
"""String used to identify this layout in home view dropdown palette."""
def __init__(self):
- RingLayout.__init__(self)
+ BasicRingLayout.__init__(self)
self.skipped_indices = []
def _calculate_radius_and_icon_size(self, children_count):
@@ -431,11 +487,11 @@ class SunflowerLayout(RingLayout):
index = self.adjust_index(oindex)
- # tweak phi to get a nice gap lined up where the "active activity"
+ # Tweak phi to get a nice gap lined up where the "active activity"
# icon is, below the central XO man.
phi = index * _SUNFLOWER_ANGLE + math.radians(-130)
- # we offset index when computing r to make space for the XO man.
+ # We offset index when computing r to make space for the XO man.
r = _SUNFLOWER_CONSTANT * math.sqrt(index + _SUNFLOWER_OFFSET)
# x,y are the top-left corner of the icon, so remove icon_size
@@ -445,8 +501,8 @@ class SunflowerLayout(RingLayout):
y = r * sin(phi) + (height - icon_size - \
(style.GRID_CELL_SIZE / 2)) / 2
- # skip allocations outside the allocation box.
- # give up once we can't fit
+ # Skip allocations outside the allocation box.
+ # Give up once we can't fit.
if r < math.hypot(width / 2, height / 2):
if y < 0 or y > (height - icon_size) or \
x < 0 or x > (width - icon_size):
@@ -457,7 +513,7 @@ class SunflowerLayout(RingLayout):
return x, y
-class BoxLayout(RingLayout):
+class BoxLayout(BasicRingLayout):
"""Lay out icons in a square around the XO man."""
__gtype_name__ = 'BoxLayout'
@@ -473,12 +529,12 @@ class BoxLayout(RingLayout):
"""String used to identify this layout in home view dropdown palette."""
def __init__(self):
- RingLayout.__init__(self)
+ BasicRingLayout.__init__(self)
def _calculate_position(self, radius, icon_size, index, children_count,
sin=None, cos=None):
- # use "orthogonal" versions of cos and sin in order to square the
+ # Use "orthogonal" versions of cos and sin in order to square the
# circle and turn the 'ring view' into a 'box view'
def cos_d(d):
while d < 0:
@@ -495,12 +551,12 @@ class BoxLayout(RingLayout):
cos = lambda r: cos_d(math.degrees(r))
sin = lambda r: cos_d(math.degrees(r) - 90)
- return RingLayout._calculate_position(self, radius, icon_size, index,
- children_count, sin=sin,
- cos=cos)
+ return BasicRingLayout._calculate_position(self, radius, icon_size,
+ index, children_count,
+ sin=sin, cos=cos)
-class TriangleLayout(RingLayout):
+class TriangleLayout(BasicRingLayout):
"""Lay out icons in a triangle around the XO man."""
__gtype_name__ = 'TriangleLayout'
@@ -516,18 +572,19 @@ class TriangleLayout(RingLayout):
"""String used to identify this layout in home view dropdown palette."""
def __init__(self):
- RingLayout.__init__(self)
+ BasicRingLayout.__init__(self)
def _calculate_radius_and_icon_size(self, children_count):
- # use slightly larger minimum radius than parent, because sides
+ # Use slightly larger minimum radius than parent, because sides
# of triangle come awful close to the center.
radius, icon_size = \
- RingLayout._calculate_radius_and_icon_size(self, children_count)
+ BasicRingLayout._calculate_radius_and_icon_size(self,
+ children_count)
return max(radius, _MINIMUM_RADIUS + style.MEDIUM_ICON_SIZE), icon_size
def _calculate_position(self, radius, icon_size, index, children_count,
sin=math.sin, cos=math.cos):
- # tweak cos and sin in order to make the 'ring' into an equilateral
+ # Tweak cos and sin in order to make the 'ring' into an equilateral
# triangle.
def cos_d(d):
@@ -555,6 +612,6 @@ class TriangleLayout(RingLayout):
cos = lambda r: cos_d(math.degrees(r))
sin = lambda r: sin_d(math.degrees(r))
- return RingLayout._calculate_position(self, radius, icon_size, index,
- children_count, sin=sin,
- cos=cos)
+ return BasicRingLayout._calculate_position(self, radius, icon_size,
+ index, children_count,
+ sin=sin, cos=cos)
--
1.7.4
More information about the Sugar-devel
mailing list