From 1452826b50dc2dc0bb9c6bc0b856691e62ee485b Mon Sep 17 00:00:00 2001
From: Walter Bender <walter@sugarlabs.org>
Date: Wed, 18 Aug 2010 09:17:25 -0400
Subject: [PATCH] adding spiral extension to Ring View
---
src/jarabe/desktop/favoriteslayout.py | 99 ++++++++++++++++++++++++++-------
1 files changed, 78 insertions(+), 21 deletions(-)
diff --git a/src/jarabe/desktop/favoriteslayout.py b/src/jarabe/desktop/favoriteslayout.py
index 85e1b59..7b847ac 100644
a
|
b
|
|
1 | 1 | # Copyright (C) 2008 One Laptop Per Child |
| 2 | # Copyright (C) 2010 Sugar Labs |
2 | 3 | # |
3 | 4 | # This program is free software; you can redistribute it and/or modify |
4 | 5 | # it under the terms of the GNU General Public License as published by |
… |
… |
_logger = logging.getLogger('FavoritesLayout') |
32 | 33 | |
33 | 34 | _CELL_SIZE = 4 |
34 | 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 | |
35 | 43 | |
36 | 44 | class FavoritesLayout(gobject.GObject, hippo.CanvasLayout): |
37 | 45 | """Base class of the different layout types.""" |
… |
… |
_MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING + \ |
185 | 193 | style.STANDARD_ICON_SIZE * 2 |
186 | 194 | _MAXIMUM_RADIUS = (gtk.gdk.screen_height() - style.GRID_CELL_SIZE) / 2 - \ |
187 | 195 | style.STANDARD_ICON_SIZE - style.DEFAULT_SPACING |
| 196 | _ICON_SPACING_FACTORS = [1.5, 1.4, 1.3, 1.2, 1.1, 1.0] |
| 197 | _SPIRAL_SPACING_FACTORS = [1.5, 1.5, 1.5, 1.4, 1.3, 1.2] |
| 198 | _MIMIMUM_RADIUS_ENCROACHMENT = 0.75 |
| 199 | _INITIAL_ANGLE = math.pi |
| 200 | |
188 | 201 | |
189 | 202 | class RingLayout(FavoritesLayout): |
190 | | """Lay out icons in a ring around the XO man.""" |
| 203 | """Lay out icons in a ring or spiral around the XO man.""" |
191 | 204 | |
192 | 205 | __gtype_name__ = 'RingLayout' |
193 | 206 | icon_name = 'view-radial' |
… |
… |
class RingLayout(FavoritesLayout): |
201 | 214 | def __init__(self): |
202 | 215 | FavoritesLayout.__init__(self) |
203 | 216 | self._locked_children = {} |
| 217 | self._spiral_mode = False |
204 | 218 | |
205 | 219 | def append(self, icon, locked=False): |
206 | 220 | FavoritesLayout.append(self, icon, locked) |
… |
… |
class RingLayout(FavoritesLayout): |
221 | 235 | self._locked_children[child] = (x, y) |
222 | 236 | |
223 | 237 | def _calculate_radius_and_icon_size(self, children_count): |
224 | | # what's the radius required without downscaling? |
225 | | distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING |
| 238 | """ Adjust the ring or spiral radius and icon size as needed. """ |
| 239 | self._spiral_mode = False |
| 240 | distance = style.MEDIUM_ICON_SIZE + style.DEFAULT_SPACING * \ |
| 241 | _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.MEDIUM_ICON_SIZE)] |
| 242 | radius = max(children_count * distance / (2 * math.pi), _MINIMUM_RADIUS) |
| 243 | if radius < _MAXIMUM_RADIUS: |
| 244 | return radius, style.MEDIUM_ICON_SIZE |
| 245 | |
| 246 | distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING * \ |
| 247 | _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)] |
| 248 | radius = max(children_count * distance / (2 * math.pi), _MINIMUM_RADIUS) |
| 249 | if radius < _MAXIMUM_RADIUS: |
| 250 | return radius, style.STANDARD_ICON_SIZE |
| 251 | |
| 252 | self._spiral_mode = True |
226 | 253 | icon_size = style.STANDARD_ICON_SIZE |
227 | | # circumference is 2*pi*r; we want this to be at least |
228 | | # 'children_count * distance' |
229 | | radius = children_count * distance / (2 * math.pi) |
230 | | # limit computed radius to reasonable bounds. |
231 | | radius = max(radius, _MINIMUM_RADIUS) |
232 | | radius = min(radius, _MAXIMUM_RADIUS) |
233 | | # recompute icon size from limited radius |
234 | | if children_count > 0: |
235 | | icon_size = (2 * math.pi * radius / children_count) \ |
236 | | - style.DEFAULT_SPACING |
237 | | # limit adjusted icon size. |
238 | | icon_size = max(icon_size, style.SMALL_ICON_SIZE) |
239 | | icon_size = min(icon_size, style.MEDIUM_ICON_SIZE) |
| 254 | angle, radius = self._calculate_angle_and_radius(children_count, |
| 255 | icon_size) |
| 256 | while radius > _MAXIMUM_RADIUS: |
| 257 | i = _ICON_SIZES.index(icon_size) |
| 258 | if i < len(_ICON_SIZES) - 1: |
| 259 | icon_size = _ICON_SIZES[i + 1] |
| 260 | angle, radius = self._calculate_angle_and_radius( |
| 261 | children_count, icon_size) |
| 262 | else: |
| 263 | break |
240 | 264 | return radius, icon_size |
241 | 265 | |
242 | | def _calculate_position(self, radius, icon_size, index, children_count, |
| 266 | def _calculate_position(self, radius, icon_size, icon_index, children_count, |
243 | 267 | sin=math.sin, cos=math.cos): |
| 268 | """ Calculate an icon position on a circle or a spiral. """ |
244 | 269 | width, height = self.box.get_allocation() |
245 | | angle = index * (2 * math.pi / children_count) - math.pi / 2 |
246 | | x = radius * cos(angle) + (width - icon_size) / 2 |
247 | | y = radius * sin(angle) + (height - icon_size - |
248 | | (style.GRID_CELL_SIZE/2) ) / 2 |
| 270 | if self._spiral_mode: |
| 271 | min_width_, box_width = self.box.get_width_request() |
| 272 | min_height_, box_height = self.box.get_height_request(box_width) |
| 273 | angle, radius = self._calculate_angle_and_radius(icon_index, |
| 274 | icon_size) |
| 275 | x, y = self._convert_from_polar_to_cartesian(angle, radius, |
| 276 | icon_size, |
| 277 | width, height) |
| 278 | else: |
| 279 | angle = icon_index * (2 * math.pi / children_count) - math.pi / 2 |
| 280 | x = radius * cos(angle) + (width - icon_size) / 2 |
| 281 | y = radius * sin(angle) + (height - icon_size - \ |
| 282 | (style.GRID_CELL_SIZE / 2)) / 2 |
| 283 | return x, y |
| 284 | |
| 285 | def _convert_from_polar_to_cartesian(self, angle, radius, icon_size, width, |
| 286 | height): |
| 287 | """ Convert angle, radius to x, y """ |
| 288 | x = int(math.sin(angle) * radius) |
| 289 | y = int(math.cos(angle) * radius) |
| 290 | x = - x + (width - icon_size) / 2 |
| 291 | y = y + (height - icon_size - (style.GRID_CELL_SIZE / 2)) / 2 |
249 | 292 | return x, y |
250 | 293 | |
| 294 | def _calculate_angle_and_radius(self, icon_count, icon_size): |
| 295 | """ Based on icon_count and icon_size, calculate radius and angle. """ |
| 296 | spiral_spacing = _SPIRAL_SPACING_FACTORS[_ICON_SIZES.index(icon_size)] |
| 297 | icon_spacing = icon_size + style.DEFAULT_SPACING * \ |
| 298 | _ICON_SPACING_FACTORS[_ICON_SIZES.index(icon_size)] |
| 299 | angle = _INITIAL_ANGLE |
| 300 | radius = _MINIMUM_RADIUS - (icon_size * _MIMIMUM_RADIUS_ENCROACHMENT) |
| 301 | for i in range(icon_count): |
| 302 | circumference = radius * 2 * math.pi |
| 303 | n = circumference / icon_spacing |
| 304 | angle += (2 * math.pi / n) |
| 305 | radius += (float(icon_spacing) * spiral_spacing / n) |
| 306 | return angle, radius |
| 307 | |
251 | 308 | def _get_children_in_ring(self): |
252 | 309 | children_in_ring = [child for child in self.box.get_layout_children() \ |
253 | 310 | if child not in self._locked_children] |