Ticket #1447: 0001-Added-Close-button-to-activity-launcher-rewrote-lay.patch
File 0001-Added-Close-button-to-activity-launcher-rewrote-lay.patch, 14.2 KB (added by wadeb, 15 years ago) |
---|
-
src/jarabe/model/shell.py
From 7cc4ffe504bb93c4864814673fa48ce8986d90b9 Mon Sep 17 00:00:00 2001 From: Wade Brainerd <wadetb@gmail.com> Date: Fri, 30 Oct 2009 22:00:40 -0400 Subject: [PATCH] Added Close button to activity launcher; rewrote layout to accomodate complex centering. Delay between icon pulses, so the user can count pulses and for a possible performance gain. --- src/jarabe/model/shell.py | 18 +++- src/jarabe/view/launcher.py | 257 ++++++++++++++++++++++++++++++++----------- src/jarabe/view/service.py | 5 + 3 files changed, 216 insertions(+), 64 deletions(-) diff --git a/src/jarabe/model/shell.py b/src/jarabe/model/shell.py index de5a66f..153e64d 100644
a b class ShellModel(gobject.GObject): 572 572 home_activity.get_type()) 573 573 home_activity.props.launching = False 574 574 self._remove_activity(home_activity) 575 self.emit('launch-failed', home_activity) 575 576 else: 576 577 logging.error('Model for activity id %s does not exist.', 577 578 activity_id) 578 579 579 self.emit('launch-failed', home_activity) 580 580 def notify_activity_ended(self, activity_id): 581 home_activity = self.get_activity_by_id(activity_id) 582 if home_activity: 583 if home_activity.props.launching: 584 logging.debug('Activity %s (%s) ended while still launching, '\ 585 'assuming it failed.', activity_id, 586 home_activity.get_type()) 587 self.notify_launch_failed(activity_id) 588 else: 589 logging.debug("Activity %s ended", activity_id) 590 self._remove_activity(home_activity) 591 else: 592 # The activity may have already been removed when its window closed. 593 pass 594 581 595 def _check_activity_launched(self, activity_id): 582 596 home_activity = self.get_activity_by_id(activity_id) 583 597 -
src/jarabe/view/launcher.py
diff --git a/src/jarabe/view/launcher.py b/src/jarabe/view/launcher.py index d4b9967..1db87fb 100644
a b 15 15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 16 17 17 import logging 18 from gettext import gettext as _ 19 20 import math 18 21 19 22 import gtk 20 23 import hippo 21 24 import gobject 22 25 23 26 from sugar import wm 27 from sugar.presence import presenceservice 24 28 from sugar.graphics import style 25 29 from sugar.graphics import animator 26 30 from sugar.graphics.xocolor import XoColor 31 from sugar.graphics.icon import Icon 32 from sugar.graphics.icon import CanvasIcon 27 33 28 34 from jarabe.model import shell 29 from jarabe.view.pulsingicon import CanvasPulsingIcon 35 36 XOCOLOR_WHITE = XoColor("#ffffff,#ffffff") 37 XOCOLOR_GRAY = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(), 38 style.COLOR_WHITE.get_svg())) 39 40 41 def _parse_svgcolor(str): 42 r = int(str[1:3], 16) 43 g = int(str[3:5], 16) 44 b = int(str[5:7], 16) 45 return r, g, b 46 47 def _parse_xocolor(xocolor): 48 stroke = _parse_svgcolor(xocolor.get_stroke_color()) 49 fill = _parse_svgcolor(xocolor.get_fill_color()) 50 return stroke, fill 51 52 def _format_xocolor(stroke_rgb, fill_rgb): 53 return XoColor("#%02x%02x%02x,#%02x%02x%02x" % (stroke_rgb + fill_rgb)) 54 55 def _blend_rgb(a, b, current): 56 return (a[0] + (b[0] - a[0]) * current, 57 a[1] + (b[1] - a[1]) * current, 58 a[2] + (b[2] - a[2]) * current) 59 60 def _blend_xocolor(a, b, current): 61 stroke_a, fill_a = _parse_xocolor(a) 62 stroke_b, fill_b = _parse_xocolor(b) 63 stroke_c = _blend_rgb(stroke_a, stroke_b, current) 64 fill_c = _blend_rgb(fill_a, fill_b, current) 65 return _format_xocolor(stroke_c, fill_c) 66 67 68 class _Layout(gobject.GObject, hippo.CanvasLayout): 69 __gtype_name__ = 'SugarLauncherLayout' 70 def __init__(self): 71 gobject.GObject.__init__(self) 72 self._box = None 73 74 self._child_mode = [] 75 76 # Note to reviewer: 77 # The child mode is kept in an array that parallels the children of the CanvasBox. 78 # This disallows objects from being deleted or hidden as the arrays would get out of sync. 79 # Is there a better way to attach a layout mode to each child? 80 def append_mode(self, child, mode): 81 self._child_mode.append(mode) 82 83 def do_set_box(self, box): 84 self._box = box 85 86 def do_get_height_request(self, for_width): 87 return 0, 0 88 89 def do_get_width_request(self): 90 return 0, 0 91 92 def do_allocate(self, x, y, width, height, 93 req_width, req_height, origin_changed): 94 95 circle_angle = -math.pi/2 96 circle_radius = min(width, height) * 0.4 97 98 center_bottom_y = height * 3 / 4 99 100 i = 0 101 for child in self._box.get_layout_children(): 102 mode = self._child_mode[i] 103 i += 1 104 105 if mode == 'center-bottom': 106 min_width, child_width = child.get_width_request() 107 min_height, child_height = child.get_height_request(child_width) 108 109 center_bottom_y -= (child_height + style.DEFAULT_SPACING) / 2 110 111 i = 0 112 for child in self._box.get_layout_children(): 113 min_width, child_width = child.get_width_request() 114 min_height, child_height = child.get_height_request(child_width) 115 116 mode = self._child_mode[i] 117 i += 1 118 119 if mode == 'center': 120 child.allocate(x + (width - child_width) / 2, 121 y + (height - child_height) / 2, 122 child_width, child_height, origin_changed) 123 124 if mode == 'center-bottom': 125 child.allocate(x + (width - child_width) / 2, 126 y + center_bottom_y, 127 child_width, child_height, origin_changed) 128 center_bottom_y += child_height + style.DEFAULT_SPACING 129 130 class _ZoomAnimation(animator.Animation): 131 def __init__(self, icon, start_size, end_size): 132 animator.Animation.__init__(self, 0.0, 1.0) 133 134 self._icon = icon 135 self._start_size = start_size 136 self._end_size = end_size 137 138 def next_frame(self, current): 139 size = self._start_size + (self._end_size - self._start_size) * current 140 self._icon.props.size = int(size) 141 142 class _PulseAnimation(animator.Animation): 143 def __init__(self, icon, start_color, middle_color): 144 animator.Animation.__init__(self, 0.0, 1.0) 145 146 self._icon = icon 147 self._start_color = start_color 148 self._middle_color = middle_color 149 150 def do_frame(self, t, duration, easing): 151 f = math.sin((t / duration) * math.pi) 152 color = _blend_xocolor(self._start_color, self._middle_color, f) 153 self._icon.xo_color = color 30 154 31 155 class LaunchWindow(gtk.Window): 32 156 def __init__(self, activity_id, icon_path, icon_color): 33 157 gobject.GObject.__init__(self) 34 158 159 self._activity_id = activity_id 160 self._home_activity = None 161 162 self._icon_color = icon_color 163 35 164 self.props.type_hint = gtk.gdk.WINDOW_TYPE_HINT_NORMAL 36 165 self.props.decorated = False 37 166 167 self._layout = _Layout() 168 169 self._box = hippo.CanvasBox() 170 self._box.set_layout(self._layout) 171 172 self._activity_icon = CanvasIcon(file_name=icon_path, 173 xo_color=self._icon_color) 174 self._box.append(self._activity_icon) 175 self._layout.append_mode(self._activity_icon, 'center') 176 177 self._error_text = hippo.CanvasText(text='', 178 xalign=hippo.ALIGNMENT_CENTER, 179 font_desc=style.FONT_BOLD.get_pango_desc(), 180 color=style.COLOR_BUTTON_GREY.get_int()) 181 self._box.append(self._error_text) 182 self._layout.append_mode(self._error_text, 'center-bottom') 183 184 button = gtk.Button(label=_('Stop')) 185 button.connect('clicked', self.__close_button_clicked_cb) 186 button.props.image = Icon(icon_name='activity-stop', 187 icon_size=gtk.ICON_SIZE_BUTTON) 188 self._close_button = hippo.CanvasWidget(widget=button, 189 xalign=hippo.ALIGNMENT_CENTER) 190 self._box.append(self._close_button) 191 self._layout.append_mode(self._close_button, 'center-bottom') 192 193 self._box.set_child_visible(self._error_text, False) 194 self._box.set_child_visible(self._close_button, False) 195 38 196 canvas = hippo.Canvas() 39 197 canvas.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color()) 40 self.add(canvas)41 canvas.show()42 43 self._activity_id = activity_id44 self._box = LaunchBox(activity_id, icon_path, icon_color)45 198 canvas.set_root(self._box) 199 canvas.show() 200 self.add(canvas) 46 201 47 202 self.connect('realize', self.__realize_cb) 48 203 49 204 screen = gtk.gdk.screen_get_default() 50 205 screen.connect('size-changed', self.__size_changed_cb) 51 52 206 self._update_size() 207 208 anim = animator.Animator(1.0) 209 anim.add(_PulseAnimation(self._activity_icon, 210 self._icon_color, XOCOLOR_GRAY)) 211 anim.add(_ZoomAnimation(self._activity_icon, 212 style.STANDARD_ICON_SIZE, style.XLARGE_ICON_SIZE)) 213 anim.start() 214 215 self._timeout_id = gobject.timeout_add(3000, self._pulse_icon) 216 217 def _pulse_icon(self): 218 anim = animator.Animator(1.0) 219 anim.add(_PulseAnimation(self._activity_icon, 220 self._icon_color, XOCOLOR_GRAY)) 221 anim.start() 222 return True 53 223 54 224 def show(self): 55 225 self.present() 56 self._box.zoom_in()57 226 58 227 def _update_size(self): 59 228 self.resize(gtk.gdk.screen_width(), gtk.gdk.screen_height()) … … class LaunchWindow(gtk.Window): 66 235 def __size_changed_cb(self, screen): 67 236 self._update_size() 68 237 69 class LaunchBox(hippo.CanvasBox): 70 def __init__(self, activity_id, icon_path, icon_color): 71 gobject.GObject.__init__(self, orientation=hippo.ORIENTATION_VERTICAL) 238 def show_failed(self, home_activity): 239 logging.debug("Displaying failure message on launcher") 240 241 gobject.source_remove(self._timeout_id) 242 243 self._home_activity = home_activity 72 244 73 self._activity_id = activity_id 74 self._activity_icon = CanvasPulsingIcon( 75 file_name=icon_path, 76 pulse_color=icon_color, 77 background_color=style.COLOR_WHITE.get_gdk_color()) 78 self.append(self._activity_icon, hippo.PACK_EXPAND) 79 80 # FIXME support non-xo colors in CanvasPulsingIcon 81 self._activity_icon.props.base_color = \ 82 XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(), 83 style.COLOR_TRANSPARENT.get_svg())) 84 85 self._animator = animator.Animator(1.0) 86 87 self._home = shell.get_model() 88 self._home.connect('active-activity-changed', 89 self.__active_activity_changed_cb) 90 91 self.connect('destroy', self.__destroy_cb) 92 93 def __destroy_cb(self, box): 94 self._activity_icon.props.pulsing = False 95 self._home.disconnect_by_func(self.__active_activity_changed_cb) 96 97 def zoom_in(self): 98 self._activity_icon.props.size = style.STANDARD_ICON_SIZE 99 100 self._animator.remove_all() 101 self._animator.add(_Animation(self._activity_icon, 102 style.STANDARD_ICON_SIZE, 103 style.XLARGE_ICON_SIZE)) 104 self._animator.start() 105 self._activity_icon.props.pulsing = True 106 107 def __active_activity_changed_cb(self, model, activity): 108 if activity.get_activity_id() == self._activity_id: 109 self._activity_icon.props.paused = False 110 else: 111 self._activity_icon.props.paused = True 112 113 class _Animation(animator.Animation): 114 def __init__(self, icon, start_size, end_size): 115 animator.Animation.__init__(self, 0.0, 1.0) 245 self._box.set_child_visible(self._error_text, True) 246 self._box.set_child_visible(self._close_button, True) 116 247 117 self._icon = icon 118 self.start_size = start_size 119 self.end_size = end_size 248 self._error_text.props.text = _('%s failed to start.') % \ 249 home_activity.get_activity_name() 120 250 121 def next_frame(self, current): 122 d = (self.end_size - self.start_size) * current 123 self._icon.props.size = int(self.start_size + d) 251 def __close_button_clicked_cb(self, button): 252 _destroy_launcher(self._home_activity) 124 253 125 254 _launchers = {} 126 255 … … def setup(): 131 260 model.connect('launch-completed', __launch_completed_cb) 132 261 133 262 def add_launcher(activity_id, icon_path, icon_color): 134 135 263 if activity_id in _launchers: 136 264 return 137 265 … … def __launch_started_cb(home_model, home_activity): 145 273 home_activity.get_icon_color()) 146 274 147 275 def __launch_failed_cb(home_model, home_activity): 148 _destroy_launcher(home_activity) 276 activity_id = home_activity.get_activity_id() 277 278 if activity_id in _launchers: 279 _launchers[activity_id].show_failed(home_activity) 280 else: 281 logging.error('Launcher for %s is missing', activity_id) 149 282 150 283 def __launch_completed_cb(home_model, home_activity): 151 284 _destroy_launcher(home_activity) -
src/jarabe/view/service.py
diff --git a/src/jarabe/view/service.py b/src/jarabe/view/service.py index 2b91437..c1da23c 100644
a b class UIService(dbus.service.Object): 98 98 def NotifyLaunchFailure(self, activity_id): 99 99 shell.get_model().notify_launch_failed(activity_id) 100 100 101 @dbus.service.method(_DBUS_SHELL_IFACE, 102 in_signature="s", out_signature="") 103 def NotifyActivityEnded(self, activity_id): 104 shell.get_model().notify_activity_ended(activity_id) 105 101 106 @dbus.service.signal(_DBUS_OWNER_IFACE, signature="s") 102 107 def ColorChanged(self, color): 103 108 pass