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
commit 0426c0c827
15 changed files with 1236 additions and 550 deletions

74
examples/radiopalette.py Normal file
View File

@ -0,0 +1,74 @@
import gtk
from sugar.graphics.radiopalette import RadioPalette, RadioMenuButton, \
RadioToolsButton
from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics import style
window = gtk.Window()
box = gtk.VBox()
window.add(box)
toolbar = gtk.Toolbar()
box.pack_start(toolbar, False)
text_view = gtk.TextView()
box.pack_start(text_view)
def echo(button, label):
if not button.props.active:
return
text_view.props.buffer.props.text += "\n" + label
# RadioMenuButton
palette = RadioPalette()
group = RadioToolButton(
icon_name='document-open')
group.connect('clicked', lambda button: echo(button, 'document-open'))
palette.append(group, 'menu.document-open')
button = RadioToolButton(
icon_name='document-save',
group=group)
button.connect('clicked', lambda button: echo(button, 'document-save'))
palette.append(button, 'menu.document-save')
button = RadioToolButton(
icon_name='document-send',
group=group)
button.connect('clicked', lambda button: echo(button, 'document-send'))
palette.append(button, 'menu.document-send')
button = RadioMenuButton(palette=palette)
toolbar.insert(button, -1)
# RadioToolsButton
palette = RadioPalette()
group = RadioToolButton(
icon_name='document-open')
group.connect('clicked', lambda button: echo(button, 'document-open'))
palette.append(group, 'menu.document-open')
button = RadioToolButton(
icon_name='document-save',
group=group)
button.connect('clicked', lambda button: echo(button, 'document-save'))
palette.append(button, 'menu.document-save')
button = RadioToolButton(
icon_name='document-send',
group=group)
button.connect('clicked', lambda button: echo(button, 'document-send'))
palette.append(button, 'menu.document-send')
button = RadioToolsButton(palette=palette)
toolbar.insert(button, -1)
window.show_all()
gtk.main()

1
examples/sugar Symbolic link
View File

@ -0,0 +1 @@
../src/sugar/

50
examples/toolbar.py Normal file
View File

@ -0,0 +1,50 @@
import gtk
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.toolbarbox import ToolbarBox, ToolbarButton
from sugar.graphics import style
window = gtk.Window()
box = gtk.VBox()
window.add(box)
toolbar = ToolbarBox()
box.pack_start(toolbar, False)
tollbarbutton_1 = ToolbarButton(
page=gtk.Button('sub-widget #1'),
icon_name='computer-xo')
toolbar.toolbar.insert(tollbarbutton_1, -1)
tollbarbutton_2 = ToolbarButton(
page=gtk.Button('sub-widget #2'),
icon_name='button_cancel',
tooltip='with custom palette instead of sub-widget')
toolbar.toolbar.insert(tollbarbutton_2, -1)
toolbar.toolbar.insert(gtk.SeparatorToolItem(), -1)
def del_cb(widget):
toolbar.toolbar.remove(tollbarbutton_3)
del_b = gtk.Button('delete sub-widget #3')
del_b.connect('clicked', del_cb)
tollbarbutton_3 = ToolbarButton(
page=del_b,
icon_name='activity-journal')
toolbar.toolbar.insert(tollbarbutton_3, -1)
subbar = gtk.Toolbar()
subbutton = ToolButton(
icon_name='document-send',
tooltip='document-send')
subbar.insert(subbutton, -1)
subbar.show_all()
tollbarbutton_4 = ToolbarButton(
page=subbar,
icon_name='document-save')
toolbar.toolbar.insert(tollbarbutton_4, -1)
window.show_all()
gtk.main()

2
m4/.gitignore vendored
View File

@ -1 +1,3 @@
intltool.m4
libtool.m4
lt*.m4

14
src/sugar/__init__.py Normal file
View File

@ -0,0 +1,14 @@
# 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.

View File

@ -1,10 +1,11 @@
sugardir = $(pythondir)/sugar/activity
sugar_PYTHON = \
__init__.py \
activity.py \
activityfactory.py \
activityhandle.py \
activityservice.py \
bundlebuilder.py \
main.py \
namingalert.py
sugar_PYTHON = \
__init__.py \
activity.py \
activityfactory.py \
activityhandle.py \
activityservice.py \
bundlebuilder.py \
main.py \
namingalert.py \
widgets.py

View File

