Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/view/home/activitiesring.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/home/activitiesring.py')
-rw-r--r--src/view/home/activitiesring.py263
1 files changed, 206 insertions, 57 deletions
diff --git a/src/view/home/activitiesring.py b/src/view/home/activitiesring.py
index dccb066..5d66ec3 100644
--- a/src/view/home/activitiesring.py
+++ b/src/view/home/activitiesring.py
@@ -42,23 +42,29 @@ from session import get_session_manager
_logger = logging.getLogger('ActivitiesRing')
-class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
+_ICON_DND_TARGET = ('activity-icon', gtk.TARGET_SAME_WIDGET, 0)
+
+class ActivitiesRing(hippo.Canvas):
__gtype_name__ = 'SugarActivitiesRing'
- def __init__(self):
- hippo.CanvasBox.__init__(self,
- background_color=style.COLOR_WHITE.get_int())
+ def __init__(self, **kwargs):
+ gobject.GObject.__init__(self, **kwargs)
+
+ self._box = hippo.CanvasBox()
+ self._box.props.background_color = style.COLOR_WHITE.get_int()
+ self.set_root(self._box)
shell_model = shellmodel.get_instance()
shell_model.connect('notify::state', self._shell_state_changed_cb)
self._my_icon = _MyIcon(style.XLARGE_ICON_SIZE)
- self.append(self._my_icon, hippo.PACK_FIXED)
+ self._box.append(self._my_icon, hippo.PACK_FIXED)
self._current_activity = CurrentActivityIcon()
- self.append(self._current_activity, hippo.PACK_FIXED)
+ self._box.append(self._current_activity, hippo.PACK_FIXED)
- self.set_layout(RingLayout())
+ self._layout = RingLayout()
+ self._box.set_layout(self._layout)
registry = activity.get_registry()
registry.get_activities_async(reply_handler=self._get_activities_cb)
@@ -66,16 +72,27 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
registry.connect('activity-removed', self.__activity_removed_cb)
registry.connect('activity-changed', self.__activity_changed_cb)
- 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
+ # DND stuff
+ self._pressed_button = None
+ self._press_start_x = None
+ self._press_start_y = None
+ self._last_clicked_icon = None
+
+ self.drag_source_set(0, [], 0)
+ self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
+ gtk.gdk.POINTER_MOTION_HINT_MASK)
+ self.connect('motion-notify-event', self.__motion_notify_event_cb)
+ self.connect('button-press-event', self.__button_press_event_cb)
+ self.connect('drag-begin', self.__drag_begin_cb)
+
+ self.drag_dest_set(0, [], 0)
+ self.connect('drag-motion', self.__drag_motion_cb)
+ self.connect('drag-drop', self.__drag_drop_cb)
+ self.connect('drag-data-received', self.__drag_data_received_cb)
def _add_activity(self, activity_info):
icon = ActivityIcon(activity_info)
- self.insert_sorted(icon, 0, self._compare_activities)
+ self._layout.append(icon)
def _get_activities_cb(self, activity_list):
for info in activity_list:
@@ -88,7 +105,7 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
self._add_activity(activity_info)
def _find_activity_icon(self, bundle_id, version):
- for icon in self.get_children():
+ for icon in self._box.get_children():
if isinstance(icon, ActivityIcon) and \
icon.bundle_id == bundle_id and icon.version == version:
return icon
@@ -98,7 +115,7 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
icon = self._find_activity_icon(activity_info.bundle_id,
activity_info.version)
if icon is not None:
- self.remove(icon)
+ self._layout.remove(icon)
def __activity_changed_cb(self, activity_registry, activity_info):
if activity_info.bundle_id == "org.laptop.JournalActivity":
@@ -106,7 +123,7 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
icon = self._find_activity_icon(activity_info.bundle_id,
activity_info.version)
if icon is not None and not activity_info.favorite:
- self.remove(icon)
+ self._box.remove(icon)
elif icon is None and activity_info.favorite:
self._add_activity(activity_info)
@@ -115,23 +132,105 @@ class ActivitiesRing(hippo.CanvasBox, hippo.CanvasItem):
if model.props.state == ShellModel.STATE_SHUTDOWN:
pass
- def do_allocate(self, width, height, origin_changed):
- hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
+ def do_size_allocate(self, allocation):
+ hippo.Canvas.do_size_allocate(self, allocation)
+
+ width = allocation.width
+ height = allocation.height
[my_icon_width, my_icon_height] = self._my_icon.get_allocation()
x = (width - my_icon_width) / 2
y = (height - my_icon_height - style.GRID_CELL_SIZE) / 2
- self.set_position(self._my_icon, x, y)
+ self._box.set_position(self._my_icon, x, y)
[icon_width, icon_height] = self._current_activity.get_allocation()
x = (width - icon_width) / 2
y = (height + my_icon_height + style.DEFAULT_PADDING \
- style.GRID_CELL_SIZE) / 2
- self.set_position(self._current_activity, x, y)
+ self._box.set_position(self._current_activity, x, y)
def enable_xo_palette(self):
self._my_icon.enable_palette()
+ # TODO: Dnd methods. This should be merged somehow inside hippo-canvas.
+ def __button_press_event_cb(self, widget, event):
+ if event.button == 1 and event.type == gtk.gdk.BUTTON_PRESS:
+ self._last_clicked_icon = self._get_icon_at_coords(event.x, event.y)
+ if self._last_clicked_icon is not None:
+ self._pressed_button = event.button
+ self._press_start_x = event.x
+ self._press_start_y = event.y
+
+ return False
+
+ def _get_icon_at_coords(self, x, y):
+ for icon in self._box.get_children():
+ icon_x, icon_y = icon.get_context().translate_to_widget(icon)
+ icon_width, icon_height = icon.get_allocation()
+
+ if (x >= icon_x ) and (x <= icon_x + icon_width) and \
+ (y >= icon_y ) and (y <= icon_y + icon_height) and \
+ isinstance(icon, ActivityIcon):
+ return icon
+ return None
+
+ def __motion_notify_event_cb(self, widget, event):
+ if not self._pressed_button:
+ return False
+
+ # if the mouse button is not pressed, no drag should occurr
+ if not event.state & gtk.gdk.BUTTON1_MASK:
+ self._pressed_button = None
+ return False
+
+ if event.is_hint:
+ x, y, state_ = event.window.get_pointer()
+ else:
+ x = event.x
+ y = event.y
+
+ if widget.drag_check_threshold(int(self._press_start_x),
+ int(self._press_start_y),
+ int(x),
+ int(y)):
+ context_ = widget.drag_begin([_ICON_DND_TARGET],
+ gtk.gdk.ACTION_MOVE,
+ 1,
+ event)
+ return False
+
+ def __drag_begin_cb(self, widget, context):
+ icon_file_name = self._last_clicked_icon.props.file_name
+ # TODO: we should get the pixbuf from the widget, so it has colors, etc
+ pixbuf = gtk.gdk.pixbuf_new_from_file(icon_file_name)
+ context.set_icon_pixbuf(pixbuf, 0, 0)
+
+ def __drag_motion_cb(self, widget, context, x, y, time):
+ if self._last_clicked_icon is not None:
+ context.drag_status(context.suggested_action, time)
+ return True
+ else:
+ return False
+
+ def __drag_drop_cb(self, widget, context, x, y, time):
+ if self._last_clicked_icon is not None:
+ self.drag_get_data(context, _ICON_DND_TARGET[0])
+
+ self._layout.move_icon(self._last_clicked_icon, x, y)
+
+ self._pressed_button = None
+ self._press_start_x = None
+ self._press_start_y = None
+ self._last_clicked_icon = None
+
+ return True
+ else:
+ return False
+
+ def __drag_data_received_cb(self, widget, context, x, y, selection_data,
+ info, time):
+ context.drop_finish(success=True, time=time)
+
class ActivityIcon(CanvasIcon):
def __init__(self, activity_info):
CanvasIcon.__init__(self, cache=True, file_name=activity_info.icon)
@@ -198,11 +297,18 @@ 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
@@ -214,15 +320,8 @@ class RingLayout(gobject.GObject, hippo.CanvasLayout):
return 0, gtk.gdk.screen_width()
def _calculate_radius_and_icon_size(self, children_count):
- 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
angle = 2 * math.pi / children_count
- _logger.debug('minimum_radius %r maximum_radius %r angle %r' % \
- (minimum_radius, maximum_radius, angle))
-
# what's the radius required without downscaling?
distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING
icon_size = style.STANDARD_ICON_SIZE
@@ -232,60 +331,110 @@ class RingLayout(gobject.GObject, hippo.CanvasLayout):
else:
radius = math.sqrt(distance ** 2 /
(math.sin(angle) ** 2 + (math.cos(angle) - 1) ** 2))
-
- _logger.debug('radius 1 %r' % radius)
- if radius < minimum_radius:
+ 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
+ (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
-
- _logger.debug('radius 2 %r icon_size %r' % (radius, icon_size))
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
+ 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, child):
+ self._box.insert_sorted(child, 0, self._compare_activities)
+ self._update_icon_sizes()
+
+ def remove(self, child):
+ self._box.remove(child)
+ self._update_icon_sizes()
+
+ def move_icon(self, child, x, y):
+ if child not in self._box.get_children():
+ raise ValueError('Child not in box.')
+ self._fixed_positions[child] = (x, y)
+ self._box.emit_request_changed()
+
def do_allocate(self, x, y, width, height, req_width, req_height,
origin_changed):
- _logger.debug('RingLayout.do_allocate: %r %r %r %r %r %r %r' % (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))
- children = self._box.get_layout_children()
- if not children:
- return
+ for n in range(len(children_in_ring)):
+ child = children_in_ring[n]
- radius, icon_size = self._calculate_radius_and_icon_size(len(children))
+ x, y = self._calculate_position(radius, icon_size, n,
+ len(children_in_ring))
- for n in range(len(children)):
- child = children[n]
- # TODO: We get here a glib warning and I don't know why.
- child.item.props.size = icon_size
+ # 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._calculate_position(radius, icon_size, n, len(children))
+ 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)
-
- child.allocate(int(x),
- int(y),
- child_width,
- child_height,
- origin_changed)
+ 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):