From 76d353a120463fa5ca5af2cddd5705555b23c0e3 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 11 Jun 2008 19:00:15 +0000 Subject: Add RandomLayout and make it the default. --- diff --git a/po/POTFILES.in b/po/POTFILES.in index 322e691..ce883b2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -28,6 +28,6 @@ src/controlpanel/view/language.py src/controlpanel/view/network.py src/view/devices/network/mesh.py src/view/frame/activitiestray.py -src/view/home/activitiesring.py +src/view/home/favoritesview.py src/view/palettes.py diff --git a/src/view/home/HomeBox.py b/src/view/home/HomeBox.py index 429b903..14c0911 100644 --- a/src/view/home/HomeBox.py +++ b/src/view/home/HomeBox.py @@ -23,10 +23,10 @@ from sugar.graphics import style from sugar.graphics import iconentry from sugar.graphics.radiotoolbutton import RadioToolButton -from view.home.activitiesring import ActivitiesRing +from view.home.favoritesview import FavoritesView from view.home.activitieslist import ActivitiesList -_RING_VIEW = 0 +_FAVORITES_VIEW = 0 _LIST_VIEW = 1 _AUTOSEARCH_TIMEOUT = 1000 @@ -37,7 +37,7 @@ class HomeBox(gtk.VBox): def __init__(self): gobject.GObject.__init__(self) - self._ring_view = ActivitiesRing() + self._favorites_view = FavoritesView() self._list_view = ActivitiesList() self._enable_xo_palette = False @@ -47,7 +47,7 @@ class HomeBox(gtk.VBox): self.pack_start(self._toolbar, expand=False) self._toolbar.show() - self._set_view(_RING_VIEW) + self._set_view(_FAVORITES_VIEW) def __toolbar_query_changed_cb(self, toolbar, query): if self._list_view is None: @@ -59,18 +59,18 @@ class HomeBox(gtk.VBox): self._set_view(view) def _set_view(self, view): - if view == _RING_VIEW: + if view == _FAVORITES_VIEW: if self._list_view in self.get_children(): self.remove(self._list_view) if self._enable_xo_palette: - self._ring_view.enable_xo_palette() + self._favorites_view.enable_xo_palette() - self.add(self._ring_view) - self._ring_view.show() + self.add(self._favorites_view) + self._favorites_view.show() elif view == _LIST_VIEW: - if self._ring_view in self.get_children(): - self.remove(self._ring_view) + if self._favorites_view in self.get_children(): + self.remove(self._favorites_view) self.add(self._list_view) self._list_view.show() @@ -80,25 +80,11 @@ class HomeBox(gtk.VBox): _REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes def resume(self): - # TODO: Do we need this? - #if self._redraw_id is None: - # self._redraw_id = gobject.timeout_add(self._REDRAW_TIMEOUT, - # self._redraw_activity_ring) - # self._redraw_activity_ring() pass def suspend(self): - # TODO: Do we need this? - #if self._redraw_id is not None: - # gobject.source_remove(self._redraw_id) - # self._redraw_id = None pass - def _redraw_activity_ring(self): - # TODO: Do we need this? - #self._donut.redraw() - return True - def has_activities(self): # TODO: Do we need this? #return self._donut.has_activities() @@ -106,8 +92,8 @@ class HomeBox(gtk.VBox): def enable_xo_palette(self): self._enable_xo_palette = True - if self._ring_view is not None: - self._ring_view.enable_xo_palette() + if self._favorites_view is not None: + self._favorites_view.enable_xo_palette() class HomeToolbar(gtk.Toolbar): __gtype_name__ = 'SugarHomeToolbar' @@ -145,16 +131,16 @@ class HomeToolbar(gtk.Toolbar): self._add_separator(expand=True) - ring_button = RadioToolButton(named_icon='view-radial', group=None) - ring_button.props.tooltip = _('Ring view') - ring_button.props.accelerator = _('R') - ring_button.connect('toggled', self.__view_button_toggled_cb, - _RING_VIEW) - self.insert(ring_button, -1) - ring_button.show() + favorites_button = RadioToolButton(named_icon='view-radial', group=None) + favorites_button.props.tooltip = _('Favorites view') + favorites_button.props.accelerator = _('R') + favorites_button.connect('toggled', self.__view_button_toggled_cb, + _FAVORITES_VIEW) + self.insert(favorites_button, -1) + favorites_button.show() list_button = RadioToolButton(named_icon='view-list') - list_button.props.group = ring_button + list_button.props.group = favorites_button list_button.props.tooltip = _('List view') list_button.props.accelerator = _('L') list_button.connect('toggled', self.__view_button_toggled_cb, diff --git a/src/view/home/Makefile.am b/src/view/home/Makefile.am index 5306e2b..0983077 100644 --- a/src/view/home/Makefile.am +++ b/src/view/home/Makefile.am @@ -2,7 +2,8 @@ sugardir = $(pkgdatadir)/shell/view/home sugar_PYTHON = \ __init__.py \ activitieslist.py \ - activitiesring.py \ + favoritesview.py \ + favoriteslayout.py \ FriendView.py \ FriendsBox.py \ launchbox.py \ diff --git a/src/view/home/favoriteslayout.py b/src/view/home/favoriteslayout.py new file mode 100644 index 0000000..9ab4eff --- /dev/null +++ b/src/view/home/favoriteslayout.py @@ -0,0 +1,225 @@ +# Copyright (C) 2008 One Laptop Per Child +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import logging +import math +import hashlib + +import gobject +import gtk +import hippo + +from sugar.graphics import style +from sugar import activity + +_logger = logging.getLogger('FavoritesLayout') + +class FavoritesLayout(gobject.GObject, hippo.CanvasLayout): + __gtype_name__ = 'FavoritesLayout' + + def __init__(self): + gobject.GObject.__init__(self) + self.box = None + self.fixed_positions = {} + + def do_set_box(self, box): + self.box = box + + def do_get_height_request(self, for_width): + return 0, gtk.gdk.screen_height() - style.GRID_CELL_SIZE + + def do_get_width_request(self): + return 0, gtk.gdk.screen_width() + + 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 + + def append(self, icon): + self.box.insert_sorted(icon, 0, self._compare_activities) + relative_x, relative_y = icon.fixed_position + if relative_x >= 0 and relative_y >= 0: + width = gtk.gdk.screen_width() + height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE + self.fixed_positions[icon] = (relative_x * 1000 / width, + relative_y * 1000 / height) + self.update_icon_sizes() + + def remove(self, icon): + del self.fixed_positions[icon] + self.box.remove(icon) + self.update_icon_sizes() + + def move_icon(self, icon, x, y): + if icon not in self.box.get_children(): + raise ValueError('Child not in box.') + + width = gtk.gdk.screen_width() + height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE + registry = activity.get_registry() + registry.set_activity_position(icon.get_bundle_id(), icon.get_version(), + x * width / 1000, y * height / 1000) + + self.fixed_positions[icon] = (x, y) + self.box.emit_request_changed() + + def update_icon_sizes(self): + pass + + def do_allocate(self, x, y, width, height, req_width, req_height, + origin_changed): + raise NotImplementedError() + +class RandomLayout(FavoritesLayout): + __gtype_name__ = 'RandomLayout' + + def __init__(self): + FavoritesLayout.__init__(self) + + def append(self, icon): + FavoritesLayout.append(self, icon) + icon.props.size = style.STANDARD_ICON_SIZE + + 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 + min_w_, child_width = child.get_width_request() + min_h_, child_height = child.get_height_request(child_width) + + if child.item in self.fixed_positions: + x, y = self.fixed_positions[child.item] + else: + name_hash = hashlib.md5(child.item.get_bundle_id()) + + x = int(name_hash.hexdigest()[:5], 16) % (width - child_width) + y = int(name_hash.hexdigest()[-5:], 16) % (height - child_height) + + # TODO Handle collisions + + child.allocate(int(x), int(y), child_width, child_height, + origin_changed) + +_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 + +class RingLayout(FavoritesLayout): + __gtype_name__ = 'RingLayout' + + def __init__(self): + FavoritesLayout.__init__(self) + + def _calculate_radius_and_icon_size(self, children_count): + angle = 2 * math.pi / children_count + + # what's the radius required without downscaling? + distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING + icon_size = style.STANDARD_ICON_SIZE + + if children_count == 1: + radius = 0 + else: + radius = math.sqrt(distance ** 2 / + (math.sin(angle) ** 2 + (math.cos(angle) - 1) ** 2)) + + if radius < _MINIMUM_RADIUS: + # we can upscale, if we want + icon_size += style.STANDARD_ICON_SIZE * \ + (0.5 * (_MINIMUM_RADIUS - radius) / _MINIMUM_RADIUS) + radius = _MINIMUM_RADIUS + elif radius > _MAXIMUM_RADIUS: + radius = _MAXIMUM_RADIUS + # need to downscale. what's the icon size required? + distance = math.sqrt((radius * math.sin(angle)) ** 2 + \ + (radius * (math.cos(angle) - 1)) ** 2) + icon_size = distance - style.DEFAULT_SPACING + + return radius, icon_size + + def _calculate_position(self, radius, icon_size, index, children_count): + width, height = self.box.get_allocation() + angle = index * (2 * math.pi / children_count) - math.pi / 2 + x = radius * math.cos(angle) + (width - icon_size) / 2 + y = radius * math.sin(angle) + (height - icon_size - + style.GRID_CELL_SIZE) / 2 + return x, y + + def _get_children_in_ring(self): + children = self.box.get_layout_children() + width, height = self.box.get_allocation() + children_in_ring = [] + for child in children: + if child.item in self.fixed_positions: + x, y = self.fixed_positions[child.item] + distance_to_center = math.hypot(x - width / 2, y - height / 2) + # TODO at what distance should we consider a child inside the ring? + else: + children_in_ring.append(child) + + return children_in_ring + + def update_icon_sizes(self): + 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] + child.item.props.size = icon_size + + for child in self.box.get_layout_children(): + if child not in children_in_ring: + child.item.props.size = style.STANDARD_ICON_SIZE + + 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) + + for child in self.box.get_layout_children(): + if child in children_in_ring: + continue + + # 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) + + x, y = self.fixed_positions[child.item] + + child.allocate(int(x), int(y), child_width, child_height, + origin_changed) + diff --git a/src/view/home/activitiesring.py b/src/view/home/favoritesview.py index a250c1d..bf8ab38 100644 --- a/src/view/home/activitiesring.py +++ b/src/view/home/favoritesview.py @@ -34,18 +34,19 @@ import view.Shell from view.palettes import JournalPalette from view.palettes import CurrentActivityPalette, ActivityPalette from view.home.MyIcon import MyIcon +from view.home.favoriteslayout import RandomLayout from model import shellmodel from model.shellmodel import ShellModel from hardware import schoolserver from controlpanel.gui import ControlPanel from session import get_session_manager -_logger = logging.getLogger('ActivitiesRing') +_logger = logging.getLogger('FavoritesView') _ICON_DND_TARGET = ('activity-icon', gtk.TARGET_SAME_WIDGET, 0) -class ActivitiesRing(hippo.Canvas): - __gtype_name__ = 'SugarActivitiesRing' +class FavoritesView(hippo.Canvas): + __gtype_name__ = 'SugarFavoritesView' def __init__(self, **kwargs): gobject.GObject.__init__(self, **kwargs) @@ -63,7 +64,7 @@ class ActivitiesRing(hippo.Canvas): self._current_activity = CurrentActivityIcon() self._box.append(self._current_activity, hippo.PACK_FIXED) - self._layout = RingLayout() + self._layout = RandomLayout() self._box.set_layout(self._layout) registry = activity.get_registry() @@ -303,159 +304,6 @@ class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem): def __active_activity_changed_cb(self, home_model, home_activity): self._update(home_activity) -_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 - -class RingLayout(gobject.GObject, hippo.CanvasLayout): - __gtype_name__ = 'SugarRingLayout' - - def __init__(self): - gobject.GObject.__init__(self) - self._box = None - self._fixed_positions = {} - - def do_set_box(self, box): - self._box = box - - def do_get_height_request(self, for_width): - return 0, gtk.gdk.screen_height() - style.GRID_CELL_SIZE - - def do_get_width_request(self): - return 0, gtk.gdk.screen_width() - - def _calculate_radius_and_icon_size(self, children_count): - angle = 2 * math.pi / children_count - - # what's the radius required without downscaling? - distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING - icon_size = style.STANDARD_ICON_SIZE - - if children_count == 1: - radius = 0 - else: - radius = math.sqrt(distance ** 2 / - (math.sin(angle) ** 2 + (math.cos(angle) - 1) ** 2)) - - if radius < _MINIMUM_RADIUS: - # we can upscale, if we want - icon_size += style.STANDARD_ICON_SIZE * \ - (0.5 * (_MINIMUM_RADIUS - radius) / _MINIMUM_RADIUS) - radius = _MINIMUM_RADIUS - elif radius > _MAXIMUM_RADIUS: - radius = _MAXIMUM_RADIUS - # need to downscale. what's the icon size required? - distance = math.sqrt((radius * math.sin(angle)) ** 2 + \ - (radius * (math.cos(angle) - 1)) ** 2) - icon_size = distance - style.DEFAULT_SPACING - - return radius, icon_size - - def _calculate_position(self, radius, icon_size, index, children_count): - width, height = self._box.get_allocation() - angle = index * (2 * math.pi / children_count) - math.pi / 2 - x = radius * math.cos(angle) + (width - icon_size) / 2 - y = radius * math.sin(angle) + (height - icon_size - - style.GRID_CELL_SIZE) / 2 - return x, y - - def _get_children_in_ring(self): - children = self._box.get_layout_children() - width, height = self._box.get_allocation() - children_in_ring = [] - for child in children: - if child.item in self._fixed_positions: - x, y = self._fixed_positions[child.item] - distance_to_center = math.hypot(x - width / 2, y - height / 2) - # TODO at what distance should we consider a child inside the ring? - else: - children_in_ring.append(child) - - return children_in_ring - - def _update_icon_sizes(self): - 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] - child.item.props.size = icon_size - - for child in self._box.get_layout_children(): - if child not in children_in_ring: - child.item.props.size = style.STANDARD_ICON_SIZE - - 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 - - def append(self, icon): - self._box.insert_sorted(icon, 0, self._compare_activities) - relative_x, relative_y = icon.fixed_position - if relative_x >= 0 and relative_y >= 0: - width = gtk.gdk.screen_width() - height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE - self._fixed_positions[icon] = (relative_x * 1000 / width, - relative_y * 1000 / height) - self._update_icon_sizes() - - def remove(self, icon): - del self._fixed_positions[icon] - self._box.remove(icon) - self._update_icon_sizes() - - def move_icon(self, icon, x, y): - if icon not in self._box.get_children(): - raise ValueError('Child not in box.') - - width = gtk.gdk.screen_width() - height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE - registry = activity.get_registry() - registry.set_activity_position(icon.get_bundle_id(), icon.get_version(), - x * width / 1000, y * height / 1000) - - self._fixed_positions[icon] = (x, y) - self._box.emit_request_changed() - - 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) - - for child in self._box.get_layout_children(): - if child in children_in_ring: - continue - - # 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) - - x, y = self._fixed_positions[child.item] - - child.allocate(int(x), int(y), child_width, child_height, - origin_changed) - class _MyIcon(MyIcon): def __init__(self, scale): MyIcon.__init__(self, scale) diff --git a/src/view/palettes.py b/src/view/palettes.py index bda7f95..8406fd0 100644 --- a/src/view/palettes.py +++ b/src/view/palettes.py @@ -116,11 +116,11 @@ class ActivityPalette(Palette): def _update_favorite_item(self): label = self._favorite_item.child if self._favorite: - label.set_text(_('Remove from ring')) + label.set_text(_('Remove favorite')) xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(), style.COLOR_TRANSPARENT.get_svg())) else: - label.set_text(_('Add to ring')) + label.set_text(_('Make favorite')) xo_color = profile.get_color() self._favorite_icon.props.xo_color = xo_color -- cgit v0.9.1