Merge branch 'master' of git://git.sugarlabs.org/sugar-toolkit/toolbars

Conflicts:
	src/sugar/graphics/window.py
This commit is contained in:
Tomeu Vizoso
2009-08-01 15:39:40 +02:00
15 changed files with 1236 additions and 550 deletions
+27 -25
View File
@@ -1,27 +1,29 @@
sugardir = $(pythondir)/sugar/graphics
sugar_PYTHON = \
__init__.py \
alert.py \
animator.py \
canvastextview.py \
combobox.py \
colorbutton.py \
entry.py \
icon.py \
iconentry.py \
menuitem.py \
notebook.py \
objectchooser.py \
radiotoolbutton.py \
palette.py \
palettegroup.py \
panel.py \
roundbox.py \
style.py \
toggletoolbutton.py \
toolbox.py \
toolbutton.py \
toolcombobox.py \
tray.py \
window.py \
sugar_PYTHON = \
alert.py \
animator.py \
canvastextview.py \
colorbutton.py \
combobox.py \
entry.py \
iconentry.py \
icon.py \
__init__.py \
menuitem.py \
notebook.py \
objectchooser.py \
palettegroup.py \
palette.py \
panel.py \
radiopalette.py \
radiotoolbutton.py \
roundbox.py \
style.py \
toggletoolbutton.py \
toolbarbox.py \
toolbox.py \
toolbutton.py \
toolcombobox.py \
tray.py \
window.py \
xocolor.py
+335 -269
View File
@@ -127,11 +127,11 @@ class MouseSpeedDetector(gobject.GObject):
return True
class Palette(gtk.Window):
class PaletteWindow(gtk.Window):
PRIMARY = 0
SECONDARY = 1
__gtype_name__ = 'SugarPalette'
__gtype_name__ = 'SugarPaletteWindow'
__gsignals__ = {
'popup' : (gobject.SIGNAL_RUN_FIRST,
@@ -142,18 +142,291 @@ 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._palette_state = self.PRIMARY
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 on_invoker_enter(self):
self._mouse_detector.start()
def on_invoker_leave(self):
self._mouse_detector.stop()
self.popdown()
def on_enter(self, event):
self._popdown_anim.stop()
self._secondary_anim.start()
def on_leave(self, event):
self.popdown()
def _invoker_mouse_enter_cb(self, invoker):
self.on_invoker_enter()
def _invoker_mouse_leave_cb(self, invoker):
self.on_invoker_leave()
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.on_enter(event)
def __leave_notify_event_cb(self, widget, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR and \
event.mode == gtk.gdk.CROSSING_NORMAL:
self.on_leave(event)
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)
def get_palette_state(self):
return self._palette_state
def _set_palette_state(self, state):
self._palette_state = state
def set_palette_state(self, state):
self._set_palette_state(state)
palette_state = property(get_palette_state)
class Palette(PaletteWindow):
__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,
text_maxlen=60, **kwargs):
self.palette_state = self.PRIMARY
self._primary_text = None
self._secondary_text = None
self._icon = None
self._icon_visible = True
self._group_id = None
palette_box = gtk.VBox()
@@ -200,49 +473,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,21 +505,63 @@ 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_palette_state(state)
self._secondary_anim.start()
def _add_menu(self):
self._menu_box = gtk.VBox()
@@ -290,52 +574,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 +676,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 +685,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,68 +703,21 @@ 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):
if self.palette_state == self.PRIMARY:
if self._palette_state == self.PRIMARY:
self.menu.embed(self._menu_box)
self._secondary_box.show()
self._full_request = self.size_request()
if self.palette_state == self.PRIMARY:
if self._palette_state == self.PRIMARY:
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:
def _set_palette_state(self, state):
if self._palette_state == state:
return
if state == self.PRIMARY:
@@ -596,73 +726,9 @@ class Palette(gtk.Window):
elif state == self.SECONDARY:
self.menu.embed(self._menu_box)
self._secondary_box.show()
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')
self.update_position()
self._palette_state = state
class PaletteActionBar(gtk.HButtonBox):
def add_action(self, label, icon_name=None):
@@ -728,7 +794,7 @@ class _SecondaryAnimation(animator.Animation):
def next_frame(self, current):
if current == 1.0:
self._palette.set_state(Palette.SECONDARY)
self._palette.set_palette_state(Palette.SECONDARY)
class _PopdownAnimation(animator.Animation):
def __init__(self, palette):
+98
View File
@@ -0,0 +1,98 @@
# Copyright (C) 2009, Aleksey Lim
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import gtk
from sugar.graphics import style
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.palette import Palette
class RadioMenuButton(ToolButton):
def __init__(self, **kwargs):
ToolButton.__init__(self, **kwargs)
self.selected_button = None
if self.props.palette:
self.__palette_cb(None, None)
self.connect('notify::palette', self.__palette_cb)
def __palette_cb(self, widget, pspec):
if not isinstance(self.props.palette, RadioPalette):
return
self.props.palette.update_button()
def do_clicked(self):
if self.palette is None:
return
if self.palette.is_up() and \
self.palette.palette_state == Palette.SECONDARY:
self.palette.popdown(immediate=True)
else:
self.palette.popup(immediate=True, state=Palette.SECONDARY)
class RadioToolsButton(RadioMenuButton):
def __init__(self, **kwargs):
RadioMenuButton.__init__(self, **kwargs)
def do_clicked(self):
if not self.selected_button:
return
self.selected_button.emit('clicked')
class RadioPalette(Palette):
def __init__(self, **kwargs):
Palette.__init__(self, **kwargs)
self.button_box = gtk.HBox()
self.button_box.show()
self.set_content(self.button_box)
def append(self, button, label):
children = self.button_box.get_children()
if button.palette is not None:
raise RuntimeError("Palette's button should not have sub-palettes")
button.show()
button.connect('clicked', self.__clicked_cb)
self.button_box.pack_start(button, fill=False)
button.palette_label = label
if not children:
self.__clicked_cb(button)
def update_button(self):
for i in self.button_box.get_children():
self.__clicked_cb(i)
def __clicked_cb(self, button):
if not button.get_active():
return
self.set_primary_text(button.palette_label)
self.popdown(immediate=True)
if self.invoker is not None:
parent = self.invoker.parent
else:
parent = None
if not isinstance(parent, RadioMenuButton):
return
parent.set_icon(button.props.icon_name)
parent.selected_button = button
+5 -3
View File
@@ -28,7 +28,7 @@ import logging
import gtk
import pango
_FOCUS_LINE_WIDTH = 2
FOCUS_LINE_WIDTH = 2
_TAB_CURVATURE = 1
def _compute_zoom_factor():
@@ -115,8 +115,8 @@ FONT_BOLD_H = zoom(24)
TOOLBOX_SEPARATOR_HEIGHT = zoom(9)
TOOLBOX_HORIZONTAL_PADDING = zoom(75)
TOOLBOX_TAB_VBORDER = int((zoom(36) - FONT_NORMAL_H - _FOCUS_LINE_WIDTH) / 2)
TOOLBOX_TAB_HBORDER = zoom(15) - _FOCUS_LINE_WIDTH - _TAB_CURVATURE
TOOLBOX_TAB_VBORDER = int((zoom(36) - FONT_NORMAL_H - FOCUS_LINE_WIDTH) / 2)
TOOLBOX_TAB_HBORDER = zoom(15) - FOCUS_LINE_WIDTH - _TAB_CURVATURE
TOOLBOX_TAB_LABEL_WIDTH = zoom(150 - 15 * 2)
COLOR_BLACK = Color('#000000')
@@ -131,3 +131,5 @@ COLOR_INACTIVE_STROKE = Color('#757575')
COLOR_TEXT_FIELD_GREY = Color('#E5E5E5')
PALETTE_CURSOR_DISTANCE = zoom(10)
TOOLBAR_ARROW_SIZE = zoom(24)
+284
View File
@@ -0,0 +1,284 @@
# Copyright (C) 2009, Aleksey Lim
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import gtk
import gobject
import logging
from sugar.graphics import style
from sugar.graphics.palette import PaletteWindow, ToolInvoker
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics import palettegroup
class ToolbarButton(ToolButton):
def __init__(self, page=None, **kwargs):
ToolButton.__init__(self, **kwargs)
self.set_page(page)
self.connect('clicked',
lambda widget: self.set_expanded(not self.is_expanded()))
def get_toolbar_box(self):
if not hasattr(self.parent, 'owner'):
return None
return self.parent.owner
toolbar_box = property(get_toolbar_box)
def get_page(self):
if self.page_widget is None:
return None
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 isinstance(self.props.palette, _ToolbarPalette):
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):
self.props.palette.popdown(immediate=True)
def set_expanded(self, expanded):
self.popdown()
box = self.toolbar_box
if box is None or self.page_widget is None or \
self.is_expanded() == expanded:
return
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:
# need to redraw it to erase arrow
expanded_toolitem = box.expanded_button.page_widget.toolbar_button
if expanded_toolitem.window is not None:
expanded_toolitem.window.invalidate_rect(None, True)
box.expanded_button.set_expanded(False)
if self.page_widget.parent is not None:
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)
box.pack_start(self.page_widget)
box.expanded_button = self
def do_expose_event(self, event):
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
alloc = self.allocation
self.get_style().paint_box(event.window,
gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self,
'palette-invoker', alloc.x, 0,
alloc.width, alloc.height + style.FOCUS_LINE_WIDTH)
if self.child.state != gtk.STATE_PRELIGHT:
self.get_style().paint_box(event.window,
gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None,
alloc.x + style.FOCUS_LINE_WIDTH, style.FOCUS_LINE_WIDTH,
alloc.width - style.FOCUS_LINE_WIDTH * 2, alloc.height)
gtk.ToolButton.do_expose_event(self, event)
_paint_arrow(self, event, gtk.ARROW_UP)
class ToolbarBox(gtk.VBox):
def __init__(self, padding=style.TOOLBOX_HORIZONTAL_PADDING):
gtk.VBox.__init__(self)
self.expanded_button = None
self.background = None
self._toolbar = gtk.Toolbar()
self._toolbar.owner = self
self._toolbar.connect('remove', self.__remove_cb)
top_widget = _embody_page(gtk.EventBox, self._toolbar)
self.pack_start(top_widget)
self.props.padding = padding
self.modify_bg(gtk.STATE_NORMAL,
style.COLOR_TOOLBAR_GREY.get_gdk_color())
def __remove_cb(self, sender, button):
if not isinstance(button, ToolbarButton):
return
button.popdown()
if self.expanded_button == button:
self.remove(button.page_widget)
self.expanded_button = None
def get_toolbar(self):
return self._toolbar
toolbar = property(get_toolbar)
def get_padding(self):
return self.toolbar.parent.props.left_padding
def set_padding(self, pad):
self.toolbar.parent.set_padding(0, 0, pad, pad)
padding = gobject.property(type=object,
getter=get_padding, setter=set_padding)
def modify_bg(self, state, color):
if state == gtk.STATE_NORMAL:
self.background = color
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)
self._focus = 0
group = palettegroup.get_group('default')
group.connect('popdown', self.__group_popdown_cb)
self.set_group_id('toolbar_box')
def on_invoker_enter(self):
PaletteWindow.on_invoker_enter(self)
self._handle_focus(+1)
def on_invoker_leave(self):
PaletteWindow.on_invoker_leave(self)
self._handle_focus(-1)
def on_enter(self, event):
PaletteWindow.on_enter(self, event)
self._handle_focus(+1)
def on_leave(self, event):
PaletteWindow.on_enter(self, event)
self._handle_focus(-1)
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 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)
def __group_popdown_cb(self, group):
if self._focus == 0:
self.popdown(immediate=True)
class _Box(gtk.EventBox):
def __init__(self):
gtk.EventBox.__init__(self)
self.connect('expose-event', self.do_expose_event)
self.set_app_paintable(True)
def do_expose_event(self, widget, event):
alloc = self.toolbar_button.allocation
self.get_style().paint_box(event.window,
gtk.STATE_NORMAL, gtk.SHADOW_IN, event.area, self,
'palette-invoker', -style.FOCUS_LINE_WIDTH, 0,
self.allocation.width + style.FOCUS_LINE_WIDTH * 2,
self.allocation.height + style.FOCUS_LINE_WIDTH)
self.get_style().paint_box(event.window,
gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, self, None,
alloc.x + style.FOCUS_LINE_WIDTH, 0,
alloc.width - style.FOCUS_LINE_WIDTH * 2,
style.FOCUS_LINE_WIDTH)
def _setup_page(page_widget, color, hpad):
vpad = style.FOCUS_LINE_WIDTH
page_widget.child.set_padding(vpad, vpad, hpad, hpad)
page = page_widget.child.child
page.modify_bg(gtk.STATE_NORMAL, color)
if isinstance(page, gtk.Container):
for i in page.get_children():
i.modify_bg(gtk.STATE_NORMAL, color)
page_widget.modify_bg(gtk.STATE_NORMAL, color)
page_widget.modify_bg(gtk.STATE_PRELIGHT, color)
def _embody_page(box_class, widget):
widget.show()
alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
alignment.add(widget)
alignment.show()
box = box_class()
box.modify_bg(gtk.STATE_ACTIVE, style.COLOR_BUTTON_GREY.get_gdk_color())
box.add(alignment)
box.show()
return box
def _paint_arrow(widget, event, arrow_type):
alloc = widget.allocation
x = alloc.x + alloc.width / 2 - style.TOOLBAR_ARROW_SIZE / 2
y = alloc.y + alloc.height - int(style.TOOLBAR_ARROW_SIZE * .85)
widget.get_style().paint_arrow(event.window,
gtk.STATE_NORMAL, gtk.SHADOW_NONE, event.area, widget,
None, arrow_type, True,
x, y, style.TOOLBAR_ARROW_SIZE, style.TOOLBAR_ARROW_SIZE)
+2 -4
View File
@@ -68,7 +68,6 @@ class ToolButton(gtk.ToolButton):
if icon_name:
self.set_icon(icon_name)
self.connect('clicked', self.__button_clicked_cb)
self.get_child().connect('can-activate-accel',
self.__button_can_activate_accel_cb)
@@ -151,8 +150,7 @@ class ToolButton(gtk.ToolButton):
allocation.width, allocation.height)
gtk.ToolButton.do_expose_event(self, event)
def __button_clicked_cb(self, widget):
def do_clicked(self):
if self.palette:
self.palette.popdown(True)
+34 -16
View File
@@ -21,6 +21,8 @@ STABLE.
import gobject
import gtk
import logging
import warnings
from sugar.graphics.icon import Icon
@@ -84,8 +86,8 @@ class Window(gtk.Window):
self.connect('realize', self.__window_realize_cb)
self.connect('window-state-event', self.__window_state_event_cb)
self.connect('key-press-event', self.__key_press_cb)
self.toolbox = None
self.__toolbar_box = None
self._alerts = []
self._canvas = None
self.tray = None
@@ -125,14 +127,19 @@ class Window(gtk.Window):
canvas = property(get_canvas, set_canvas)
def set_toolbox(self, toolbox):
if self.toolbox:
self._vbox.remove(self.toolbox)
self._vbox.pack_start(toolbox, False)
self._vbox.reorder_child(toolbox, 0)
self.toolbox = toolbox
def get_toolbar_box(self):
return self.__toolbar_box
def set_toolbar_box(self, toolbar_box):
if self.__toolbar_box:
self._vbox.remove(self.__toolbar_box)
self._vbox.pack_start(toolbar_box, False)
self._vbox.reorder_child(toolbar_box, 0)
self.__toolbar_box = toolbar_box
toolbar_box = property(get_toolbar_box, set_toolbar_box)
def set_tray(self, tray, position):
if self.tray:
@@ -152,7 +159,7 @@ class Window(gtk.Window):
self._alerts.append(alert)
if len(self._alerts) == 1:
self._vbox.pack_start(alert, False)
if self.toolbox is not None:
if self.__toolbar_box is not None:
self._vbox.reorder_child(alert, 1)
else:
self._vbox.reorder_child(alert, 0)
@@ -165,7 +172,7 @@ class Window(gtk.Window):
self._vbox.remove(alert)
if len(self._alerts) >= 1:
self._vbox.pack_start(self._alerts[0], False)
if self.toolbox is not None:
if self.__toolbar_box is not None:
self._vbox.reorder_child(self._alerts[0], 1)
else:
self._vbox.reorder_child(self._alert[0], 0)
@@ -180,8 +187,8 @@ class Window(gtk.Window):
return False
if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
if self.toolbox is not None:
self.toolbox.hide()
if self.__toolbar_box is not None:
self.__toolbar_box.hide()
if self.tray is not None:
self.tray.hide()
@@ -199,8 +206,8 @@ class Window(gtk.Window):
self.__unfullscreen_button_timeout_cb)
else:
if self.toolbox is not None:
self.toolbox.show()
if self.__toolbar_box is not None:
self.__toolbar_box.show()
if self.tray is not None:
self.tray.show()
@@ -258,3 +265,14 @@ class Window(gtk.Window):
setter=set_enable_fullscreen_mode,
getter=get_enable_fullscreen_mode)
# DEPRECATED
def set_toolbox(self, toolbar_box):
warnings.warn('use toolbar_box instead of toolbox', DeprecationWarning)
self.set_toolbar_box(toolbar_box)
def get_toolbox(self):
warnings.warn('use toolbar_box instead of toolbox', DeprecationWarning)
return self.__toolbar_box
toolbox = property(get_toolbox, set_toolbox)