@ -67,16 +67,15 @@ from sugar.activity.activityservice import ActivityService
from sugar.activity.namingalert import NamingAlert
from sugar.graphics import style
from sugar.graphics.window import Window
from sugar.graphics.toolbox import Toolbox
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.toolcombobox import ToolComboBox
from sugar.graphics.alert import Alert
from sugar.graphics.icon import Icon
from sugar.graphics.xocolor import XoColor
from sugar.datastore import datastore
from sugar.session import XSMPClient
from sugar import wm
# support deprecated imports
from sugar.activity.widgets import ActivityToolbar, EditToolbar, ActivityToolbox
_ = lambda msg: gettext.dgettext('sugar-toolkit', msg)
SCOPE_PRIVATE = "private"
@ -87,226 +86,6 @@ J_DBUS_SERVICE = 'org.laptop.Journal'
J_DBUS_PATH = '/org/laptop/Journal'
J_DBUS_INTERFACE = 'org.laptop.Journal'
class ActivityToolbar(gtk.Toolbar):
"""The Activity toolbar with the Journal entry title, sharing,
Keep and Stop buttons
All activities should have this toolbar. It is easiest to add it to your
Activity by using the ActivityToolbox.
"""
def __init__(self, activity):
gtk.Toolbar.__init__(self)
self._activity = activity
self._updating_share = False
activity.connect('shared', self.__activity_shared_cb)
activity.connect('joined', self.__activity_shared_cb)
activity.connect('notify::max_participants',
self.__max_participants_changed_cb)
if activity.metadata:
self.title = gtk.Entry()
self.title.set_size_request(int(gtk.gdk.screen_width() / 3), -1)
self.title.set_text(activity.metadata['title'])
self.title.connect('changed', self.__title_changed_cb)
self._add_widget(self.title)
activity.metadata.connect('updated', self.__jobject_updated_cb)
separator = gtk.SeparatorToolItem()
separator.props.draw = False
separator.set_expand(True)
self.insert(separator, -1)
separator.show()
self.share = ToolComboBox(label_text=_('Share with:'))
self.share.combo.connect('changed', self.__share_changed_cb)
self.share.combo.append_item(SCOPE_PRIVATE, _('Private'), 'zoom-home')
self.share.combo.append_item(SCOPE_NEIGHBORHOOD, _('My Neighborhood'),
'zoom-neighborhood')
self.insert(self.share, -1)
self.share.show()
self._update_share()
self.keep = ToolButton(tooltip=_('Keep'))
client = gconf.client_get_default()
color = XoColor(client.get_string('/desktop/sugar/user/color'))
keep_icon = Icon(icon_name='document-save', xo_color=color)
self.keep.set_icon_widget(keep_icon)
keep_icon.show()
self.keep.props.accelerator = '<Ctrl>S'
self.keep.connect('clicked', self.__keep_clicked_cb)
self.insert(self.keep, -1)
self.keep.show()
self.stop = ToolButton('activity-stop', tooltip=_('Stop'))
self.stop.props.accelerator = '<Ctrl>Q'
self.stop.connect('clicked', self.__stop_clicked_cb)
self.insert(self.stop, -1)
self.stop.show()
self._update_title_sid = None
def _update_share(self):
self._updating_share = True
if self._activity.props.max_participants == 1:
self.share.hide()
if self._activity.get_shared():
self.share.set_sensitive(False)
self.share.combo.set_active(1)
else:
self.share.set_sensitive(True)
self.share.combo.set_active(0)
self._updating_share = False
def __share_changed_cb(self, combo):
if self._updating_share:
return
model = self.share.combo.get_model()
it = self.share.combo.get_active_iter()
(scope, ) = model.get(it, 0)
if scope == SCOPE_NEIGHBORHOOD:
self._activity.share()
def __keep_clicked_cb(self, button):
self._activity.copy()
def __stop_clicked_cb(self, button):
self._activity.close()
def __jobject_updated_cb(self, jobject):
self.title.set_text(jobject['title'])
def __title_changed_cb(self, entry):
if not self._update_title_sid:
self._update_title_sid = gobject.timeout_add_seconds(
1, self.__update_title_cb)
def __update_title_cb(self):
title = self.title.get_text()
self._activity.metadata['title'] = title
self._activity.metadata['title_set_by_user'] = '1'
self._activity.save()
shared_activity = self._activity.get_shared_activity()
if shared_activity:
shared_activity.props.name = title
self._update_title_sid = None
return False
def _add_widget(self, widget, expand=False):
tool_item = gtk.ToolItem()
tool_item.set_expand(expand)
tool_item.add(widget)
widget.show()
self.insert(tool_item, -1)
tool_item.show()
def __activity_shared_cb(self, activity):
self._update_share()
def __max_participants_changed_cb(self, activity, pspec):
self._update_share()
class EditToolbar(gtk.Toolbar):
"""Provides the standard edit toolbar for Activities.
Members:
undo -- the undo button
redo -- the redo button
copy -- the copy button
paste -- the paste button
separator -- A separator between undo/redo and copy/paste
This class only provides the 'edit' buttons in a standard layout,
your activity will need to either hide buttons which make no sense for your
Activity, or you need to connect the button events to your own callbacks:
## Example from Read.activity:
# Create the edit toolbar:
self._edit_toolbar = EditToolbar(self._view)
# Hide undo and redo, they're not needed
self._edit_toolbar.undo.props.visible = False
self._edit_toolbar.redo.props.visible = False
# Hide the separator too:
self._edit_toolbar.separator.props.visible = False
# As long as nothing is selected, copy needs to be insensitive:
self._edit_toolbar.copy.set_sensitive(False)
# When the user clicks the button, call _edit_toolbar_copy_cb()
self._edit_toolbar.copy.connect('clicked', self._edit_toolbar_copy_cb)
# Add the edit toolbar:
toolbox.add_toolbar(_('Edit'), self._edit_toolbar)
# And make it visible:
self._edit_toolbar.show()
"""
def __init__(self):
gtk.Toolbar.__init__(self)
self.undo = ToolButton('edit-undo')
self.undo.set_tooltip(_('Undo'))
self.insert(self.undo, -1)
self.undo.show()
self.redo = ToolButton('edit-redo')
self.redo.set_tooltip(_('Redo'))
self.insert(self.redo, -1)
self.redo.show()
self.separator = gtk.SeparatorToolItem()
self.separator.set_draw(True)
self.insert(self.separator, -1)
self.separator.show()
self.copy = ToolButton('edit-copy')
self.copy.set_tooltip(_('Copy'))
self.insert(self.copy, -1)
self.copy.show()
self.paste = ToolButton('edit-paste')
self.paste.set_tooltip(_('Paste'))
self.insert(self.paste, -1)
self.paste.show()
class ActivityToolbox(Toolbox):
"""Creates the Toolbox for the Activity
By default, the toolbox contains only the ActivityToolbar. After creating
the toolbox, you can add your activity specific toolbars, for example the
EditToolbar.
To add the ActivityToolbox to your Activity in MyActivity.__init__() do:
# Create the Toolbar with the ActivityToolbar:
toolbox = activity.ActivityToolbox(self)
... your code, inserting all other toolbars you need, like EditToolbar
# Add the toolbox to the activity frame:
self.set_toolbox(toolbox)
# And make it visible:
toolbox.show()
"""
def __init__(self, activity):
Toolbox.__init__(self)
self._activity_toolbar = ActivityToolbar(activity)
self.add_toolbar(_('Activity'), self._activity_toolbar)
self._activity_toolbar.show()
def get_activity_toolbar(self):
return self._activity_toolbar
class _ActivitySession(gobject.GObject):
__gsignals__ = {
'quit-requested': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),

