diff options
Diffstat (limited to 'sugar')
-rw-r--r-- | sugar/Makefile.am | 24 | ||||
-rw-r--r-- | sugar/_sugaruiext.defs | 20 | ||||
-rw-r--r-- | sugar/_sugaruiext.override | 1 | ||||
-rw-r--r-- | sugar/activity/activityfactory.py | 7 | ||||
-rw-r--r-- | sugar/activity/bundle.py | 14 | ||||
-rw-r--r-- | sugar/activity/bundlebuilder.py | 3 | ||||
-rw-r--r-- | sugar/activity/registry.py | 37 | ||||
-rw-r--r-- | sugar/graphics/canvasicon.py | 8 | ||||
-rw-r--r-- | sugar/graphics/iconbutton.py | 2 | ||||
-rw-r--r-- | sugar/graphics/objectchooser.py | 6 | ||||
-rw-r--r-- | sugar/graphics/palette.py | 342 | ||||
-rw-r--r-- | sugar/graphics/radiotoolbutton.py | 18 | ||||
-rw-r--r-- | sugar/graphics/style.py | 7 | ||||
-rw-r--r-- | sugar/graphics/toggletoolbutton.py | 18 | ||||
-rw-r--r-- | sugar/graphics/toolbutton.py | 17 |
15 files changed, 358 insertions, 166 deletions
diff --git a/sugar/Makefile.am b/sugar/Makefile.am index 5fcb387..dffca33 100644 --- a/sugar/Makefile.am +++ b/sugar/Makefile.am @@ -12,21 +12,19 @@ sugar_PYTHON = \ util.py \ wm.py -INCLUDES = \ - $(LIB_CFLAGS) \ - $(LIB_BINDINGS_CFLAGS) \ - $(PYTHON_INCLUDES) \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/lib/ui - pkgpyexecdir = $(pythondir)/sugar pkgpyexec_LTLIBRARIES = _sugarext.la _sugaruiext.la +_sugarext_la_CFLAGS = \ + $(LIB_CFLAGS) \ + $(LIB_BINDINGS_CFLAGS) \ + $(PYTHON_INCLUDES) \ + -I$(top_srcdir)/lib + _sugarext_la_LDFLAGS = -module -avoid-version _sugarext_la_LIBADD = \ $(LIB_BINDINGS_LIBS) \ - $(LIB_LIBS) \ $(top_builddir)/lib/libsugar.la _sugarext_la_SOURCES = \ @@ -36,10 +34,16 @@ nodist__sugarext_la_SOURCES = _sugarext.c _sugarext.c: _sugarext.defs _sugarext.override +_sugaruiext_la_CFLAGS = \ + $(LIBUI_CFLAGS) \ + $(LIBUI_BINDINGS_CFLAGS) \ + $(PYTHON_INCLUDES) \ + -I$(top_srcdir)/lib/ui + _sugaruiext_la_LDFLAGS = -module -avoid-version _sugaruiext_la_LIBADD = \ - $(LIB_BINDINGS_LIBS) \ - $(LIB_LIBS) \ + $(LIBUI_BINDINGS_LIBS) \ + $(LIBUI_LIBS) \ $(top_builddir)/lib/ui/libsugarui.la _sugaruiext_la_SOURCES = \ diff --git a/sugar/_sugaruiext.defs b/sugar/_sugaruiext.defs index c4efbbc..3c011e1 100644 --- a/sugar/_sugaruiext.defs +++ b/sugar/_sugaruiext.defs @@ -26,19 +26,27 @@ ;; From sugar-menu.h -(define-method popup +(define-method set_active (of-object "SugarMenu") - (c-name "sugar_menu_popup") + (c-name "sugar_menu_set_active") (return-type "none") (parameters - '("gint" "x") - '("gint" "y") + '("gboolean" "active") ) ) -(define-method popdown +(define-method embed (of-object "SugarMenu") - (c-name "sugar_menu_popdown") + (c-name "sugar_menu_embed") + (return-type "none") + (parameters + '("GtkContainer" "container") + ) +) + +(define-method unembed + (of-object "SugarMenu") + (c-name "sugar_menu_unembed") (return-type "none") ) diff --git a/sugar/_sugaruiext.override b/sugar/_sugaruiext.override index 02e900e..6daafc3 100644 --- a/sugar/_sugaruiext.override +++ b/sugar/_sugaruiext.override @@ -18,6 +18,7 @@ modulename _sugarext import gobject.GObject as PyGObject_Type import gtk.Entry as PyGtkEntry_Type import gtk.Menu as PyGtkMenu_Type +import gtk.Container as PyGtkContainer_Type import gtk.gdk.Window as PyGdkWindow_Type %% ignore-glob diff --git a/sugar/activity/activityfactory.py b/sugar/activity/activityfactory.py index 404e5f4..b1d55eb 100644 --- a/sugar/activity/activityfactory.py +++ b/sugar/activity/activityfactory.py @@ -114,7 +114,7 @@ class ActivityCreationHandler(gobject.GObject): self._factory.create(self._activity_handle.get_dict(), timeout=120 * 1000, - reply_handler=self._no_reply_handler, + reply_handler=self._create_reply_handler, error_handler=self._create_error_handler) def get_activity_id(self): @@ -137,7 +137,10 @@ class ActivityCreationHandler(gobject.GObject): def _activate_error_handler(self, err): logging.debug("Activity activation request failed %s" % err) - def _create_reply_handler(self, xid): + def _create_reply_handler(self, xid=None): + if xid is None: + self._create_error_handler('D-Bus error') + return logging.debug("Activity created %s (%s)." % (self._activity_handle.activity_id, self._service_name)) diff --git a/sugar/activity/bundle.py b/sugar/activity/bundle.py index a9c246d..d361c62 100644 --- a/sugar/activity/bundle.py +++ b/sugar/activity/bundle.py @@ -41,6 +41,7 @@ class NotInstalledException(Exception): pass class InvalidPathException(Exception): pass class ZipExtractException(Exception): pass class RegistrationException(Exception): pass +class MalformedBundleException(Exception): pass class Bundle: """Metadata description of a given application/activity @@ -265,10 +266,12 @@ class Bundle: if not bundle_root_dir: bundle_root_dir = file_name.split('/')[0] if not bundle_root_dir.endswith('.activity'): - raise 'Incorrect bundle.' + raise MalformedBundleException( + 'The activity directory name must end with .activity') else: if not file_name.startswith(bundle_root_dir): - raise 'Incorrect bundle.' + raise MalformedBundleException( + 'All files in the bundle must be inside the activity directory') return bundle_root_dir @@ -293,11 +296,8 @@ class Bundle: raise ZipExtractException self._init_with_path(bundle_path) - - bus = dbus.SessionBus() - proxy_obj = bus.get_object(_DBUS_SHELL_SERVICE, _DBUS_SHELL_PATH) - dbus_service = dbus.Interface(proxy_obj, _DBUS_ACTIVITY_REGISTRY_IFACE) - if not dbus_service.AddBundle(bundle_path): + + if not activity.get_registry().add_bundle(bundle_path): raise RegistrationException def deinstall(self): diff --git a/sugar/activity/bundlebuilder.py b/sugar/activity/bundlebuilder.py index b255cfb..3bbe454 100644 --- a/sugar/activity/bundlebuilder.py +++ b/sugar/activity/bundlebuilder.py @@ -162,8 +162,7 @@ def _get_mo_list(manifest): for lang in _get_po_list(manifest).keys(): filename = _get_service_name() + '.mo' - mo_list.append(os.path.join(_get_source_path(), 'locale', - lang, 'LC_MESSAGES', filename)) + mo_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename)) return mo_list diff --git a/sugar/activity/registry.py b/sugar/activity/registry.py index b19abee..1483a78 100644 --- a/sugar/activity/registry.py +++ b/sugar/activity/registry.py @@ -1,4 +1,5 @@ # Copyright (C) 2006-2007 Red Hat, Inc. +# Copyright (C) 2007 One Laptop Per Child # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,29 +19,39 @@ import logging import dbus +import gobject -_SHELL_SERVICE = "org.laptop.Shell" -_SHELL_PATH = "/org/laptop/Shell" -_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry" +_ACTIVITY_REGISTRY_SERVICE_NAME = 'org.laptop.ActivityRegistry' +_ACTIVITY_REGISTRY_IFACE = 'org.laptop.ActivityRegistry' +_ACTIVITY_REGISTRY_PATH = '/org/laptop/ActivityRegistry' def _activity_info_from_dict(info_dict): if not info_dict: return None return ActivityInfo(info_dict['name'], info_dict['icon'], - info_dict['service_name'], info_dict['path']) + info_dict['service_name'], info_dict['path'], + info_dict['show_launcher']) class ActivityInfo(object): - def __init__(self, name, icon, service_name, path): + def __init__(self, name, icon, service_name, path, show_launcher): self.name = name self.icon = icon self.service_name = service_name self.path = path + self.show_launcher = show_launcher -class ActivityRegistry(object): +class ActivityRegistry(gobject.GObject): + __gsignals__ = { + 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } def __init__(self): + gobject.GObject.__init__(self) + bus = dbus.SessionBus() - bus_object = bus.get_object(_SHELL_SERVICE, _SHELL_PATH) - self._registry = dbus.Interface(bus_object, _REGISTRY_IFACE) + bus_object = bus.get_object(_ACTIVITY_REGISTRY_SERVICE_NAME, + _ACTIVITY_REGISTRY_PATH) + self._registry = dbus.Interface(bus_object, _ACTIVITY_REGISTRY_IFACE) self._registry.connect_to_signal('ActivityAdded', self._activity_added_cb) # Two caches fo saving some travel across dbus. @@ -55,6 +66,10 @@ class ActivityRegistry(object): return result + def get_activities(self): + info_list = self._registry.GetActivities() + return self._convert_info_list(info_list) + def get_activity(self, service_name): if self._service_name_to_activity_info.has_key(service_name): return self._service_name_to_activity_info[service_name] @@ -79,10 +94,14 @@ class ActivityRegistry(object): self._mime_type_to_activities[mime_type] = activities return activities - def _activity_added_cb(self, bundle): + def add_bundle(self, bundle_path): + return self._registry.AddBundle(bundle_path) + + def _activity_added_cb(self, info_dict): logging.debug('ActivityRegistry._activity_added_cb: flushing caches') self._service_name_to_activity_info.clear() self._mime_type_to_activities.clear() + self.emit('activity-added', _activity_info_from_dict(info_dict)) _registry = None diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py index e879f05..39f1358 100644 --- a/sugar/graphics/canvasicon.py +++ b/sugar/graphics/canvasicon.py @@ -231,13 +231,13 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): stroke_color = None if self._active: if self._fill_color: - fill_color = self._fill_color.get_html() + fill_color = self._fill_color.get_svg() if self._stroke_color: - stroke_color = self._stroke_color.get_html() + stroke_color = self._stroke_color.get_svg() else: - stroke_color = color.ICON_STROKE_INACTIVE.get_html() + stroke_color = color.ICON_STROKE_INACTIVE.get_svg() if self._fill_color: - fill_color = self._fill_color.get_html() + fill_color = self._fill_color.get_svg() return [fill_color, stroke_color] def _get_handle(self, name, handle): diff --git a/sugar/graphics/iconbutton.py b/sugar/graphics/iconbutton.py index 85ea4e4..ba9fad8 100644 --- a/sugar/graphics/iconbutton.py +++ b/sugar/graphics/iconbutton.py @@ -53,7 +53,7 @@ class IconButton(CanvasIcon, hippo.CanvasItem): if self.props.active: self.props.background_color = 0x000000FF else: - self.props.background_color = 0x404040FF + self.props.background_color = 0x00000000 def _icon_clicked_cb(self, button): if self._palette: diff --git a/sugar/graphics/objectchooser.py b/sugar/graphics/objectchooser.py index c75cec0..5b09e13 100644 --- a/sugar/graphics/objectchooser.py +++ b/sugar/graphics/objectchooser.py @@ -21,12 +21,12 @@ import time import gtk import hippo -from sugar.graphics.frame import Frame from sugar.activity.bundle import Bundle from sugar.date import Date from sugar.graphics import style from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.xocolor import XoColor +from sugar.graphics.canvasroundbox import CanvasRoundBox from sugar.datastore import datastore from sugar import activity from sugar.objects import objecttype @@ -95,12 +95,12 @@ class ObjectChooser(gtk.Dialog): else: return None -class CollapsedEntry(Frame): +class CollapsedEntry(CanvasRoundBox): _DATE_COL_WIDTH = style.zoom(100) _BUDDIES_COL_WIDTH = style.zoom(50) def __init__(self, jobject): - Frame.__init__(self) + CanvasRoundBox.__init__(self) self.props.box_height = style.zoom(75) self.props.spacing = style.DEFAULT_SPACING self.props.border_color = style.COLOR_BLACK.get_int() diff --git a/sugar/graphics/palette.py b/sugar/graphics/palette.py index 45ac057..368a0f6 100644 --- a/sugar/graphics/palette.py +++ b/sugar/graphics/palette.py @@ -36,7 +36,40 @@ _RIGHT_TOP = 5 _TOP_LEFT = 6 _TOP_RIGHT = 7 -class Palette(gobject.GObject): + +# Helper function to find the gap position and size of widget a +def _calculate_gap(a, b): + # Test for each side if the palette and invoker are + # adjacent to each other. + gap = True + + if a.y + a.height == b.y: + gap_side = gtk.POS_BOTTOM + elif a.x + a.width == b.x: + gap_side = gtk.POS_RIGHT + elif a.x == b.x + b.width: + gap_side = gtk.POS_LEFT + elif a.y == b.y + b.height: + gap_side = gtk.POS_TOP + else: + gap = False + + if gap: + if gap_side == gtk.POS_BOTTOM or gap_side == gtk.POS_TOP: + gap_start = min(a.width, max(0, b.x - a.x)) + gap_size = max(0, min(a.width, + (b.x + b.width) - a.x) - gap_start) + elif gap_side == gtk.POS_RIGHT or gap_side == gtk.POS_LEFT: + gap_start = min(a.height, max(0, b.y - a.y)) + gap_size = max(0, min(a.height, + (b.y + b.height) - a.y) - gap_start) + + if gap and gap_size > 0: + return (gap_side, gap_start, gap_size) + else: + return False + +class Palette(gtk.Window): DEFAULT = 0 AT_CURSOR = 1 AROUND = 2 @@ -54,7 +87,9 @@ class Palette(gobject.GObject): 'invoker' : (object, None, None, gobject.PARAM_READWRITE), 'position' : (gobject.TYPE_INT, None, None, 0, 6, - 0, gobject.PARAM_READWRITE) + 0, gobject.PARAM_READWRITE), + 'draw-gap' : (bool, None, None, False, + gobject.PARAM_READWRITE) } __gsignals__ = { @@ -65,16 +100,21 @@ class Palette(gobject.GObject): } def __init__(self, label, accel_path=None): - gobject.GObject.__init__(self) + gtk.Window.__init__(self) + + self.set_decorated(False) + self.set_resizable(False) + self.connect('realize', self._realize_cb) self._full_request = [0, 0] self._cursor_x = 0 self._cursor_y = 0 - self._state = self._SECONDARY + self._state = self._PRIMARY self._invoker = None self._group_id = None self._up = False self._position = self.DEFAULT + self._draw_gap = False self._palette_popup_sid = None self._popup_anim = animator.Animator(0.3, 10) @@ -86,60 +126,70 @@ class Palette(gobject.GObject): self._popdown_anim = animator.Animator(0.6, 10) self._popdown_anim.add(_PopdownAnimation(self)) - self._menu = _sugaruiext.Menu() + vbox = gtk.VBox() + vbox.set_border_width(style.DEFAULT_PADDING) - self._primary = _PrimaryMenuItem(label, accel_path) - self._menu.append(self._primary) - self._primary.show() + self._label = gtk.Label() + vbox.pack_start(self._label, False) - self._separator = gtk.SeparatorMenuItem() - self._menu.append(self._separator) + self._secondary_box = gtk.VBox() + vbox.pack_start(self._secondary_box) - self._content = _ContentMenuItem() - self._menu.append(self._content) + self._separator = gtk.HSeparator() + self._secondary_box.pack_start(self._separator) - self._button_bar = _ButtonBarMenuItem() - self._menu.append(self._button_bar) + self._menu_box = gtk.VBox() + self._secondary_box.pack_start(self._menu_box) + self._menu_box.show() - self._menu.connect('enter-notify-event', - self._enter_notify_event_cb) - self._menu.connect('leave-notify-event', - self._leave_notify_event_cb) + self._content = gtk.VBox() + self._secondary_box.pack_start(self._content) + self._content.show() - def is_up(self): - return self._up + self.action_bar = PaletteActionBar() + self._secondary_box.pack_start(self.action_bar) + self.action_bar.show() - def set_primary_text(self, label, accel_path=None): - self._primary.set_label(label, accel_path) + self.add(vbox) + vbox.show() - def append_menu_item(self, item): - self._separator.show() - self._menu.insert(item, len(self._menu.get_children()) - 2) + self.menu = _Menu(self) + self.menu.show() - def insert_menu_item(self, item, index=-1): - self._separator.show() - if index < 0: - self._menu.insert(item, len(self._menu.get_children()) - 2) - else: - self._menu.insert(item, index + 2) + self.connect('enter-notify-event', + self._enter_notify_event_cb) + self.connect('leave-notify-event', + self._leave_notify_event_cb) + + self.set_primary_text(label, accel_path) - def remove_menu_item(self, index): - if index > len(self._menu.get_children()) - 4: - raise ValueError('index %i out of range' % index) - self._menu.remove(self._menu.get_children()[index + 2]) - if len(self._menu.get_children()) == 0: - self._separator.hide() + def is_up(self): + return self._up + + def get_rect(self): + win_x, win_y = self.window.get_origin() + rectangle = self.get_allocation() - def menu_item_count(self): - return len(self._menu.get_children()) - 4 + x = win_x + rectangle.x + y = win_y + rectangle.y + width = rectangle.width + height = rectangle.height + return gtk.gdk.Rectangle(x, y, width, height) + + def set_primary_text(self, label, accel_path=None): + self._label.set_text(label) + self._label.show() + def set_content(self, widget): - self._content.set_widget(widget) - self._content.show() + if len(self._content.get_children()) > 0: + self.remove(self._content.get_children()[0]) + + if widget is not None: + self._content.add(widget) - def append_button(self, button): - self._button_bar.append_button(button) - self._button_bar.show() + self._update_accept_focus() + self._update_separator() def set_group_id(self, group_id): if self._group_id: @@ -154,9 +204,11 @@ class Palette(gobject.GObject): self._invoker = value self._invoker.connect('mouse-enter', self._invoker_mouse_enter_cb) self._invoker.connect('mouse-leave', self._invoker_mouse_leave_cb) - self._invoker.connect('focus-out', self._invoker_focus_out_cb) elif pspec.name == 'position': self._position = value + elif pspec.name == 'draw-gap': + self._draw_gap = value + self.queue_draw() else: raise AssertionError @@ -165,9 +217,57 @@ class Palette(gobject.GObject): return self._invoker elif pspec.name == 'position': return self._position + elif pspec.name == 'draw-gap': + return self._draw_gap else: raise AssertionError + def do_size_allocate(self, allocation): + gtk.Window.do_size_allocate(self, allocation) + self.queue_draw() + + def do_expose_event(self, event): + # We want to draw a border with a beautiful gap + if self._draw_gap: + invoker = self._invoker.get_rect() + palette = self.get_rect() + + gap = _calculate_gap(palette, invoker) + else: + gap = False + + if gap: + self.style.paint_box_gap(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self, "palette", + 0, 0, + self.allocation.width, + self.allocation.height, + gap[0], gap[1], gap[2]) + else: + self.style.paint_box(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self, "palette", + 0, 0, + self.allocation.width, + self.allocation.height) + + # Fall trough to the container expose handler. + # (Leaving out the window expose handler which redraws everything) + gtk.Bin.do_expose_event(self, event) + + def _update_separator(self): + visible = len(self.menu.get_children()) > 0 or \ + len(self._content.get_children()) > 0 + self._separator.props.visible = visible + + def _update_accept_focus(self): + accept_focus = len(self._content.get_children()) + if self.window: + self.window.set_accept_focus(accept_focus) + + def _realize_cb(self, widget): + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self._update_accept_focus() + def _in_screen(self, x, y): [width, height] = self._full_request screen_area = self._invoker.get_screen_area() @@ -182,7 +282,7 @@ class Palette(gobject.GObject): if inv_rect == None: inv_rect = self._invoker.get_rect() - palette_width, palette_height = self._menu.size_request() + palette_width, palette_height = self.size_request() x = inv_rect.x + inv_rect.width * invoker_halign + \ palette_width * palette_halign @@ -241,12 +341,12 @@ class Palette(gobject.GObject): def _update_full_request(self): state = self._state - self._menu.set_size_request(-1, -1) + self.set_size_request(-1, -1) self._set_state(self._SECONDARY) - self._full_request = self._menu.size_request() + self._full_request = self.size_request() - self._menu.set_size_request(self._full_request[0], -1) + self.set_size_request(self._full_request[0], -1) self._set_state(state) @@ -282,7 +382,7 @@ class Palette(gobject.GObject): elif position == self.TOP: x, y = self._get_top_position() - self._menu.popup(x, y) + self.move(x, y) def _show(self): if self._up: @@ -291,11 +391,12 @@ class Palette(gobject.GObject): self._update_cursor_position() self._update_full_request() - self._invoker.connect_to_parent() - self._palette_popup_sid = _palette_observer.connect('popup', - self._palette_observer_popup_cb) + self._palette_popup_sid = _palette_observer.connect( + 'popup', self._palette_observer_popup_cb) self._update_position() + self.menu.set_active(True) + self.show() self._up = True _palette_observer.emit('popup', self) @@ -305,7 +406,8 @@ class Palette(gobject.GObject): if not self._palette_popup_sid is None: _palette_observer.disconnect(self._palette_popup_sid) self._palette_popup_sid = None - self._menu.popdown() + self.menu.set_active(False) + self.hide() self._up = False self.emit('popdown') @@ -329,25 +431,11 @@ class Palette(gobject.GObject): return if state == self._PRIMARY: - self._primary.show() - for menu_item in self._menu.get_children()[1:]: - menu_item.hide() + self.menu.unembed() + self._secondary_box.hide() elif state == self._SECONDARY: - middle_menu_items = self._menu.get_children() - middle_menu_items = middle_menu_items[2:len(middle_menu_items) - 2] - if middle_menu_items or \ - not self._content.is_empty() or \ - not self._button_bar.is_empty(): - self._separator.show() - - for menu_item in middle_menu_items: - menu_item.show() - - if not self._content.is_empty(): - self._content.show() - - if not self._button_bar.is_empty(): - self._button_bar.show() + self.menu.embed(self._menu_box) + self._secondary_box.show() self._state = state @@ -357,68 +445,54 @@ class Palette(gobject.GObject): def _invoker_mouse_leave_cb(self, invoker): self.popdown() - def _invoker_focus_out_cb(self, invoker): - self._hide() - def _enter_notify_event_cb(self, widget, event): - if event.detail == gtk.gdk.NOTIFY_NONLINEAR: + if event.detail != gtk.gdk.NOTIFY_INFERIOR: self._popdown_anim.stop() self._secondary_anim.start() def _leave_notify_event_cb(self, widget, event): - if event.detail == gtk.gdk.NOTIFY_NONLINEAR: + if event.detail != gtk.gdk.NOTIFY_INFERIOR: self.popdown() def _palette_observer_popup_cb(self, observer, palette): if self != palette: self._hide() -class _PrimaryMenuItem(gtk.MenuItem): - def __init__(self, label, accel_path): - gtk.MenuItem.__init__(self) - self.set_border_width(style.DEFAULT_PADDING) - self._set_label(label, accel_path) +class PaletteActionBar(gtk.HButtonBox): + def add_action(label, icon_name=None): + button = Button(label) - def set_label(self, label, accel_path): - self.remove(self._label) - self._set_label(label, accel_path) + if icon_name: + icon = Icon(icon_name) + button.set_image(icon) + icon.show() - def _set_label(self, label, accel_path): - self._label = gtk.AccelLabel(label) - self._label.set_accel_widget(self) + self.pack_start(button) + button.show() - if accel_path: - self.set_accel_path(accel_path) - self._label.set_alignment(0.0, 0.5) +class _Menu(_sugaruiext.Menu): + __gtype_name__ = 'SugarPaletteMenu' - self.add(self._label) - self._label.show() - -class _ContentMenuItem(gtk.MenuItem): - def __init__(self): - gtk.MenuItem.__init__(self) - - def set_widget(self, widget): - if self.child: - self.remove(self.child) - self.add(widget) - - def is_empty(self): - return self.child is None or not self.child.props.visible + def __init__(self, palette): + _sugaruiext.Menu.__init__(self) + self._palette = palette -class _ButtonBarMenuItem(gtk.MenuItem): - def __init__(self): - gtk.MenuItem.__init__(self) + def do_insert(self, item, position): + _sugaruiext.Menu.do_insert(self, item, position) + self._palette._update_separator() - self._hbar = gtk.HButtonBox() - self.add(self._hbar) - self._hbar.show() + def do_expose_event(self, event): + # Ignore the Menu expose, just do the MenuShell expose to prevent any + # border from being drawn here. A border is drawn by the palette object + # around everything. + gtk.MenuShell.do_expose_event(self, event) - def append_button(self, button): - self._hbar.pack_start(button) + def do_grab_notify(self, was_grabbed): + # Ignore grab_notify as the menu would close otherwise + pass - def is_empty(self): - return len(self._hbar.get_children()) == 0 + def do_deactivate(self): + self._palette._hide() class _PopupAnimation(animator.Animation): def __init__(self, palette): @@ -469,13 +543,6 @@ class Invoker(gobject.GObject): height = gtk.gdk.screen_height() return gtk.gdk.Rectangle(0, 0, width, height) - def connect_to_parent(self): - window = self.get_toplevel() - window.connect('focus-out-event', self._window_focus_out_event_cb) - - def _window_focus_out_event_cb(self, widget, event): - self.emit('focus-out') - class WidgetInvoker(Invoker): def __init__(self, widget): Invoker.__init__(self) @@ -495,6 +562,37 @@ class WidgetInvoker(Invoker): return gtk.gdk.Rectangle(x, y, width, height) + def draw_invoker_rect(self, event, palette): + style = self._widget.style + if palette.is_up(): + gap = _calculate_gap(self.get_rect(), palette.get_rect()) + + if gap: + style.paint_box_gap(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self._widget, + "palette-invoker", + self._widget.allocation.x, + self._widget.allocation.y, + self._widget.allocation.width, + self._widget.allocation.height, + gap[0], gap[1], gap[2]) + else: + style.paint_box(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self._widget, + "palette-invoker", + self._widget.allocation.x, + self._widget.allocation.y, + self._widget.allocation.width, + self._widget.allocation.height) + else: + style.paint_box(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_NONE, event.area, self._widget, + "palette-invoker", + self._widget.allocation.x, + self._widget.allocation.y, + self._widget.allocation.width, + self._widget.allocation.height) + def _enter_notify_event_cb(self, widget, event): self.emit('mouse-enter') diff --git a/sugar/graphics/radiotoolbutton.py b/sugar/graphics/radiotoolbutton.py index 94ff6ba..fb584ee 100644 --- a/sugar/graphics/radiotoolbutton.py +++ b/sugar/graphics/radiotoolbutton.py @@ -22,6 +22,8 @@ from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, WidgetInvoker class RadioToolButton(gtk.RadioToolButton): + __gtype_name__ = "SugarRadioToolButton" + def __init__(self, named_icon=None, group=None): gtk.RadioToolButton.__init__(self, group=group) self._palette = None @@ -38,9 +40,25 @@ class RadioToolButton(gtk.RadioToolButton): def set_palette(self, palette): self._palette = palette self._palette.props.invoker = WidgetInvoker(self.child) + self._palette.props.draw_gap = True + + self._palette.connect("popup", self._palette_changed) + self._palette.connect("popdown", self._palette_changed) def set_tooltip(self, text): self._palette = Palette(text) self._palette.props.invoker = WidgetInvoker(self.child) + + def do_expose_event(self, event): + if self._palette and self._palette.props.draw_gap: + if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT: + invoker = self._palette.props.invoker + invoker.draw_invoker_rect(event, self._palette) + + gtk.RadioToolButton.do_expose_event(self, event) + + def _palette_changed(self, palette): + # Force a redraw to update the invoker rectangle + self.queue_draw() palette = property(get_palette, set_palette) diff --git a/sugar/graphics/style.py b/sugar/graphics/style.py index 55b4a4b..a5186da 100644 --- a/sugar/graphics/style.py +++ b/sugar/graphics/style.py @@ -81,6 +81,12 @@ class Color(object): return (r, g, b) + def get_svg(self): + if self._a == 0.0: + return 'none' + else: + return self.get_html() + _XO_DPI = 200.0 _FOCUS_LINE_WIDTH = 2 @@ -113,6 +119,7 @@ TOOLBOX_TAB_LABEL_WIDTH = zoom(150 - 15 * 2) COLOR_BLACK = Color('#000000') COLOR_WHITE = Color('#FFFFFF') +COLOR_TRANSPARENT = Color('#FFFFFF', alpha=0.0) COLOR_PANEL_GREY = Color('#C0C0C0') COLOR_SELECTION_GREY = Color('#A6A6A6') COLOR_INACTIVE_FILL = Color('#9D9FA1') diff --git a/sugar/graphics/toggletoolbutton.py b/sugar/graphics/toggletoolbutton.py index 3684e9c..41050e2 100644 --- a/sugar/graphics/toggletoolbutton.py +++ b/sugar/graphics/toggletoolbutton.py @@ -21,6 +21,8 @@ from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, WidgetInvoker class ToggleToolButton(gtk.ToggleToolButton): + __gtype_name__ = "SugarToggleToolButton" + def __init__(self, named_icon=None): gtk.ToggleToolButton.__init__(self) self._palette = None @@ -37,9 +39,25 @@ class ToggleToolButton(gtk.ToggleToolButton): def set_palette(self, palette): self._palette = palette self._palette.props.invoker = WidgetInvoker(self.child) + self._palette.props.draw_gap = True + + self._palette.connect("popup", self._palette_changed) + self._palette.connect("popdown", self._palette_changed) def set_tooltip(self, text): self._palette = Palette(text) self._palette.props.invoker = WidgetInvoker(self.child) + def do_expose_event(self, event): + if self._palette and self._palette.props.draw_gap: + if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT: + invoker = self._palette.props.invoker + invoker.draw_invoker_rect(event, self._palette) + + gtk.ToggleToolButton.do_expose_event(self, event) + + def _palette_changed(self, palette): + # Force a redraw to update the invoker rectangle + self.queue_draw() + palette = property(get_palette, set_palette) diff --git a/sugar/graphics/toolbutton.py b/sugar/graphics/toolbutton.py index e5d90ab..52a5d62 100644 --- a/sugar/graphics/toolbutton.py +++ b/sugar/graphics/toolbutton.py @@ -23,6 +23,7 @@ from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, WidgetInvoker class ToolButton(gtk.ToolButton): + __gtype_name__ = "SugarToolButton" def __init__(self, icon_name=None): gtk.ToolButton.__init__(self) @@ -41,12 +42,28 @@ class ToolButton(gtk.ToolButton): def set_palette(self, palette): self._palette = palette self._palette.props.invoker = WidgetInvoker(self.child) + self._palette.props.draw_gap = True + + self._palette.connect("popup", self._palette_changed) + self._palette.connect("popdown", self._palette_changed) def set_tooltip(self, text): self.set_palette(Palette(text)) + def do_expose_event(self, event): + if self._palette and self._palette.props.draw_gap: + if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT: + invoker = self._palette.props.invoker + invoker.draw_invoker_rect(event, self._palette) + + gtk.ToolButton.do_expose_event(self, event) + def _button_clicked_cb(self, widget): if self._palette: self._palette.popdown(True) + def _palette_changed(self, palette): + # Force a redraw to update the invoker rectangle + self.queue_draw() + palette = property(get_palette, set_palette) |