From 6573e8c5e6ed9ec8c9e7af10c6ad34acb10bcd67 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 10 Aug 2007 18:29:28 +0200 Subject: [PATCH] Draw an invoker rectangle that looks connected to the main palette. There is one catch though, the menu placement inside the palette seems broken. (Probably was already broken before.) Not sure what is going on there. --- NEWS | 1 + sugar/graphics/palette.py | 119 ++++++++++++++++++++++++++++- sugar/graphics/radiotoolbutton.py | 18 +++++ sugar/graphics/toggletoolbutton.py | 18 +++++ sugar/graphics/toolbutton.py | 17 +++++ 5 files changed, 172 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index e08efc6b..87173398 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,4 @@ +* Draw an invoker that is connected with the palette for toolbuttons. (benzea) * Fix traceback when reading in saved WPA2 network configs (dcbw) * #2475 Retrieve correctly the file path for files in removable devices. (tomeu) * #2119 If config is missing start intro. (marco) diff --git a/sugar/graphics/palette.py b/sugar/graphics/palette.py index e8532024..b251ca77 100644 --- a/sugar/graphics/palette.py +++ b/sugar/graphics/palette.py @@ -36,6 +36,39 @@ _RIGHT_TOP = 5 _TOP_LEFT = 6 _TOP_RIGHT = 7 + +# Helper function to find the gap position and size of widget a +def _calculate_gap(a, b): + # Test for each side if the palette and invoker are + # adjacent to each other. + gap = True + + if a.y + a.height == b.y: + gap_side = gtk.POS_BOTTOM + elif a.x + a.width == b.x: + gap_side = gtk.POS_RIGHT + elif a.x == b.x + b.width: + gap_side = gtk.POS_LEFT + elif a.y == b.y + b.height: + gap_side = gtk.POS_TOP + else: + gap = False + + if gap: + if gap_side == gtk.POS_BOTTOM or gap_side == gtk.POS_TOP: + gap_start = min(a.width, max(0, b.x - b.x)) + gap_size = max(0, min(a.width, + (b.x + b.width) - a.x) - gap_start) + elif gap_side == gtk.POS_RIGHT or gap_side == gtk.POS_LEFT: + gap_start = min(a.height, max(0, b.y - a.y)) + gap_size = max(0, min(a.height, + (b.y + b.height) - a.y) - gap_start) + + if gap and gap_size > 0: + return (gap_side, gap_start, gap_size) + else: + return False + class Palette(gtk.Window): DEFAULT = 0 AT_CURSOR = 1 @@ -54,7 +87,9 @@ class Palette(gtk.Window): 'invoker' : (object, None, None, gobject.PARAM_READWRITE), 'position' : (gobject.TYPE_INT, None, None, 0, 6, - 0, gobject.PARAM_READWRITE) + 0, gobject.PARAM_READWRITE), + 'draw-gap' : (bool, None, None, False, + gobject.PARAM_READWRITE) } __gsignals__ = { @@ -79,6 +114,7 @@ class Palette(gtk.Window): self._group_id = None self._up = False self._position = self.DEFAULT + self._draw_gap = False self._palette_popup_sid = None self._popup_anim = animator.Animator(0.3, 10) @@ -131,6 +167,17 @@ class Palette(gtk.Window): 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_primary_text(self, label, accel_path=None): self._label.set_text(label) self._label.show() @@ -160,6 +207,9 @@ class Palette(gtk.Window): self._invoker.connect('mouse-leave', self._invoker_mouse_leave_cb) elif pspec.name == 'position': self._position = value + elif pspec.name == 'draw-gap': + self._draw_gap = value + self.queue_draw() else: raise AssertionError @@ -168,9 +218,39 @@ class Palette(gtk.Window): return self._invoker elif pspec.name == 'position': return self._position + elif pspec.name == 'draw-gap': + return self._draw_gap else: raise AssertionError + def do_expose_event(self, event): + # We want to draw a border with a beautiful gap + if self._draw_gap: + invoker = self._invoker.get_rect() + palette = self.get_rect() + + gap = _calculate_gap(palette, invoker) + else: + gap = False + + if gap: + self.style.paint_box_gap(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self, "palette", + 0, 0, + self.allocation.width, + self.allocation.height, + gap[0], gap[1], gap[2]) + else: + self.style.paint_box(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self, "palette", + 0, 0, + self.allocation.width, + self.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_separator(self): visible = len(self.menu.get_children()) > 0 or \ len(self._content.get_children()) > 0 @@ -396,6 +476,12 @@ class _Menu(_sugaruiext.Menu): _sugaruiext.Menu.do_insert(self, item, position) self._palette._update_separator() + def do_expose_event(self, event): + # Ignore the Menu expose, just do the MenuShell expose to prevent any + # border from being drawn here. A border is drawn by the palette object + # around everything. + gtk.MenuShell.do_expose_event(self, event) + def do_deactivate(self): self._palette._hide() @@ -467,6 +553,37 @@ class WidgetInvoker(Invoker): return gtk.gdk.Rectangle(x, y, width, height) + def draw_invoker_rect(self, event, palette): + style = self._widget.style + if palette.is_up(): + gap = _calculate_gap(self.get_rect(), palette.get_rect()) + + if gap: + style.paint_box_gap(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self._widget, + "palette-invoker", + self._widget.allocation.x, + self._widget.allocation.y, + self._widget.allocation.width, + self._widget.allocation.height, + gap[0], gap[1], gap[2]) + else: + style.paint_box(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self._widget, + "palette-invoker", + self._widget.allocation.x, + self._widget.allocation.y, + self._widget.allocation.width, + self._widget.allocation.height) + else: + style.paint_box(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_NONE, event.area, self._widget, + "palette-invoker", + self._widget.allocation.x, + self._widget.allocation.y, + self._widget.allocation.width, + self._widget.allocation.height) + def _enter_notify_event_cb(self, widget, event): self.emit('mouse-enter') diff --git a/sugar/graphics/radiotoolbutton.py b/sugar/graphics/radiotoolbutton.py index 94ff6ba8..52fe61c7 100644 --- a/sugar/graphics/radiotoolbutton.py +++ b/sugar/graphics/radiotoolbutton.py @@ -22,6 +22,8 @@ from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, WidgetInvoker class RadioToolButton(gtk.RadioToolButton): + __gtype_name__ = "SugarRadioToolButton" + def __init__(self, named_icon=None, group=None): gtk.RadioToolButton.__init__(self, group=group) self._palette = None @@ -38,9 +40,25 @@ class RadioToolButton(gtk.RadioToolButton): def set_palette(self, palette): self._palette = palette self._palette.props.invoker = WidgetInvoker(self.child) + self._palette.props.draw_gap = True + + self._palette.connect("popup", self._palette_changed) + self._palette.connect("popdown", self._palette_changed) def set_tooltip(self, text): self._palette = Palette(text) self._palette.props.invoker = WidgetInvoker(self.child) + + def do_expose_event(self, event): + if self._palette: + if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT: + invoker = self._palette.props.invoker + invoker.draw_invoker_rect(event, self._palette) + + gtk.RadioToolButton.do_expose_event(self, event) + + def _palette_changed(self, palette): + # Force a redraw to update the invoker rectangle + self.queue_draw() palette = property(get_palette, set_palette) diff --git a/sugar/graphics/toggletoolbutton.py b/sugar/graphics/toggletoolbutton.py index 3684e9ca..975e78a4 100644 --- a/sugar/graphics/toggletoolbutton.py +++ b/sugar/graphics/toggletoolbutton.py @@ -21,6 +21,8 @@ from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, WidgetInvoker class ToggleToolButton(gtk.ToggleToolButton): + __gtype_name__ = "SugarToggleToolButton" + def __init__(self, named_icon=None): gtk.ToggleToolButton.__init__(self) self._palette = None @@ -37,9 +39,25 @@ class ToggleToolButton(gtk.ToggleToolButton): def set_palette(self, palette): self._palette = palette self._palette.props.invoker = WidgetInvoker(self.child) + self._palette.props.draw_gap = True + + self._palette.connect("popup", self._palette_changed) + self._palette.connect("popdown", self._palette_changed) def set_tooltip(self, text): self._palette = Palette(text) self._palette.props.invoker = WidgetInvoker(self.child) + def do_expose_event(self, event): + if self._palette: + if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT: + invoker = self._palette.props.invoker + invoker.draw_invoker_rect(event, self._palette) + + gtk.ToggleToolButton.do_expose_event(self, event) + + def _palette_changed(self, palette): + # Force a redraw to update the invoker rectangle + self.queue_draw() + palette = property(get_palette, set_palette) diff --git a/sugar/graphics/toolbutton.py b/sugar/graphics/toolbutton.py index e5d90ab8..c447b3c6 100644 --- a/sugar/graphics/toolbutton.py +++ b/sugar/graphics/toolbutton.py @@ -23,6 +23,7 @@ from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, WidgetInvoker class ToolButton(gtk.ToolButton): + __gtype_name__ = "SugarToolButton" def __init__(self, icon_name=None): gtk.ToolButton.__init__(self) @@ -41,12 +42,28 @@ class ToolButton(gtk.ToolButton): def set_palette(self, palette): self._palette = palette self._palette.props.invoker = WidgetInvoker(self.child) + self._palette.props.draw_gap = True + + self._palette.connect("popup", self._palette_changed) + self._palette.connect("popdown", self._palette_changed) def set_tooltip(self, text): self.set_palette(Palette(text)) + def do_expose_event(self, event): + if self._palette: + if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT: + invoker = self._palette.props.invoker + invoker.draw_invoker_rect(event, self._palette) + + gtk.ToolButton.do_expose_event(self, event) + def _button_clicked_cb(self, widget): if self._palette: self._palette.popdown(True) + def _palette_changed(self, palette): + # Force a redraw to update the invoker rectangle + self.queue_draw() + palette = property(get_palette, set_palette)