From 0d24d0be2f5ed51301dc02fa45c27bd8a3f803f9 Mon Sep 17 00:00:00 2001
From: Simon Schampijer <simon@laptop.org>
Date: Thu, 15 Nov 2012 18:09:00 +0100
Subject: [PATCH toolkit-gtk3] 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>
---
src/sugar3/activity/widgets.py | 41 +++++++++++++++++++--------
src/sugar3/graphics/colorbutton.py | 34 +++++++++++++++++------
src/sugar3/graphics/palettewindow.py | 54 ++++++++++++++++++++++++++++++++----
src/sugar3/graphics/toolbarbox.py | 1 +
4 files changed, 105 insertions(+), 25 deletions(-)
diff --git a/src/sugar3/activity/widgets.py b/src/sugar3/activity/widgets.py
index 03b2415..b60fc66 100644
a
|
b
|
class TitleEntry(Gtk.ToolItem): |
219 | 219 | shared_activity.props.name = title |
220 | 220 | |
221 | 221 | |
222 | | class DescriptionItem(Gtk.ToolItem): |
| 222 | class DescriptionItem(ToolButton): |
223 | 223 | |
224 | 224 | def __init__(self, activity, **kwargs): |
225 | | Gtk.ToolItem.__init__(self) |
226 | | |
227 | | description_button = ToolButton('edit-description') |
228 | | description_button.show() |
229 | | description_button.set_tooltip(_('Description')) |
230 | | description_button.palette_invoker.props.toggle_palette = True |
231 | | description_button.props.hide_tooltip_on_click = False |
232 | | self._palette = description_button.get_palette() |
| 225 | ToolButton.__init__(self, 'edit-description', **kwargs) |
| 226 | self.set_tooltip(_('Description')) |
| 227 | self.palette_invoker.props.toggle_palette = True |
| 228 | self.palette_invoker.props.lock_palette = True |
| 229 | self.props.hide_tooltip_on_click = False |
| 230 | self._palette = self.get_palette() |
233 | 231 | |
234 | 232 | description_box = PaletteMenuBox() |
235 | 233 | sw = Gtk.ScrolledWindow() |
… |
… |
class DescriptionItem(Gtk.ToolItem): |
251 | 249 | self._palette.set_content(description_box) |
252 | 250 | description_box.show_all() |
253 | 251 | |
254 | | self.add(description_button) |
255 | | |
256 | 252 | activity.metadata.connect('updated', self.__jobject_updated_cb) |
257 | 253 | |
| 254 | def set_expanded(self, expanded): |
| 255 | box = self.toolbar_box |
| 256 | if not box: |
| 257 | return |
| 258 | |
| 259 | if not expanded: |
| 260 | self.palette_invoker.notify_popdown() |
| 261 | return |
| 262 | |
| 263 | if box.expanded_button is not None: |
| 264 | box.expanded_button.queue_draw() |
| 265 | if box.expanded_button != self: |
| 266 | box.expanded_button.set_expanded(False) |
| 267 | box.expanded_button = self |
| 268 | |
| 269 | def get_toolbar_box(self): |
| 270 | parent = self.get_parent() |
| 271 | if not hasattr(parent, 'owner'): |
| 272 | return None |
| 273 | return parent.owner |
| 274 | |
| 275 | toolbar_box = property(get_toolbar_box) |
| 276 | |
258 | 277 | def _get_text_from_buffer(self): |
259 | 278 | buf = self._text_view.get_buffer() |
260 | 279 | start_iter = buf.get_start_iter() |
diff --git a/src/sugar3/graphics/colorbutton.py b/src/sugar3/graphics/colorbutton.py
index 823fd61..065a55e 100644
a
|
b
|
class _ColorButton(Gtk.Button): |
112 | 112 | return '#%.2X%.2X%.2X' % (fg_color.red * 255, fg_color.green * 255, |
113 | 113 | fg_color.blue * 255) |
114 | 114 | |
115 | | def do_clicked(self): |
116 | | if self._palette: |
117 | | if not self._palette.is_up(): |
118 | | self._palette.popup(immediate=True, |
119 | | state=self._palette.SECONDARY) |
120 | | else: |
121 | | self._palette.popdown(immediate=True) |
122 | | return True |
123 | | |
124 | 115 | def set_color(self, color): |
125 | 116 | assert isinstance(color, Gdk.Color) |
126 | 117 | |
… |
… |
class ColorToolButton(Gtk.ToolItem): |
451 | 442 | color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR |
452 | 443 | |
453 | 444 | self._palette_invoker.attach_tool(self) |
| 445 | self._palette_invoker.props.toggle_palette = True |
| 446 | self._palette_invoker.props.lock_palette = True |
454 | 447 | |
455 | 448 | # This widget just proxies the following properties to the colorbutton |
456 | 449 | color_button.connect('notify::color', self.__notify_change) |
… |
… |
class ColorToolButton(Gtk.ToolItem): |
489 | 482 | palette_invoker = GObject.property( |
490 | 483 | type=object, setter=set_palette_invoker, getter=get_palette_invoker) |
491 | 484 | |
| 485 | def set_expanded(self, expanded): |
| 486 | box = self.toolbar_box |
| 487 | if not box: |
| 488 | return |
| 489 | |
| 490 | if not expanded: |
| 491 | self._palette_invoker.notify_popdown() |
| 492 | return |
| 493 | |
| 494 | if box.expanded_button is not None: |
| 495 | box.expanded_button.queue_draw() |
| 496 | if box.expanded_button != self: |
| 497 | box.expanded_button.set_expanded(False) |
| 498 | box.expanded_button = self |
| 499 | |
| 500 | def get_toolbar_box(self): |
| 501 | parent = self.get_parent() |
| 502 | if not hasattr(parent, 'owner'): |
| 503 | return None |
| 504 | return parent.owner |
| 505 | |
| 506 | toolbar_box = property(get_toolbar_box) |
| 507 | |
492 | 508 | def set_color(self, color): |
493 | 509 | self.get_child().props.color = color |
494 | 510 | |
diff --git a/src/sugar3/graphics/palettewindow.py b/src/sugar3/graphics/palettewindow.py
index 0283551..cadb4fe 100644
a
|
b
|
STABLE. |
24 | 24 | """ |
25 | 25 | |
26 | 26 | import logging |
| 27 | import math |
27 | 28 | |
28 | 29 | from gi.repository import Gdk |
29 | 30 | from gi.repository import Gtk |
… |
… |
class PaletteWindow(GObject.GObject): |
655 | 656 | self.popdown() |
656 | 657 | |
657 | 658 | def _invoker_mouse_enter_cb(self, invoker): |
658 | | self.on_invoker_enter() |
| 659 | if not self._invoker.locked: |
| 660 | self.on_invoker_enter() |
659 | 661 | |
660 | 662 | def _invoker_mouse_leave_cb(self, invoker): |
661 | | self.on_invoker_leave() |
| 663 | if not self._invoker.locked: |
| 664 | self.on_invoker_leave() |
662 | 665 | |
663 | 666 | def _invoker_right_click_cb(self, invoker): |
664 | 667 | self.popup(immediate=True, state=self.SECONDARY) |
665 | 668 | |
666 | 669 | def _invoker_toggle_state_cb(self, invoker): |
667 | | if self.is_up(): |
| 670 | if self.is_up() and self._palette_state == self.SECONDARY: |
668 | 671 | self.popdown(immediate=True) |
669 | 672 | else: |
670 | 673 | self.popup(immediate=True, state=self.SECONDARY) |
671 | 674 | |
672 | 675 | def __enter_notify_cb(self, widget): |
673 | | self.on_enter() |
| 676 | if not self._invoker.locked: |
| 677 | self.on_enter() |
674 | 678 | |
675 | 679 | def __leave_notify_cb(self, widget): |
676 | | self.on_leave() |
| 680 | if not self._invoker.locked: |
| 681 | self.on_leave() |
677 | 682 | |
678 | 683 | def __show_cb(self, widget): |
679 | 684 | if self._invoker is not None: |
… |
… |
class Invoker(GObject.GObject): |
774 | 779 | self._palette = None |
775 | 780 | self._cache_palette = True |
776 | 781 | self._toggle_palette = False |
| 782 | self._lock_palette = False |
| 783 | self.locked = False |
777 | 784 | |
778 | 785 | def attach(self, parent): |
779 | 786 | self.parent = parent |
… |
… |
class Invoker(GObject.GObject): |
1012 | 1019 | button left click/touch tap. Defaults to False. |
1013 | 1020 | """ |
1014 | 1021 | |
| 1022 | def get_lock_palette(self): |
| 1023 | return self._lock_palette |
| 1024 | |
| 1025 | def set_lock_palette(self, lock_palette): |
| 1026 | self._lock_palette = lock_palette |
| 1027 | |
| 1028 | lock_palette = GObject.property(type=object, setter=set_lock_palette, |
| 1029 | getter=get_lock_palette) |
| 1030 | """Whether the invoker will lock the Palette and |
| 1031 | ignore mouse events. Defaults to False. |
| 1032 | """ |
| 1033 | |
1015 | 1034 | def __palette_popdown_cb(self, palette): |
1016 | 1035 | if not self.props.cache_palette: |
1017 | 1036 | self.set_palette(None) |
… |
… |
class WidgetInvoker(Invoker): |
1023 | 1042 | Invoker.__init__(self) |
1024 | 1043 | |
1025 | 1044 | self._widget = None |
| 1045 | self._expanded = False |
1026 | 1046 | self._enter_hid = None |
1027 | 1047 | self._leave_hid = None |
1028 | 1048 | self._release_hid = None |
1029 | 1049 | self._click_hid = None |
1030 | 1050 | self._touch_hid = None |
| 1051 | self._draw_hid = None |
1031 | 1052 | self._long_pressed_recognized = False |
1032 | 1053 | self._long_pressed_hid = None |
1033 | 1054 | self._long_pressed_controller = SugarGestures.LongPressController() |
… |
… |
class WidgetInvoker(Invoker): |
1054 | 1075 | self.__touch_event_cb) |
1055 | 1076 | self._release_hid = self._widget.connect('button-release-event', |
1056 | 1077 | self.__button_release_event_cb) |
| 1078 | self._draw_hid = self._widget.connect_after('draw', self.__drawing_cb) |
1057 | 1079 | |
1058 | 1080 | self._long_pressed_hid = self._long_pressed_controller.connect( |
1059 | 1081 | 'pressed', self.__long_pressed_event_cb, self._widget) |
1060 | 1082 | self._long_pressed_controller.attach(self._widget, |
1061 | 1083 | SugarGestures.EventControllerFlags.NONE) |
| 1084 | |
1062 | 1085 | self.attach(parent) |
1063 | 1086 | |
1064 | 1087 | def detach(self): |
… |
… |
class WidgetInvoker(Invoker): |
1066 | 1089 | self._widget.disconnect(self._enter_hid) |
1067 | 1090 | self._widget.disconnect(self._leave_hid) |
1068 | 1091 | self._widget.disconnect(self._release_hid) |
| 1092 | self._widget.disconnect(self._draw_hid) |
1069 | 1093 | if self._click_hid: |
1070 | 1094 | self._widget.disconnect(self._click_hid) |
1071 | 1095 | self._widget.disconnect(self._touch_hid) |
… |
… |
class WidgetInvoker(Invoker): |
1126 | 1150 | return False |
1127 | 1151 | |
1128 | 1152 | def __click_event_cb(self, button): |
| 1153 | if self.props.lock_palette: |
| 1154 | if not self.locked: |
| 1155 | self.locked = True |
| 1156 | self.parent.set_expanded(True) |
| 1157 | |
1129 | 1158 | if self.props.toggle_palette: |
1130 | 1159 | self.notify_toggle_state() |
1131 | 1160 | |
… |
… |
class WidgetInvoker(Invoker): |
1151 | 1180 | self._widget.queue_draw() |
1152 | 1181 | |
1153 | 1182 | def notify_popdown(self): |
| 1183 | self.locked = False |
1154 | 1184 | Invoker.notify_popdown(self) |
1155 | 1185 | self._widget.queue_draw() |
1156 | 1186 | |
… |
… |
class WidgetInvoker(Invoker): |
1158 | 1188 | return self._widget |
1159 | 1189 | widget = GObject.property(type=object, getter=_get_widget, setter=None) |
1160 | 1190 | |
| 1191 | def __drawing_cb(self, widget, cr): |
| 1192 | if not self.props.lock_palette: |
| 1193 | return False |
| 1194 | alloc = widget.get_allocation() |
| 1195 | arrow_size = style.TOOLBAR_ARROW_SIZE / 2 |
| 1196 | y = alloc.height - arrow_size |
| 1197 | x = (alloc.width - arrow_size) / 2 |
| 1198 | context = widget.get_style_context() |
| 1199 | context.add_class('toolitem') |
| 1200 | if self.locked: |
| 1201 | Gtk.render_arrow(context, cr, 0, x, y, arrow_size) |
| 1202 | else: |
| 1203 | Gtk.render_arrow(context, cr, math.pi, x, y, arrow_size) |
| 1204 | |
1161 | 1205 | |
1162 | 1206 | class CursorInvoker(Invoker): |
1163 | 1207 | |
diff --git a/src/sugar3/graphics/toolbarbox.py b/src/sugar3/graphics/toolbarbox.py
index 1683403..8c4e644 100644
a
|
b
|
class ToolbarButton(ToolButton): |
89 | 89 | |
90 | 90 | def set_expanded(self, expanded): |
91 | 91 | self.popdown() |
| 92 | palettegroup.popdown_all() |
92 | 93 | |
93 | 94 | if self.page is None or self.is_expanded() == expanded: |
94 | 95 | return |