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') |
33 | 33 | |
34 | 34 | _CELL_SIZE = 4 |
35 | 35 | _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] |
42 | 36 | |
43 | 37 | |
44 | 38 | class Layout(object): |
… |
… |
class RandomLayout(SpreadLayout): |
279 | 273 | self.fixed_positions[child] = (x, y) |
280 | 274 | |
281 | 275 | |
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 |
284 | 277 | _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 |
289 | 284 | _INITIAL_ANGLE = math.pi |
290 | 285 | |
291 | 286 | |
… |
… |
class RingLayout(ViewLayout): |
308 | 303 | def _calculate_radius_and_icon_size(self, children_count): |
309 | 304 | """ Adjust the ring or spiral radius and icon size as needed. """ |
310 | 305 | 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 |
324 | 306 | |
325 | | self._spiral_mode = True |
326 | | icon_size = style.STANDARD_ICON_SIZE |
| 307 | icon_size = style.MEDIUM_ICON_SIZE |
327 | 308 | angle_, radius = self._calculate_angle_and_radius(children_count, |
328 | 309 | 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: |
333 | 317 | angle_, radius = self._calculate_angle_and_radius( |
334 | 318 | 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: |
336 | 326 | break |
| 327 | else: |
| 328 | angle_, radius = self._calculate_angle_and_radius( |
| 329 | children_count, icon_size) |
| 330 | icon_size -= 1 |
337 | 331 | return radius, icon_size |
338 | 332 | |
339 | 333 | def _calculate_position(self, radius, icon_size, icon_index, |
… |
… |
class RingLayout(ViewLayout): |
362 | 356 | y = y + (height - icon_size - (style.GRID_CELL_SIZE / 2)) / 2 |
363 | 357 | return x, y |
364 | 358 | |
| 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 | |
365 | 363 | def _calculate_angle_and_radius(self, icon_count, icon_size): |
366 | 364 | """ 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 |
370 | 371 | angle = _INITIAL_ANGLE |
371 | | radius = _MINIMUM_RADIUS - (icon_size * _MIMIMUM_RADIUS_ENCROACHMENT) |
| 372 | radius = _MINIMUM_RADIUS + (icon_spacing * _MIMIMUM_RADIUS_PADDING_FACTOR) |
372 | 373 | for i_ in range(icon_count): |
373 | 374 | circumference = radius * 2 * math.pi |
374 | 375 | n = circumference / icon_spacing |
375 | 376 | angle += (2 * math.pi / n) |
376 | | radius += (float(icon_spacing) * spiral_spacing / n) |
| 377 | radius += (float(icon_spacing) * _RADIUS_GROWTH_FACTOR / n) |
377 | 378 | return angle, radius |
378 | 379 | |
379 | 380 | def allocate_children(self, allocation, children): |