Add support for locking Palettes, SL #4008

This adds a property that indicates that a Palette should
behave in a locking manner. The behaviour is the same
as with the secondary Toolbars: when you hover over the invoking
widget the Palette will popdown and react to mouse movements,
leaving the invoker area or the Palette itself will popdown
the Palette again. When you click the invoking widget
the Palette will be locked. You have to unlock it again
to pop it down.

This patch makes the DescriptionButton and the Colorbutton
work.

If the DescriptionButton or the Colorbutton are placed in
the primary toolbar they will share the locked state with
the secondary toolbars. Only one can be locked at a time.

When a secondary toolbar is unlocked we do force that the
open Palettes are closed. Having a locking Palette in
a subtoolbar will also work (Activity Toolbar case or
ColorButton case in a few examples). There is no state
sharing implemented here at the moment, but so far we
do only have cases with one lockable Palette in a
subtoolbar.

This will also fix the case where we want to use the
OSK to edit the description of the activity SL #3887.

Signed-off-by: Simon Schampijer <simon@laptop.org>
Acked-by: Manuel Quiñones <manuq@laptop.org>
This commit is contained in:
Simon Schampijer 2012-11-15 18:09:00 +01:00
parent 2d9e9ec526
commit 6064fb30cc
4 changed files with 105 additions and 25 deletions

View File

@ -219,17 +219,15 @@ class TitleEntry(Gtk.ToolItem):
shared_activity.props.name = title shared_activity.props.name = title
class DescriptionItem(Gtk.ToolItem): class DescriptionItem(ToolButton):
def __init__(self, activity, **kwargs): def __init__(self, activity, **kwargs):
Gtk.ToolItem.__init__(self) ToolButton.__init__(self, 'edit-description', **kwargs)
self.set_tooltip(_('Description'))
description_button = ToolButton('edit-description') self.palette_invoker.props.toggle_palette = True
description_button.show() self.palette_invoker.props.lock_palette = True
description_button.set_tooltip(_('Description')) self.props.hide_tooltip_on_click = False
description_button.palette_invoker.props.toggle_palette = True self._palette = self.get_palette()
description_button.props.hide_tooltip_on_click = False
self._palette = description_button.get_palette()
description_box = PaletteMenuBox() description_box = PaletteMenuBox()
sw = Gtk.ScrolledWindow() sw = Gtk.ScrolledWindow()
@ -251,10 +249,31 @@ class DescriptionItem(Gtk.ToolItem):
self._palette.set_content(description_box) self._palette.set_content(description_box)
description_box.show_all() description_box.show_all()
self.add(description_button)
activity.metadata.connect('updated', self.__jobject_updated_cb) activity.metadata.connect('updated', self.__jobject_updated_cb)
def set_expanded(self, expanded):
box = self.toolbar_box
if not box:
return
if not expanded:
self.palette_invoker.notify_popdown()
return
if box.expanded_button is not None:
box.expanded_button.queue_draw()
if box.expanded_button != self:
box.expanded_button.set_expanded(False)
box.expanded_button = self
def get_toolbar_box(self):
parent = self.get_parent()
if not hasattr(parent, 'owner'):
return None
return parent.owner
toolbar_box = property(get_toolbar_box)
def _get_text_from_buffer(self): def _get_text_from_buffer(self):
buf = self._text_view.get_buffer() buf = self._text_view.get_buffer()
start_iter = buf.get_start_iter() start_iter = buf.get_start_iter()

View File

