Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar3/graphics/palette.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/sugar3/graphics/palette.py')
-rw-r--r--src/sugar3/graphics/palette.py228
1 files changed, 104 insertions, 124 deletions
diff --git a/src/sugar3/graphics/palette.py b/src/sugar3/graphics/palette.py
index ee76674..1f95c11 100644
--- a/src/sugar3/graphics/palette.py
+++ b/src/sugar3/graphics/palette.py
@@ -1,6 +1,8 @@
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
# Copyright (C) 2008, One Laptop Per Child
# Copyright (C) 2009, Tomeu Vizoso
+# Copyright (C) 2011, Benjamin Berg <benjamin@sipsolutions.net>
+# Copyright (C) 2011, Marco Pesenti Gritti <marco@marcopg.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -29,8 +31,8 @@ from sugar3.graphics import palettegroup
from sugar3.graphics import animator
from sugar3.graphics import style
from sugar3.graphics.icon import Icon
-from sugar3.graphics.palettewindow import PaletteWindow
-from gi.repository import SugarExt
+from sugar3.graphics.palettewindow import PaletteWindow, \
+ _PaletteWindowWidget, _PaletteMenuWidget
# DEPRECATED
# Import these for backwards compatibility
@@ -39,16 +41,37 @@ from sugar3.graphics.palettewindow import MouseSpeedDetector, Invoker, \
class Palette(PaletteWindow):
+ """
+ Floating palette implementation.
+
+ This class dynamically switches between one of two encapsulated child
+ widget types: a _PaletteWindowWidget or a _PaletteMenuWidget.
+
+ The window widget, created by default, acts as the container for any
+ type of widget the user may wish to add. It can optionally display primary
+ text, secondary text, and an icon at the top of the palette.
+
+ If the user attempts to access the 'menu' property, the window widget is
+ destroyed and the palette is dynamically switched to use a menu widget.
+ This is a GtkMenu that retains the same look and feel as a normal palette,
+ allowing submenus and so on. If primary text, secondary text and/or icons
+ were provided, an initial menu entry is created containing widgets to
+ display such information.
+ """
+
PRIMARY = 0
SECONDARY = 1
+ __gsignals__ = {
+ 'activate': (GObject.SignalFlags.RUN_FIRST, None, ([])),
+ }
+
__gtype_name__ = 'SugarPalette'
- def __init__(self, label=None, accel_path=None, menu_after_content=False,
+ def __init__(self, label=None, accel_path=None,
text_maxlen=60, **kwargs):
# DEPRECATED: label is passed with the primary-text property,
- # accel_path is set via the invoker property, and menu_after_content
- # is not used
+ # accel_path is set via the invoker property
self._primary_text = None
self._secondary_text = None
@@ -56,15 +79,12 @@ class Palette(PaletteWindow):
self._icon_visible = True
self._palette_state = self.PRIMARY
- palette_box = Gtk.VBox()
-
- primary_box = Gtk.HBox()
- palette_box.pack_start(primary_box, False, True, 0)
- primary_box.show()
+ self._primary_box = Gtk.HBox()
+ self._primary_box.show()
self._icon_box = Gtk.HBox()
self._icon_box.set_size_request(style.GRID_CELL_SIZE, -1)
- primary_box.pack_start(self._icon_box, False, True, 0)
+ self._primary_box.pack_start(self._icon_box, False, True, 0)
labels_box = Gtk.VBox()
self._label_alignment = Gtk.Alignment(xalign=0, yalign=0.5, xscale=1,
@@ -73,7 +93,7 @@ class Palette(PaletteWindow):
style.DEFAULT_SPACING)
self._label_alignment.add(labels_box)
self._label_alignment.show()
- primary_box.pack_start(self._label_alignment, True, True, 0)
+ self._primary_box.pack_start(self._label_alignment, True, True, 0)
labels_box.show()
self._label = Gtk.AccelLabel(label='')
@@ -94,77 +114,51 @@ class Palette(PaletteWindow):
labels_box.pack_start(self._secondary_label, True, True, 0)
self._secondary_box = Gtk.VBox()
- palette_box.pack_start(self._secondary_box, True, True, 0)
self._separator = Gtk.HSeparator()
self._secondary_box.pack_start(self._separator, True, True, 0)
- self._menu_content_separator = Gtk.HSeparator()
-
self._secondary_anim = animator.Animator(2.0, 10)
self._secondary_anim.add(_SecondaryAnimation(self))
# we init after initializing all of our containers
PaletteWindow.__init__(self, **kwargs)
- primary_box.set_size_request(-1, style.GRID_CELL_SIZE
- - 2 * self.get_border_width())
-
self._full_request = [0, 0]
- self._menu_box = None
self._content = None
# we set these for backward compatibility
if label is not None:
self.props.primary_text = label
- self._add_menu()
- self._secondary_box.pack_start(self._menu_content_separator, True, True, 0)
self._add_content()
self.action_bar = PaletteActionBar()
self._secondary_box.pack_start(self.action_bar, True, True, 0)
self.action_bar.show()
- self.add(palette_box)
- palette_box.show()
+ self.connect('notify::invoker', self.__notify_invoker_cb)
+ self.connect('popdown', self.__popdown_cb)
- # The menu is not shown here until an item is added
- self.menu = _Menu(self)
- self.menu.connect('item-inserted', self.__menu_item_inserted_cb)
+ # Default to a normal window palette
+ self._content_widget = None
+ self.set_content(None)
- self.connect('realize', self.__realize_cb)
- self.connect('show', self.__show_cb)
- self.connect('hide', self.__hide_cb)
- self.connect('notify::invoker', self.__notify_invoker_cb)
- self.connect('destroy', self.__destroy_cb)
+ def _setup_widget(self):
+ PaletteWindow._setup_widget(self)
+ self._widget.connect('destroy', self.__destroy_cb)
def _invoker_right_click_cb(self, invoker):
self.popup(immediate=True, state=self.SECONDARY)
- def do_style_set(self, previous_style):
- # Prevent a warning from pygtk
- if previous_style is not None:
- Gtk.Window.do_style_set(self, previous_style)
- self.set_border_width(self.get_style().xthickness)
-
- def __menu_item_inserted_cb(self, menu):
- self._update_separators()
-
def __destroy_cb(self, palette):
self._secondary_anim.stop()
self.popdown(immediate=True)
# Break the reference cycle. It looks like the gc is not able to free
# it, possibly because Gtk.Menu memory handling is very special.
- self.menu.disconnect_by_func(self.__menu_item_inserted_cb)
- self.menu = None
+ self._widget = None
- def __show_cb(self, widget):
- self.menu.set_active(True)
-
- def __hide_cb(self, widget):
- self.menu.set_active(False)
- self.menu.cancel()
+ def __popdown_cb(self, widget):
self._secondary_anim.stop()
def __notify_invoker_cb(self, palette, pspec):
@@ -198,28 +192,16 @@ class Palette(PaletteWindow):
def popdown(self, immediate=False):
if immediate:
self._secondary_anim.stop()
- self._popdown_submenus()
# to suppress glitches while later re-opening
self.set_palette_state(self.PRIMARY)
+ if self._widget:
+ self._widget.size_request()
PaletteWindow.popdown(self, immediate)
- def _popdown_submenus(self):
- # TODO explicit hiding of subitems
- # should be removed after fixing #1301
- if self.menu is not None:
- for menu_item in self.menu.get_children():
- if menu_item.props.submenu is not None:
- menu_item.props.submenu.popdown()
-
- def on_enter(self, event):
- PaletteWindow.on_enter(self, event)
+ def on_enter(self):
+ PaletteWindow.on_enter(self)
self._secondary_anim.start()
- def _add_menu(self):
- self._menu_box = Gtk.VBox()
- self._secondary_box.pack_start(self._menu_box, True, True, 0)
- self._menu_box.show()
-
def _add_content(self):
# The content is not shown until a widget is added
self._content = Gtk.VBox()
@@ -315,6 +297,22 @@ class Palette(PaletteWindow):
setter=set_icon_visible)
def set_content(self, widget):
+ assert self._widget is None \
+ or isinstance(self._widget, _PaletteWindowWidget)
+
+ if self._widget is None:
+ self._widget = _PaletteWindowWidget()
+ self._setup_widget()
+
+ self._palette_box = Gtk.VBox()
+ self._palette_box.pack_start(self._primary_box, False, True, 0)
+ self._palette_box.pack_start(self._secondary_box, True, True, 0)
+
+ self._widget.add(self._palette_box)
+ self._palette_box.show()
+ height = style.GRID_CELL_SIZE - 2 * self._widget.get_border_width()
+ self._primary_box.set_size_request(-1, height)
+
if self._content.get_children():
self._content.remove(self._content.get_children()[0])
@@ -324,48 +322,37 @@ class Palette(PaletteWindow):
else:
self._content.hide()
+ self._content_widget = widget
+
self._update_accept_focus()
self._update_separators()
- def do_size_request(self, requisition):
- PaletteWindow.do_size_request(self, requisition)
+ def do_get_preferred_width(self):
+ minimum, natural = PaletteWindow.do_get_preferred_width(self)
# Gtk.AccelLabel request doesn't include the accelerator.
label_width = self._label_alignment.size_request()[0] + \
self._label.get_accel_width() + \
2 * self.get_border_width()
- requisition.width = max(requisition.width,
- label_width,
- self._full_request[0])
+ width = max(minimum, label_width, self._full_request[0])
+ return width, width
def _update_separators(self):
- visible = self.menu.get_children() or \
- self._content.get_children()
+ visible = self._content.get_children()
self._separator.props.visible = visible
- visible = self.menu.get_children() and \
- self._content.get_children()
- self._menu_content_separator.props.visible = visible
-
def _update_accept_focus(self):
accept_focus = len(self._content.get_children())
- window = self.get_window()
- if window:
- window.set_accept_focus(accept_focus)
-
- def __realize_cb(self, widget):
- self._update_accept_focus()
+ self._widget.set_accept_focus(accept_focus)
def _update_full_request(self):
if self._palette_state == self.PRIMARY:
- self.menu.embed(self._menu_box)
self._secondary_box.show()
- self._full_request = self.size_request()
+ self._full_request = self._widget.size_request()
if self._palette_state == self.PRIMARY:
- self.menu.unembed()
self._secondary_box.hide()
def _set_palette_state(self, state):
@@ -373,15 +360,46 @@ class Palette(PaletteWindow):
return
if state == self.PRIMARY:
- self.menu.unembed()
self._secondary_box.hide()
elif state == self.SECONDARY:
- self.menu.embed(self._menu_box)
self._secondary_box.show()
self.update_position()
self._palette_state = state
+ def get_menu(self):
+ assert self._content_widget is None
+
+ if self._widget is None \
+ or not isinstance(self._widget, _PaletteMenuWidget):
+ if self._widget is not None:
+ self._palette_box.remove(self._primary_box)
+ self._palette_box.remove(self._secondary_box)
+ self._teardown_widget()
+ self._widget.destroy()
+
+ self._widget = _PaletteMenuWidget()
+
+ self._label_menuitem = Gtk.MenuItem()
+ child = self._label_menuitem.get_child()
+ if child is not None:
+ self._label_menuitem.remove(child)
+ self._label_menuitem.add(self._primary_box)
+
+ # Mark the menuitem as insensitive so that it appears as an
+ # informational element, rather than a clickable item in the menu.
+ # TODO: see if we can do this better in GTK.
+ self._label_menuitem.set_sensitive(False)
+
+ self._label_menuitem.show()
+ self._widget.append(self._label_menuitem)
+
+ self._setup_widget()
+
+ return self._widget
+
+ menu = GObject.property(type=object, getter=get_menu)
+
class PaletteActionBar(Gtk.HButtonBox):
@@ -397,44 +415,6 @@ class PaletteActionBar(Gtk.HButtonBox):
button.show()
-class _Menu(SugarExt.Menu):
-
- __gtype_name__ = 'SugarPaletteMenu'
-
- __gsignals__ = {
- 'item-inserted': (GObject.SignalFlags.RUN_FIRST, None, ([])),
- }
-
- def __init__(self, palette):
- SugarExt.Menu.__init__(self)
- self._palette = palette
-
- def do_insert(self, item, position):
- SugarExt.Menu.do_insert(self, item, position)
- self.emit('item-inserted')
- self.show()
-
- def attach(self, child, left_attach, right_attach,
- top_attach, bottom_attach):
- SugarExt.Menu.attach(self, child, left_attach, right_attach,
- top_attach, bottom_attach)
- self.emit('item-inserted')
- self.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 do_grab_notify(self, was_grabbed):
- # Ignore grab_notify as the menu would close otherwise
- pass
-
- def do_deactivate(self):
- self._palette.hide()
-
-
class _SecondaryAnimation(animator.Animation):
def __init__(self, palette):