Ticket #4025: Improve_circle_and_spiral_layout_favourite_view_fo_improved_touch_target_size_and_spacing.patch

File Improve_circle_and_spiral_layout_favourite_view_fo_improved_touch_target_size_and_spacing.patch, 6.5 KB (added by garycmartin, 12 years ago)
  • src/jarabe/desktop/favoriteslayout.py

    From e86d61ca84c3b59217057c70870e5ab511378902 Mon Sep 17 00:00:00 2001
    From: Gary Martin <gary@garycmartin.com>
    Date: Thu, 11 Oct 2012 04:17:04 +0100
    Subject: [PATCH] Improve circle and spiral layout favourite view to improve
     touch target size and spacing. #4025
    
    The original controls provided for RingLayout did not allow clean enough control of the circle and spiral icon layout. The global arrays of hard coded icon spacing at discrete icon size steps has now been replaced with a continuous function that will much more smoothly layout from 0 up to a large number of activities (a wider range of testing and comparison with previous behaviour was done with up to 55 activities). The layout now make better use of the available canvas space using larger icons, smaller steps between icon sizes, and larger circle/spiral arrangements to try and maximise both icon hit target size and provide enough space between icons to minimise target miss press errors.
    ---
     src/jarabe/desktop/favoriteslayout.py |   75 +++++++++++++++++----------------
     1 file changed, 38 insertions(+), 37 deletions(-)
    
    diff --git a/src/jarabe/desktop/favoriteslayout.py b/src/jarabe/desktop/favoriteslayout.py
    index 3c9be8b..154002b 100644
    a b _logger = logging.getLogger('FavoritesLayout') 
    3333
    3434_CELL_SIZE = 4
    3535_BASE_SCALE = 1000
    36 _INTERMEDIATE_B = (style.STANDARD_ICON_SIZE + style.SMALL_ICON_SIZE) / 2
    37 _INTERMEDIATE_A = (style.STANDARD_ICON_SIZE + _INTERMEDIATE_B) / 2
    38 _INTERMEDIATE_C = (_INTERMEDIATE_B + style.SMALL_ICON_SIZE) / 2
    39 _ICON_SIZES = [style.MEDIUM_ICON_SIZE, style.STANDARD_ICON_SIZE,
    40                _INTERMEDIATE_A, _INTERMEDIATE_B, _INTERMEDIATE_C,
    41                style.SMALL_ICON_SIZE]
    4236
    4337
    4438class Layout(object):
    class RandomLayout(SpreadLayout): 
    279273        self.fixed_positions[child] = (x, y)
    280274
    281275
    282 _MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING + \
    283         style.STANDARD_ICON_SIZE * 2
     276_MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING
    284277_MAXIMUM_RADIUS = (Gdk.Screen.height() - style.GRID_CELL_SIZE) / 2 - \
    285         style.STANDARD_ICON_SIZE - style.DEFAULT_SPACING
    286 _ICON_SPACING_FACTORS = [1.5, 1.4, 1.3, 1.2, 1.1, 1.0]
    287 _SPIRAL_SPACING_FACTORS = [1.5, 1.5, 1.5, 1.4, 1.3, 1.2]
    288 _MIMIMUM_RADIUS_ENCROACHMENT = 0.75
     278                                                 - style.DEFAULT_SPACING
     279_RING_SPACING_FACTOR = 0.95
     280_SPIRAL_SPACING_FACTOR = 0.75
     281_RADIUS_GROWTH_FACTOR = 1.25
     282_MIMIMUM_RADIUS_PADDING_FACTOR = 0.85
     283_MAXIMUM_RADIUS_PADDING_FACTOR = 1.25
    289284_INITIAL_ANGLE = math.pi
    290285
    291286
    class RingLayout(ViewLayout): 
    308303    def _calculate_radius_and_icon_size(self, children_count):
    309304        """ Adjust the ring or spiral radius and icon size as needed. """
    310305        self._spiral_mode = False
    311         distance = style.MEDIUM_ICON_SIZE + style.DEFAULT_SPACING * \
    312             _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.MEDIUM_ICON_SIZE)]
    313         radius = max(children_count * distance / (2 * math.pi),
    314                      _MINIMUM_RADIUS)
    315         if radius < _MAXIMUM_RADIUS:
    316             return radius, style.MEDIUM_ICON_SIZE
    317 
    318         distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING * \
    319             _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)]
    320         radius = max(children_count * distance / (2 * math.pi),
    321                      _MINIMUM_RADIUS)
    322         if radius < _MAXIMUM_RADIUS:
    323             return radius, style.STANDARD_ICON_SIZE
    324306
    325         self._spiral_mode = True
    326         icon_size = style.STANDARD_ICON_SIZE
     307        icon_size = style.MEDIUM_ICON_SIZE
    327308        angle_, radius = self._calculate_angle_and_radius(children_count,
    328309                                                          icon_size)
    329         while radius > _MAXIMUM_RADIUS:
    330             i = _ICON_SIZES.index(icon_size)
    331             if i < len(_ICON_SIZES) - 1:
    332                 icon_size = _ICON_SIZES[i + 1]
     310        if radius <= self._calculate_maximum_radius(icon_size):
     311            return radius, icon_size
     312        while radius > self._calculate_maximum_radius(icon_size):
     313            icon_size -= 1
     314            if icon_size <= style.STANDARD_ICON_SIZE:
     315                break
     316            else:
    333317                angle_, radius = self._calculate_angle_and_radius(
    334318                    children_count, icon_size)
    335             else:
     319        if radius <= self._calculate_maximum_radius(icon_size):
     320            return radius, icon_size
     321
     322        self._spiral_mode = True
     323        icon_size = style.MEDIUM_ICON_SIZE
     324        while radius > self._calculate_maximum_radius(icon_size):
     325            if icon_size < style.SMALL_ICON_SIZE:
    336326                break
     327            else:
     328                angle_, radius = self._calculate_angle_and_radius(
     329                    children_count, icon_size)
     330            icon_size -= 1
    337331        return radius, icon_size
    338332
    339333    def _calculate_position(self, radius, icon_size, icon_index,
    class RingLayout(ViewLayout): 
    362356        y = y + (height - icon_size - (style.GRID_CELL_SIZE / 2)) / 2
    363357        return x, y
    364358
     359    def _calculate_maximum_radius(self, icon_size):
     360        """ Return the maximum radius including encroachment. """
     361        return _MAXIMUM_RADIUS - (icon_size * _MAXIMUM_RADIUS_PADDING_FACTOR)
     362
    365363    def _calculate_angle_and_radius(self, icon_count, icon_size):
    366364        """ Based on icon_count and icon_size, calculate radius and angle. """
    367         spiral_spacing = _SPIRAL_SPACING_FACTORS[_ICON_SIZES.index(icon_size)]
    368         icon_spacing = icon_size + style.DEFAULT_SPACING * \
    369             _ICON_SPACING_FACTORS[_ICON_SIZES.index(icon_size)]
     365        if self._spiral_mode:
     366            _icon_spacing_factor = _SPIRAL_SPACING_FACTOR
     367        else:
     368            _icon_spacing_factor = _RING_SPACING_FACTOR
     369        icon_spacing = math.sqrt(icon_size ** 2 * 2) * _icon_spacing_factor + \
     370                                                          style.DEFAULT_SPACING
    370371        angle = _INITIAL_ANGLE
    371         radius = _MINIMUM_RADIUS - (icon_size * _MIMIMUM_RADIUS_ENCROACHMENT)
     372        radius = _MINIMUM_RADIUS + (icon_spacing * _MIMIMUM_RADIUS_PADDING_FACTOR)
    372373        for i_ in range(icon_count):
    373374            circumference = radius * 2 * math.pi
    374375            n = circumference / icon_spacing
    375376            angle += (2 * math.pi / n)
    376             radius += (float(icon_spacing) * spiral_spacing / n)
     377            radius += (float(icon_spacing) * _RADIUS_GROWTH_FACTOR / n)
    377378        return angle, radius
    378379
    379380    def allocate_children(self, allocation, children):