@ -112,15 +112,6 @@ class _ColorButton(Gtk.Button):
return '#%.2X%.2X%.2X' % (fg_color.red * 255, fg_color.green * 255, return '#%.2X%.2X%.2X' % (fg_color.red * 255, fg_color.green * 255,
fg_color.blue * 255) fg_color.blue * 255)
def do_clicked(self):
if self._palette:
if not self._palette.is_up():
self._palette.popup(immediate=True,
state=self._palette.SECONDARY)
else:
self._palette.popdown(immediate=True)
return True
def set_color(self, color): def set_color(self, color):
assert isinstance(color, Gdk.Color) assert isinstance(color, Gdk.Color)
@ -451,6 +442,8 @@ class ColorToolButton(Gtk.ToolItem):
color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR
self._palette_invoker.attach_tool(self) self._palette_invoker.attach_tool(self)
self._palette_invoker.props.toggle_palette = True
self._palette_invoker.props.lock_palette = True
# This widget just proxies the following properties to the colorbutton # This widget just proxies the following properties to the colorbutton
color_button.connect('notify::color', self.__notify_change) color_button.connect('notify::color', self.__notify_change)
@ -489,6 +482,29 @@ class ColorToolButton(Gtk.ToolItem):
palette_invoker = GObject.property( palette_invoker = GObject.property(
type=object, setter=set_palette_invoker, getter=get_palette_invoker) type=object, setter=set_palette_invoker, getter=get_palette_invoker)
def set_expanded(self, expanded):
box = self.toolbar_box
if not box:
return
if not expanded:
self._palette_invoker.notify_popdown()
return
if box.expanded_button is not None:
box.expanded_button.queue_draw()
if box.expanded_button != self:
box.expanded_button.set_expanded(False)
box.expanded_button = self
def get_toolbar_box(self):
parent = self.get_parent()
if not hasattr(parent, 'owner'):
return None
return parent.owner
toolbar_box = property(get_toolbar_box)
def set_color(self, color): def set_color(self, color):
self.get_child().props.color = color self.get_child().props.color = color

View File

