Refactor Palette class (tomeu)

This commit is contained in:
Aleksey Lim 2009-08-01 11:23:06 +00:00
parent 3ee78747d4
commit e0aedcd5c7
2 changed files with 357 additions and 466 deletions

View File

@ -127,11 +127,8 @@ class MouseSpeedDetector(gobject.GObject):
return True
class Palette(gtk.Window):
PRIMARY = 0
SECONDARY = 1
__gtype_name__ = 'SugarPalette'
class PaletteWindow(gtk.Window):
__gtype_name__ = 'SugarPaletteWindow'
__gsignals__ = {
'popup' : (gobject.SIGNAL_RUN_FIRST,
@ -142,6 +139,261 @@ class Palette(gtk.Window):
gobject.TYPE_NONE, ([]))
}
def __init__(self, **kwargs):
self._group_id = None
self._invoker = None
self._invoker_hids = []
self._cursor_x = 0
self._cursor_y = 0
self._alignment = None
self._up = False
self._old_alloc = None
self._popup_anim = animator.Animator(.5, 10)
self._popup_anim.add(_PopupAnimation(self))
self._secondary_anim = animator.Animator(2.0, 10)
self._secondary_anim.add(_SecondaryAnimation(self))
self._popdown_anim = animator.Animator(0.6, 10)
self._popdown_anim.add(_PopdownAnimation(self))
gobject.GObject.__init__(self, **kwargs)
self.set_decorated(False)
self.set_resizable(False)
# Just assume xthickness and ythickness are the same
self.set_border_width(self.get_style().xthickness)
accel_group = gtk.AccelGroup()
self.set_data('sugar-accel-group', accel_group)
self.add_accel_group(accel_group)
self.set_group_id("default")
self.connect('show', self.__show_cb)
self.connect('hide', self.__hide_cb)
self.connect('realize', self.__realize_cb)
self.connect('destroy', self.__destroy_cb)
self.connect('enter-notify-event', self.__enter_notify_event_cb)
self.connect('leave-notify-event', self.__leave_notify_event_cb)
self._mouse_detector = MouseSpeedDetector(self, 200, 5)
self._mouse_detector.connect('motion-slow', self._mouse_slow_cb)
def __destroy_cb(self, palette):
self.set_group_id(None)
def set_invoker(self, invoker):
for hid in self._invoker_hids[:]:
self._invoker.disconnect(hid)
self._invoker_hids.remove(hid)
self._invoker = invoker
if invoker is not None:
self._invoker_hids.append(self._invoker.connect(
'mouse-enter', self._invoker_mouse_enter_cb))
self._invoker_hids.append(self._invoker.connect(
'mouse-leave', self._invoker_mouse_leave_cb))
self._invoker_hids.append(self._invoker.connect(
'right-click', self._invoker_right_click_cb))
logging.debug(' Invoker set to %r' % self._invoker)
def get_invoker(self):
return self._invoker
invoker = gobject.property(type=object,
getter=get_invoker,
setter=set_invoker)
def __realize_cb(self, widget):
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
def _mouse_slow_cb(self, widget):
self._mouse_detector.stop()
self._palette_do_popup()
def _palette_do_popup(self):
immediate = False
if self.is_up():
self._popdown_anim.stop()
return
if self._group_id:
group = palettegroup.get_group(self._group_id)
if group and group.is_up():
immediate = True
group.popdown()
self.popup(immediate=immediate)
def is_up(self):
return self._up
def set_group_id(self, group_id):
if self._group_id:
group = palettegroup.get_group(self._group_id)
group.remove(self)
if group_id:
self._group_id = group_id
group = palettegroup.get_group(group_id)
group.add(self)
def get_group_id(self):
return self._group_id
group_id = gobject.property(type=str,
getter=get_group_id,
setter=set_group_id)
def do_size_request(self, requisition):
gtk.Window.do_size_request(self, requisition)
requisition.width = max(requisition.width, style.GRID_CELL_SIZE * 2)
def do_size_allocate(self, allocation):
gtk.Window.do_size_allocate(self, allocation)
if self._old_alloc is None or \
self._old_alloc.x != allocation.x or \
self._old_alloc.y != allocation.y or \
self._old_alloc.width != allocation.width or \
self._old_alloc.height != allocation.height:
self.queue_draw()
# We need to store old allocation because when size_allocate
# is called widget.allocation is already updated.
# gtk.Window resizing is different from normal containers:
# the X window is resized, widget.allocation is updated from
# the configure request handler and finally size_allocate is called.
self._old_alloc = allocation
def do_expose_event(self, event):
# We want to draw a border with a beautiful gap
if self._invoker is not None and self._invoker.has_rectangle_gap():
invoker = self._invoker.get_rect()
palette = self.get_rect()
gap = _calculate_gap(palette, invoker)
else:
gap = False
allocation = self.get_allocation()
wstyle = self.get_style()
if gap:
wstyle.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
gtk.SHADOW_IN, event.area, self, "palette",
0, 0, allocation.width, allocation.height,
gap[0], gap[1], gap[2])
else:
wstyle.paint_box(event.window, gtk.STATE_PRELIGHT,
gtk.SHADOW_IN, event.area, self, "palette",
0, 0, allocation.width, 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_position(self):
logging.debug(' update_position 1 %r %r' % (self._invoker, self._alignment))
invoker = self._invoker
if invoker is None or self._alignment is None:
logging.error('Cannot update the palette position.')
return
rect = self.size_request()
position = invoker.get_position_for_alignment(self._alignment, rect)
if position is None:
position = invoker.get_position(rect)
logging.debug(' update_position %r %r' % (position.x, position.y))
self.move(position.x, position.y)
def get_full_size_request(self):
return self.size_request()
def popup(self, immediate=False):
if self._invoker is not None:
full_size_request = self.get_full_size_request()
self._alignment = self._invoker.get_alignment(full_size_request)
self.update_position()
self.set_transient_for(self._invoker.get_toplevel())
self._popdown_anim.stop()
if not immediate:
self._popup_anim.start()
else:
self.show()
def popdown(self, immediate=False):
logging.debug('Palette.popdown immediate %r' % immediate)
self._popup_anim.stop()
self._mouse_detector.stop()
if not immediate:
self._popdown_anim.start()
else:
self.hide()
def _invoker_mouse_enter_cb(self, invoker):
self._mouse_detector.start()
def _invoker_mouse_leave_cb(self, invoker):
self._mouse_detector.stop()
self.popdown()
def _invoker_right_click_cb(self, invoker):
self.popup(immediate=True)
def __enter_notify_event_cb(self, widget, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR and \
event.mode == gtk.gdk.CROSSING_NORMAL:
self._popdown_anim.stop()
self._secondary_anim.start()
def __leave_notify_event_cb(self, widget, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR and \
event.mode == gtk.gdk.CROSSING_NORMAL:
self.popdown()
def __show_cb(self, widget):
self._invoker.notify_popup()
self._up = True
self.emit('popup')
def __hide_cb(self, widget):
logging.debug('__hide_cb')
self._secondary_anim.stop()
if self._invoker:
self._invoker.notify_popdown()
self._up = False
self.emit('popdown')
def get_rect(self):
win_x, win_y = self.window.get_origin()
rectangle = self.get_allocation()
x = win_x + rectangle.x
y = win_y + rectangle.y
width = rectangle.width
height = rectangle.height
return gtk.gdk.Rectangle(x, y, width, height)
class Palette(PaletteWindow):
PRIMARY = 0
SECONDARY = 1
__gtype_name__ = 'SugarPalette'
# DEPRECATED: label is passed with the primary-text property, accel_path
# is set via the invoker property, and menu_after_content is not used
def __init__(self, label=None, accel_path=None, menu_after_content=False,
@ -153,7 +405,6 @@ class Palette(gtk.Window):
self._secondary_text = None
self._icon = None
self._icon_visible = True
self._group_id = None
palette_box = gtk.VBox()
@ -200,49 +451,18 @@ class Palette(gtk.Window):
self._menu_content_separator = gtk.HSeparator()
self._popup_anim = animator.Animator(.5, 10)
self._popup_anim.add(_PopupAnimation(self))
self._secondary_anim = animator.Animator(2.0, 10)
self._secondary_anim.add(_SecondaryAnimation(self))
self._popdown_anim = animator.Animator(0.6, 10)
self._popdown_anim.add(_PopdownAnimation(self))
# we init after initializing all of our containers
gobject.GObject.__init__(self, **kwargs)
self.set_decorated(False)
self.set_resizable(False)
# Just assume xthickness and ythickness are the same
self.set_border_width(self.get_style().xthickness)
accel_group = gtk.AccelGroup()
self.set_data('sugar-accel-group', accel_group)
self.add_accel_group(accel_group)
PaletteWindow.__init__(self, **kwargs)
primary_box.set_size_request(-1, style.GRID_CELL_SIZE
- 2 * self.get_border_width())
self.connect('show', self.__show_cb)
self.connect('hide', self.__hide_cb)
self.connect('realize', self.__realize_cb)
self.connect('destroy', self.__destroy_cb)
self._alignment = None
self._old_alloc = None
self._full_request = [0, 0]
self._cursor_x = 0
self._cursor_y = 0
self._invoker = None
self._group_id = None
self._up = False
self._menu_box = None
self._content = None
self._invoker_hids = []
self.set_group_id("default")
# we set these for backward compatibility
if label is not None:
@ -263,22 +483,64 @@ class Palette(gtk.Window):
self.menu = _Menu(self)
self.menu.connect('item-inserted', self.__menu_item_inserted_cb)
self.connect('enter-notify-event', self.__enter_notify_event_cb)
self.connect('leave-notify-event', self.__leave_notify_event_cb)
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)
self._mouse_detector = MouseSpeedDetector(self, 200, 5)
self._mouse_detector.connect('motion-slow', self._mouse_slow_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.set_group_id(None)
# 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 = None
def __show_cb(self, widget):
self.menu.set_active(True)
def __hide_cb(self, widget):
logging.debug('__hide_cb')
self.menu.set_active(False)
def __notify_invoker_cb(self, palette, pspec):
invoker = self.props.invoker
if invoker is not None and hasattr(invoker.props, 'widget'):
logging.debug(('Setup widget', invoker.props.widget))
self._update_accel_widget()
self._invoker.connect('notify::widget',
self.__invoker_widget_changed_cb)
def __invoker_widget_changed_cb(self, invoker, spec):
self._update_accel_widget()
def get_full_size_request(self):
return self._full_request
def popup(self, immediate=False, state=None):
logging.debug('Palette.popup immediate %r' % immediate)
if self._invoker is not None:
self._update_full_request()
PaletteWindow.popup(self, immediate)
if state is None:
state = self.PRIMARY
self.set_state(state)
self._secondary_anim.start()
def _add_menu(self):
self._menu_box = gtk.VBox()
self._secondary_box.pack_start(self._menu_box)
@ -290,52 +552,6 @@ class Palette(gtk.Window):
self._content.set_border_width(style.DEFAULT_SPACING)
self._secondary_box.pack_start(self._content)
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 is_up(self):
return self._up
def get_rect(self):
win_x, win_y = self.window.get_origin()
rectangle = self.get_allocation()
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_invoker(self, invoker):
for hid in self._invoker_hids[:]:
self._invoker.disconnect(hid)
self._invoker_hids.remove(hid)
self._invoker = invoker
if invoker is not None:
self._invoker_hids.append(self._invoker.connect(
'mouse-enter', self._invoker_mouse_enter_cb))
self._invoker_hids.append(self._invoker.connect(
'mouse-leave', self._invoker_mouse_leave_cb))
self._invoker_hids.append(self._invoker.connect(
'right-click', self._invoker_right_click_cb))
if hasattr(invoker.props, 'widget'):
self._update_accel_widget()
logging.debug(('Setup widget', invoker.props.widget))
self._invoker_hids.append(self._invoker.connect(
'notify::widget', self._invoker_widget_changed_cb))
def get_invoker(self):
return self._invoker
invoker = gobject.property(type=object,
getter=get_invoker,
setter=set_invoker)
def _update_accel_widget(self):
assert self.props.invoker is not None
self._label.props.accel_widget = self.props.invoker.props.widget
@ -438,24 +654,8 @@ class Palette(gtk.Window):
self._update_accept_focus()
self._update_separators()
def set_group_id(self, group_id):
if self._group_id:
group = palettegroup.get_group(self._group_id)
group.remove(self)
if group_id:
self._group_id = group_id
group = palettegroup.get_group(group_id)
group.add(self)
def get_group_id(self):
return self._group_id
group_id = gobject.property(type=str,
getter=get_group_id,
setter=set_group_id)
def do_size_request(self, requisition):
gtk.Window.do_size_request(self, requisition)
PaletteWindow.do_size_request(self, requisition)
# gtk.AccelLabel request doesn't include the accelerator.
label_width = self._label_alignment.size_request()[0] + \
@ -463,54 +663,9 @@ class Palette(gtk.Window):
2 * self.get_border_width()
requisition.width = max(requisition.width,
style.GRID_CELL_SIZE * 2,
label_width,
self._full_request[0])
def do_size_allocate(self, allocation):
gtk.Window.do_size_allocate(self, allocation)
if self._old_alloc is None or \
self._old_alloc.x != allocation.x or \
self._old_alloc.y != allocation.y or \
self._old_alloc.width != allocation.width or \
self._old_alloc.height != allocation.height:
self.queue_draw()
# We need to store old allocation because when size_allocate
# is called widget.allocation is already updated.
# gtk.Window resizing is different from normal containers:
# the X window is resized, widget.allocation is updated from
# the configure request handler and finally size_allocate is called.
self._old_alloc = allocation
def do_expose_event(self, event):
# We want to draw a border with a beautiful gap
if self._invoker is not None and self._invoker.has_rectangle_gap():
invoker = self._invoker.get_rect()
palette = self.get_rect()
gap = _calculate_gap(palette, invoker)
else:
gap = False
allocation = self.get_allocation()
wstyle = self.get_style()
if gap:
wstyle.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
gtk.SHADOW_IN, event.area, self, "palette",
0, 0, allocation.width, allocation.height,
gap[0], gap[1], gap[2])
else:
wstyle.paint_box(event.window, gtk.STATE_PRELIGHT,
gtk.SHADOW_IN, event.area, self, "palette",
0, 0, allocation.width, 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_separators(self):
visible = len(self.menu.get_children()) > 0 or \
len(self._content.get_children()) > 0
@ -526,7 +681,6 @@ class Palette(gtk.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 _update_full_request(self):
@ -540,52 +694,6 @@ class Palette(gtk.Window):
self.menu.unembed()
self._secondary_box.hide()
def _update_position(self):
invoker = self._invoker
if invoker is None or self._alignment is None:
logging.error('Cannot update the palette position.')
return
rect = self.size_request()
position = invoker.get_position_for_alignment(self._alignment, rect)
if position is None:
position = invoker.get_position(rect)
self.move(position.x, position.y)
def popup(self, immediate=False, state=None):
logging.debug('Palette.popup immediate %r' % immediate)
if state is None:
state = self.PRIMARY
self.set_state(state)
if self._invoker is not None:
self._update_full_request()
self._alignment = self._invoker.get_alignment(self._full_request)
self._update_position()
self.set_transient_for(self._invoker.get_toplevel())
self._popdown_anim.stop()
if not immediate:
self._popup_anim.start()
else:
self.show()
self._secondary_anim.start()
def popdown(self, immediate=False):
logging.debug('Palette.popdown immediate %r' % immediate)
self._popup_anim.stop()
self._mouse_detector.stop()
if not immediate:
self._popdown_anim.start()
else:
self.hide()
def set_state(self, state):
if self.palette_state == state:
return
@ -596,74 +704,10 @@ class Palette(gtk.Window):
elif state == self.SECONDARY:
self.menu.embed(self._menu_box)
self._secondary_box.show()
self._update_position()
self.update_position()
self.palette_state = state
def _mouse_slow_cb(self, widget):
self._mouse_detector.stop()
self._palette_do_popup()
def _palette_do_popup(self):
immediate = False
if self.is_up():
self._popdown_anim.stop()
return
if self._group_id:
group = palettegroup.get_group(self._group_id)
if group and group.is_up():
immediate = True
group.popdown()
self.popup(immediate=immediate)
def _invoker_widget_changed_cb(self, invoker, spec):
self._update_accel_widget()
def _invoker_mouse_enter_cb(self, invoker):
self._mouse_detector.start()
def _invoker_mouse_leave_cb(self, invoker):
self._mouse_detector.stop()
self.popdown()
def _invoker_right_click_cb(self, invoker):
self.popup(immediate=True, state=self.SECONDARY)
def __enter_notify_event_cb(self, widget, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR and \
event.mode == gtk.gdk.CROSSING_NORMAL:
self._popdown_anim.stop()
self._secondary_anim.start()
def __leave_notify_event_cb(self, widget, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR and \
event.mode == gtk.gdk.CROSSING_NORMAL:
self.popdown()
def __show_cb(self, widget):
self.menu.set_active(True)
self._invoker.notify_popup()
self._up = True
self.emit('popup')
def __hide_cb(self, widget):
logging.debug('__hide_cb')
self.menu.set_active(False)
self._secondary_anim.stop()
if self._invoker:
self._invoker.notify_popdown()
self._up = False
self.emit('popdown')
class PaletteActionBar(gtk.HButtonBox):
def add_action(self, label, icon_name=None):
button = gtk.Button(label)

View File

@ -15,25 +15,21 @@
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import gtk
import gobject
import logging
import gobject
from gobject import SIGNAL_RUN_FIRST, TYPE_NONE
import gtk
from sugar.graphics import style
from sugar.graphics.palette import PaletteWindow, ToolInvoker
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.palette import MouseSpeedDetector, Invoker
from sugar.graphics.animator import Animator, Animation
from sugar.graphics import palettegroup
class ToolbarButton(ToolButton):
def __init__(self, **kwargs):
self.page_widget = None
def __init__(self, page=None, **kwargs):
ToolButton.__init__(self, **kwargs)
if self.palette is None:
self.palette = _Palette(self)
self.set_page(page)
self.connect('clicked',
lambda widget: self.set_expanded(not self.is_expanded()))
@ -51,19 +47,36 @@ class ToolbarButton(ToolButton):
return self.page_widget.child.child
def set_page(self, page):
if page is None:
self.page_widget = None
return
self.page_widget = _embody_page(_Box, page)
self.page_widget.toolbar_button = self
page.show()
if self.props.palette is None:
self.props.palette = _ToolbarPalette(invoker=ToolInvoker(self))
self.props.palette.toolbar_button = self
self._move_page_to_palette()
page = gobject.property(type=object, getter=get_page, setter=set_page)
def _move_page_to_palette(self):
if self.page_widget is None:
return
if self.toolbar_box is not None and \
self.page_widget in self.toolbar_box.get_children():
self.toolbar_box.remove(self.page_widget)
if self.props.palette is not None:
self.props.palette.add(self.page_widget)
def is_expanded(self):
return self.toolbar_box is not None and self.page_widget is not None \
and self.toolbar_box.expanded_button == self
def popdown(self):
if isinstance(self.palette, _Palette) and self.palette.is_up():
self.palette.popdown(immediate=True)
self.props.palette.popdown(immediate=True)
def set_expanded(self, expanded):
self.popdown()
@ -77,6 +90,7 @@ class ToolbarButton(ToolButton):
if not expanded:
box.remove(self.page_widget)
box.expanded_button = None
self._move_page_to_palette()
return
if box.expanded_button is not None:
@ -86,7 +100,7 @@ class ToolbarButton(ToolButton):
box.expanded_button.set_expanded(False)
if self.page_widget.parent is not None:
self.palette.remove(self.page_widget)
self.props.palette.remove(self.page_widget)
self.modify_bg(gtk.STATE_NORMAL, box.background)
_setup_page(self.page_widget, box.background, box.props.padding)
@ -94,8 +108,8 @@ class ToolbarButton(ToolButton):
box.expanded_button = self
def do_expose_event(self, event):
if not self.is_expanded() or self.palette is not None and \
self.palette.is_up():
if not self.is_expanded() or self.props.palette is not None and \
self.props.palette.is_up():
ToolButton.do_expose_event(self, event)
_paint_arrow(self, event, gtk.ARROW_DOWN)
return
@ -157,6 +171,26 @@ class ToolbarBox(gtk.VBox):
self.toolbar.parent.parent.modify_bg(state, color)
self.toolbar.modify_bg(state, color)
class _ToolbarPalette(PaletteWindow):
def __init__(self, **kwargs):
PaletteWindow.__init__(self, **kwargs)
self.toolbar_box = None
self.set_border_width(0)
def do_size_request(self, requisition):
gtk.Window.do_size_request(self, requisition)
requisition.width = max(requisition.width,
gtk.gdk.screen_width())
def popup(self, immediate=False):
button = self.toolbar_button
if button.is_expanded():
return
box = button.toolbar_box
_setup_page(button.page_widget, style.COLOR_BLACK.get_gdk_color(),
box.props.padding)
PaletteWindow.popup(self, immediate)
class _Box(gtk.EventBox):
def __init__(self):
gtk.EventBox.__init__(self)
@ -176,193 +210,6 @@ class _Box(gtk.EventBox):
alloc.width - style._FOCUS_LINE_WIDTH*2,
style._FOCUS_LINE_WIDTH)
class _Palette(gtk.Window):
def __init__(self, toolitem, **kwargs):
gobject.GObject.__init__(self, **kwargs)
self.set_decorated(False)
self.set_resizable(False)
self.set_border_width(0)
self._toolitem = toolitem
self._invoker = None
self._up = False
self._invoker_hids = []
self._focus = 0
self._popup_anim = Animator(.5, 10)
self._popup_anim.add(_PopupAnimation(self))
self._popdown_anim = Animator(0.6, 10)
self._popdown_anim.add(_PopdownAnimation(self))
accel_group = gtk.AccelGroup()
self.set_data('sugar-accel-group', accel_group)
self.add_accel_group(accel_group)
self.connect('show', self.__show_cb)
self.connect('hide', self.__hide_cb)
self.connect('realize', self.__realize_cb)
self.connect('enter-notify-event', self.__enter_notify_event_cb)
self.connect('leave-notify-event', self.__leave_notify_event_cb)
self._mouse_detector = MouseSpeedDetector(self, 200, 5)
self._mouse_detector.connect('motion-slow', self._mouse_slow_cb)
group = palettegroup.get_group('default')
group.connect('popdown', self.__group_popdown_cb)
def is_up(self):
return self._up
def get_rect(self):
win_x, win_y = self.window.get_origin()
rectangle = self.get_allocation()
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_invoker(self, invoker):
for hid in self._invoker_hids[:]:
self._invoker.disconnect(hid)
self._invoker_hids.remove(hid)
self._invoker = invoker
if invoker is not None:
self._invoker_hids.append(self._invoker.connect(
'mouse-enter', self.__invoker_mouse_enter_cb))
self._invoker_hids.append(self._invoker.connect(
'mouse-leave', self.__invoker_mouse_leave_cb))
self._invoker_hids.append(self._invoker.connect(
'right-click', self.__invoker_right_click_cb))
def get_invoker(self):
return self._invoker
invoker = gobject.property(type=object,
getter=get_invoker,
setter=set_invoker)
def do_size_request(self, requisition):
gtk.Window.do_size_request(self, requisition)
if self._toolitem.toolbar_box is not None:
requisition.width = self._toolitem.toolbar_box.allocation.width
def __realize_cb(self, widget):
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
#accept_focus = len(self._content.get_children())
#if self.window:
# self.window.set_accept_focus(accept_focus)
def popup(self, immediate=False):
self._popdown_anim.stop()
toolbar = self._toolitem.toolbar_box
page = self._toolitem.page_widget
if not self._invoker or self._toolitem.is_expanded() or not toolbar:
return
_setup_page(page, style.COLOR_BLACK.get_gdk_color(),
toolbar.props.padding)
if self.child is None:
self.add(page)
x, y = toolbar.window.get_origin()
self.move(x + toolbar.allocation.x,
y + toolbar.toolbar.allocation.height)
self.set_transient_for(self._invoker.get_toplevel())
if not immediate:
self._popup_anim.start()
else:
self.show()
def popdown(self, immediate=False):
self._popup_anim.stop()
self._mouse_detector.stop()
if not immediate:
self._popdown_anim.start()
else:
self.hide()
def _mouse_slow_cb(self, widget):
self._mouse_detector.stop()
if self.is_up():
self._popdown_anim.stop()
return
self.popup(immediate=False)
def _handle_focus(self, delta):
self._focus += delta
if self._focus not in (0, 1):
logging.error('_Palette._focus=%s not in (0, 1)' % self._focus)
if self._focus == 0:
group = palettegroup.get_group('default')
if not group.is_up():
self.popdown()
def __group_popdown_cb(self, group):
if self._focus == 0:
self.popdown(immediate=True)
def __invoker_mouse_enter_cb(self, invoker):
self._mouse_detector.start()
self._handle_focus(+1)
def __invoker_mouse_leave_cb(self, invoker):
self._mouse_detector.stop()
self._handle_focus(-1)
def __invoker_right_click_cb(self, invoker):
self.popup(immediate=True)
def __enter_notify_event_cb(self, widget, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR and \
event.mode == gtk.gdk.CROSSING_NORMAL:
self._popdown_anim.stop()
self._handle_focus(+1)
def __leave_notify_event_cb(self, widget, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR and \
event.mode == gtk.gdk.CROSSING_NORMAL:
self._handle_focus(-1)
def __show_cb(self, widget):
self._invoker.notify_popup()
self._up = True
def __hide_cb(self, widget):
if self._invoker:
self._invoker.notify_popdown()
self._up = False
class _PopupAnimation(Animation):
def __init__(self, palette):
Animation.__init__(self, 0.0, 1.0)
self._palette = palette
def next_frame(self, current):
if current == 1.0:
self._palette.show()
class _PopdownAnimation(Animation):
def __init__(self, palette):
Animation.__init__(self, 0.0, 1.0)
self._palette = palette
def next_frame(self, current):
if current == 1.0:
self._palette.hide()
def _setup_page(page, color, hpad):
vpad = style._FOCUS_LINE_WIDTH
page.child.set_padding(vpad, vpad, hpad, hpad)