Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--sugar/graphics/palette.py119
-rw-r--r--sugar/graphics/radiotoolbutton.py18
-rw-r--r--sugar/graphics/toggletoolbutton.py18
-rw-r--r--sugar/graphics/toolbutton.py17
5 files changed, 172 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index e08efc6..8717339 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 e853202..b251ca7 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 94ff6ba..52fe61c 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 3684e9c..975e78a 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 e5d90ab..c447b3c 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)