View File

@ -0,0 +1,297 @@
# 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 gettext
import gconf
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.toolbarbox import ToolbarButton
from sugar.graphics.radiopalette import RadioPalette, RadioMenuButton
from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.toolbox import Toolbox
from sugar.graphics.xocolor import XoColor
from sugar.graphics.icon import Icon
from sugar.bundle.activitybundle import ActivityBundle
_ = lambda msg: gettext.dgettext('sugar-toolkit', msg)
class ActivityToolbarButton(ToolbarButton):
def __init__(self, activity, **kwargs):
toolbar = ActivityToolbar(activity)
toolbar.stop.hide()
ToolbarButton.__init__(self, page=toolbar, **kwargs)
from sugar.activity.activity import get_bundle_path
bundle = ActivityBundle(get_bundle_path())
client = gconf.client_get_default()
color = XoColor(client.get_string('/desktop/sugar/user/color'))
icon = Icon(file=bundle.get_icon(), xo_color=color)
icon.show()
self.set_icon_widget(icon)
class StopButton(ToolButton):
def __init__(self, activity, **kwargs):
ToolButton.__init__(self, 'activity-stop', **kwargs)
self.props.tooltip = _('Stop')
self.props.accelerator = '<Ctrl>Q'
self.connect('clicked', self.__stop_button_clicked_cb, activity)
def __stop_button_clicked_cb(self, button, activity):
activity.close()
class UndoButton(ToolButton):
def __init__(self, **kwargs):
ToolButton.__init__(self, 'edit-undo', **kwargs)
self.props.tooltip = _('Undo')
self.props.accelerator = '<Ctrl>Q'
class RedoButton(ToolButton):
def __init__(self, **kwargs):
ToolButton.__init__(self, 'edit-redo', **kwargs)
self.props.tooltip = _('Redo')
class CopyButton(ToolButton):
def __init__(self, **kwargs):
ToolButton.__init__(self, 'edit-copy', **kwargs)
self.props.tooltip = _('Copy')
class PasteButton(ToolButton):
def __init__(self, **kwargs):
ToolButton.__init__(self, 'edit-paste', **kwargs)
self.props.tooltip = _('Paste')
class ShareButton(RadioMenuButton):
def __init__(self, activity, **kwargs):
palette = RadioPalette()
self.private = RadioToolButton(
icon_name='zoom-home')
palette.append(self.private, _('Private'))
self.neighborhood = RadioToolButton(
icon_name='zoom-neighborhood',
group=self.private)
self._neighborhood_handle = self.neighborhood.connect(
'clicked', self.__neighborhood_clicked_cb, activity)
palette.append(self.neighborhood, _('My Neighborhood'))
activity.connect('shared', self.__update_share_cb)
activity.connect('joined', self.__update_share_cb)
RadioMenuButton.__init__(self, **kwargs)
self.props.palette = palette
def __neighborhood_clicked_cb(self, button, activity):
activity.share()
def __update_share_cb(self, activity):
self.neighborhood.handler_block(self._neighborhood_handle)
try:
if activity.get_shared():
self.private.props.sensitive = False
self.neighborhood.props.sensitive = False
self.neighborhood.props.active = True
else:
self.private.props.sensitive = True
self.neighborhood.props.sensitive = True
self.private.props.active = True
finally:
self.neighborhood.handler_unblock(self._neighborhood_handle)
class KeepButton(ToolButton):
def __init__(self, activity, **kwargs):
ToolButton.__init__(self, **kwargs)
self.props.tooltip = _('Keep')
self.props.accelerator = '<Ctrl>S'
client = gconf.client_get_default()
color = XoColor(client.get_string('/desktop/sugar/user/color'))
keep_icon = Icon(icon_name='document-save', xo_color=color)
keep_icon.show()
self.set_icon_widget(keep_icon)
self.connect('clicked', self.__keep_button_clicked_cb, activity)
def __keep_button_clicked_cb(self, button, activity):
activity.copy()
class TitleEntry(gtk.ToolItem):
def __init__(self, activity, **kwargs):
gtk.ToolItem.__init__(self)
self.set_expand(False)
self._update_title_sid = None
self.entry = gtk.Entry(**kwargs)
self.entry.set_size_request(int(gtk.gdk.screen_width() / 3), -1)
self.entry.set_text(activity.metadata['title'])
self.entry.connect('changed', self.__title_changed_cb, activity)
self.entry.show()
self.add(self.entry)
activity.metadata.connect('updated', self.__jobject_updated_cb)
def modify_bg(self, state, color):
gtk.ToolItem.modify_bg(self, state, color)
if state == gtk.STATE_NORMAL:
self.entry.modify_bg(gtk.STATE_INSENSITIVE, color)
def __jobject_updated_cb(self, jobject):
self.entry.set_text(jobject['title'])
def __title_changed_cb(self, entry, activity):
if not self._update_title_sid:
self._update_title_sid = gobject.timeout_add_seconds(
1, self.__update_title_cb, activity)
def __update_title_cb(self, activity):
title = self.entry.get_text()
activity.metadata['title'] = title
activity.metadata['title_set_by_user'] = '1'
activity.save()
shared_activity = activity.get_shared_activity()
if shared_activity is None:
shared_activity.props.name = title
self._update_title_sid = None
return False
class ActivityToolbar(gtk.Toolbar):
"""The Activity toolbar with the Journal entry title, sharing,
Keep and Stop buttons
All activities should have this toolbar. It is easiest to add it to your
Activity by using the ActivityToolbox.
"""
def __init__(self, activity):
gtk.Toolbar.__init__(self)
self._activity = activity
if activity.metadata:
title_button = TitleEntry(activity)
title_button.show()
self.insert(title_button, -1)
self.title = title_button.entry
separator = gtk.SeparatorToolItem()
separator.props.draw = False
separator.set_expand(True)
self.insert(separator, -1)
separator.show()
self.share = ShareButton(activity)
self.share.show()
self.insert(self.share, -1)
self.keep = KeepButton(activity)
self.insert(self.keep, -1)
self.keep.show()
self.stop = StopButton(activity)
self.insert(self.stop, -1)
self.stop.show()
class EditToolbar(gtk.Toolbar):
"""Provides the standard edit toolbar for Activities.
Members:
undo -- the undo button
redo -- the redo button
copy -- the copy button
paste -- the paste button
separator -- A separator between undo/redo and copy/paste
This class only provides the 'edit' buttons in a standard layout,
your activity will need to either hide buttons which make no sense for your
Activity, or you need to connect the button events to your own callbacks:
## Example from Read.activity:
# Create the edit toolbar:
self._edit_toolbar = EditToolbar(self._view)
# Hide undo and redo, they're not needed
self._edit_toolbar.undo.props.visible = False
self._edit_toolbar.redo.props.visible = False
# Hide the separator too:
self._edit_toolbar.separator.props.visible = False
# As long as nothing is selected, copy needs to be insensitive:
self._edit_toolbar.copy.set_sensitive(False)
# When the user clicks the button, call _edit_toolbar_copy_cb()
self._edit_toolbar.copy.connect('clicked', self._edit_toolbar_copy_cb)
# Add the edit toolbar:
toolbox.add_toolbar(_('Edit'), self._edit_toolbar)
# And make it visible:
self._edit_toolbar.show()
"""
def __init__(self):
gtk.Toolbar.__init__(self)
self.undo = UndoButton()
self.insert(self.undo, -1)
self.undo.show()
self.redo = RedoButton()
self.insert(self.redo, -1)
self.redo.show()
self.separator = gtk.SeparatorToolItem()
self.separator.set_draw(True)
self.insert(self.separator, -1)
self.separator.show()
self.copy = CopyButton()
self.insert(self.copy, -1)
self.copy.show()
self.paste = PasteButton()
self.insert(self.paste, -1)
self.paste.show()
class ActivityToolbox(Toolbox):
"""Creates the Toolbox for the Activity
By default, the toolbox contains only the ActivityToolbar. After creating
the toolbox, you can add your activity specific toolbars, for example the
EditToolbar.
To add the ActivityToolbox to your Activity in MyActivity.__init__() do:
# Create the Toolbar with the ActivityToolbar:
toolbox = activity.ActivityToolbox(self)
... your code, inserting all other toolbars you need, like EditToolbar
# Add the toolbox to the activity frame:
self.set_toolbox(toolbox)
# And make it visible:
toolbox.show()
"""
def __init__(self, activity):
Toolbox.__init__(self)
self._activity_toolbar = ActivityToolbar(activity)
self.add_toolbar(_('Activity'), self._activity_toolbar)
self._activity_toolbar.show()
def get_activity_toolbar(self):
return self._activity_toolbar

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

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,22 +505,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_palette_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 +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):

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

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)

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)

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)
@ -152,7 +151,6 @@ class ToolButton(gtk.ToolButton):
gtk.ToolButton.do_expose_event(self, event)
def __button_clicked_cb(self, widget):
def do_clicked(self):
if self.palette:
self.palette.popdown(True)

View File

@ -21,6 +21,8 @@ STABLE.
import gobject
import gtk
import logging
import warnings
from sugar.graphics.icon import Icon
@ -85,7 +87,7 @@ class Window(gtk.Window):
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)
def get_toolbar_box(self):
return self.__toolbar_box
self._vbox.pack_start(toolbox, False)
self._vbox.reorder_child(toolbox, 0)
def set_toolbar_box(self, toolbar_box):
if self.__toolbar_box:
self._vbox.remove(self.__toolbar_box)
self.toolbox = toolbox
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)