@ -24,6 +24,7 @@ STABLE.
""" """
import logging import logging
import math
from gi.repository import Gdk from gi.repository import Gdk
from gi.repository import Gtk from gi.repository import Gtk
@ -655,25 +656,29 @@ class PaletteWindow(GObject.GObject):
self.popdown() self.popdown()
def _invoker_mouse_enter_cb(self, invoker): def _invoker_mouse_enter_cb(self, invoker):
self.on_invoker_enter() if not self._invoker.locked:
self.on_invoker_enter()
def _invoker_mouse_leave_cb(self, invoker): def _invoker_mouse_leave_cb(self, invoker):
self.on_invoker_leave() if not self._invoker.locked:
self.on_invoker_leave()
def _invoker_right_click_cb(self, invoker): def _invoker_right_click_cb(self, invoker):
self.popup(immediate=True, state=self.SECONDARY) self.popup(immediate=True, state=self.SECONDARY)
def _invoker_toggle_state_cb(self, invoker): def _invoker_toggle_state_cb(self, invoker):
if self.is_up(): if self.is_up() and self._palette_state == self.SECONDARY:
self.popdown(immediate=True) self.popdown(immediate=True)
else: else:
self.popup(immediate=True, state=self.SECONDARY) self.popup(immediate=True, state=self.SECONDARY)
def __enter_notify_cb(self, widget): def __enter_notify_cb(self, widget):
self.on_enter() if not self._invoker.locked:
self.on_enter()
def __leave_notify_cb(self, widget): def __leave_notify_cb(self, widget):
self.on_leave() if not self._invoker.locked:
self.on_leave()
def __show_cb(self, widget): def __show_cb(self, widget):
if self._invoker is not None: if self._invoker is not None:
@ -774,6 +779,8 @@ class Invoker(GObject.GObject):
self._palette = None self._palette = None
self._cache_palette = True self._cache_palette = True
self._toggle_palette = False self._toggle_palette = False
self._lock_palette = False
self.locked = False
def attach(self, parent): def attach(self, parent):
self.parent = parent self.parent = parent
@ -1012,6 +1019,18 @@ class Invoker(GObject.GObject):
button left click/touch tap. Defaults to False. button left click/touch tap. Defaults to False.
""" """
def get_lock_palette(self):
return self._lock_palette
def set_lock_palette(self, lock_palette):
self._lock_palette = lock_palette
lock_palette = GObject.property(type=object, setter=set_lock_palette,
getter=get_lock_palette)
"""Whether the invoker will lock the Palette and
ignore mouse events. Defaults to False.
"""
def __palette_popdown_cb(self, palette): def __palette_popdown_cb(self, palette):
if not self.props.cache_palette: if not self.props.cache_palette:
self.set_palette(None) self.set_palette(None)
@ -1023,11 +1042,13 @@ class WidgetInvoker(Invoker):
Invoker.__init__(self) Invoker.__init__(self)
self._widget = None self._widget = None
self._expanded = False
self._enter_hid = None self._enter_hid = None
self._leave_hid = None self._leave_hid = None
self._release_hid = None self._release_hid = None
self._click_hid = None self._click_hid = None
self._touch_hid = None self._touch_hid = None
self._draw_hid = None
self._long_pressed_recognized = False self._long_pressed_recognized = False
self._long_pressed_hid = None self._long_pressed_hid = None
self._long_pressed_controller = SugarGestures.LongPressController() self._long_pressed_controller = SugarGestures.LongPressController()
@ -1054,11 +1075,13 @@ class WidgetInvoker(Invoker):
self.__touch_event_cb) self.__touch_event_cb)
self._release_hid = self._widget.connect('button-release-event', self._release_hid = self._widget.connect('button-release-event',
self.__button_release_event_cb) self.__button_release_event_cb)
self._draw_hid = self._widget.connect_after('draw', self.__drawing_cb)
self._long_pressed_hid = self._long_pressed_controller.connect( self._long_pressed_hid = self._long_pressed_controller.connect(
'pressed', self.__long_pressed_event_cb, self._widget) 'pressed', self.__long_pressed_event_cb, self._widget)
self._long_pressed_controller.attach(self._widget, self._long_pressed_controller.attach(self._widget,
SugarGestures.EventControllerFlags.NONE) SugarGestures.EventControllerFlags.NONE)
self.attach(parent) self.attach(parent)
def detach(self): def detach(self):
@ -1066,6 +1089,7 @@ class WidgetInvoker(Invoker):
self._widget.disconnect(self._enter_hid) self._widget.disconnect(self._enter_hid)
self._widget.disconnect(self._leave_hid) self._widget.disconnect(self._leave_hid)
self._widget.disconnect(self._release_hid) self._widget.disconnect(self._release_hid)
self._widget.disconnect(self._draw_hid)
if self._click_hid: if self._click_hid:
self._widget.disconnect(self._click_hid) self._widget.disconnect(self._click_hid)
self._widget.disconnect(self._touch_hid) self._widget.disconnect(self._touch_hid)
@ -1126,6 +1150,11 @@ class WidgetInvoker(Invoker):
return False return False
def __click_event_cb(self, button): def __click_event_cb(self, button):
if self.props.lock_palette:
if not self.locked:
self.locked = True
self.parent.set_expanded(True)
if self.props.toggle_palette: if self.props.toggle_palette:
self.notify_toggle_state() self.notify_toggle_state()
@ -1151,6 +1180,7 @@ class WidgetInvoker(Invoker):
self._widget.queue_draw() self._widget.queue_draw()
def notify_popdown(self): def notify_popdown(self):
self.locked = False
Invoker.notify_popdown(self) Invoker.notify_popdown(self)
self._widget.queue_draw() self._widget.queue_draw()
@ -1158,6 +1188,20 @@ class WidgetInvoker(Invoker):
return self._widget return self._widget
widget = GObject.property(type=object, getter=_get_widget, setter=None) widget = GObject.property(type=object, getter=_get_widget, setter=None)
def __drawing_cb(self, widget, cr):
if not self.props.lock_palette:
return False
alloc = widget.get_allocation()
arrow_size = style.TOOLBAR_ARROW_SIZE / 2
y = alloc.height - arrow_size
x = (alloc.width - arrow_size) / 2
context = widget.get_style_context()
context.add_class('toolitem')
if self.locked:
Gtk.render_arrow(context, cr, 0, x, y, arrow_size)
else:
Gtk.render_arrow(context, cr, math.pi, x, y, arrow_size)
class CursorInvoker(Invoker): class CursorInvoker(Invoker):

View File

@ -89,6 +89,7 @@ class ToolbarButton(ToolButton):
def set_expanded(self, expanded): def set_expanded(self, expanded):
self.popdown() self.popdown()
palettegroup.popdown_all()
if self.page is None or self.is_expanded() == expanded: if self.page is None or self.is_expanded() == expanded:
return return