Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/widgets.py
diff options
context:
space:
mode:
authorGonzalo Odiard <godiard@sugarlabs.org>2011-04-08 20:32:45 (GMT)
committer Gonzalo Odiard <godiard@sugarlabs.org>2011-04-08 20:32:45 (GMT)
commit1cbc7b2a88db47101cdd22aa32519946c877997c (patch)
treed9929b73d2b532189ff5ca2856f45a7c442b56e7 /widgets.py
parentb7f251ae0d4250ffe7571733c3b2e0578dc4c422 (diff)
Implement a custom widget to show in the toolbar the brush shape, color and size
Diffstat (limited to 'widgets.py')
-rw-r--r--widgets.py307
1 files changed, 307 insertions, 0 deletions
diff --git a/widgets.py b/widgets.py
new file mode 100644
index 0000000..832649b
--- /dev/null
+++ b/widgets.py
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+
+from gettext import gettext as _
+
+import gtk
+import gobject
+
+from sugar.graphics import style
+from sugar.graphics.palette import ToolInvoker
+from sugar.graphics.colorbutton import _ColorButton
+
+
+class BrushButton(_ColorButton):
+ """This is a ColorButton but show the color, the size and the shape
+ of the brush.
+ Instead of a color selector dialog it will pop up a Sugar palette.
+ As a preview an DrawingArea is used, to use the same methods to
+ draw than in the main window.
+ """
+
+ __gtype_name__ = 'BrushButton'
+
+ def __init__(self, **kwargs):
+ self._title = _('Choose brush properties')
+ self._color = gtk.gdk.Color(0, 0, 0)
+ self._has_palette = True
+ self._has_invoker = True
+ self._palette = None
+ self._accept_drag = True
+ self._brush_size = 2
+ self._brush_shape = 'circle'
+ self._preview = gtk.DrawingArea()
+ self._preview.set_size_request(style.STANDARD_ICON_SIZE,
+ style.STANDARD_ICON_SIZE)
+
+ gobject.GObject.__init__(self, **kwargs)
+ self._preview.set_events(gtk.gdk.BUTTON_PRESS_MASK)
+
+ self._preview.connect('button_press_event', self.__mouse_down_cb)
+ self._preview.connect("expose_event", self.expose)
+ self.set_image(self._preview)
+
+ self._gc = None
+
+ if self._has_palette and self._has_invoker:
+ self._invoker = WidgetInvoker(self)
+ # FIXME: This is a hack.
+ self._invoker.has_rectangle_gap = lambda: False
+ self._invoker.palette = self._palette
+
+ def _setup(self):
+ if self.get_window() is not None:
+ self._preview.fill_color = ''
+ self._preview.show()
+ self.pixmap = gtk.gdk.Pixmap(self.get_window(),
+ style.STANDARD_ICON_SIZE,
+ style.STANDARD_ICON_SIZE, 24)
+ self._gc = self.get_window().new_gc()
+ self.show_all()
+
+ def get_brush_size(self):
+ return self._brush_size
+
+ def set_brush_size(self, brush_size):
+ self._brush_size = brush_size
+ self._preview.queue_draw()
+
+ brush_size = gobject.property(type=int, getter=get_brush_size,
+ setter=set_brush_size)
+
+ def get_brush_shape(self):
+ return self._brush_shape
+
+ def set_brush_shape(self, brush_shape):
+ self._brush_shape = brush_shape
+ self._preview.queue_draw()
+
+ brush_shape = gobject.property(type=str, getter=get_brush_shape,
+ setter=set_brush_shape)
+
+ def set_color(self, color):
+ # receive gtk.gdk.Color
+ self._color = color
+ self._preview.queue_draw()
+
+ def expose(self, widget, event):
+ if self._gc is None:
+ self._setup()
+
+ if self.get_window() is not None:
+ center = style.STANDARD_ICON_SIZE / 2
+ self.pixmap.draw_rectangle(self._preview.get_style().white_gc,
+ True, 0, 0, style.STANDARD_ICON_SIZE, style.STANDARD_ICON_SIZE)
+ self._gc.set_foreground(self._color)
+
+ if(self._brush_shape == 'circle'):
+ self.pixmap.draw_arc(self._gc, True,
+ center - self._brush_size / 2,
+ center - self._brush_size / 2,
+ self._brush_size, self._brush_size, 0, 360 * 64)
+ if(self._brush_shape == 'square'):
+ self.pixmap.draw_rectangle(self._gc, True,
+ center - self._brush_size / 2,
+ center - self._brush_size / 2,
+ self._brush_size, self._brush_size)
+
+ area = event.area
+ widget.window.draw_drawable(self._gc, self.pixmap,
+ area[0], area[1], area[0], area[1], area[2], area[3])
+ return False
+
+ def do_style_set(self, previous_style):
+ pass
+
+ def set_icon_name(self, icon_name):
+ pass
+
+ def get_icon_name(self):
+ pass
+
+ def set_icon_size(self, icon_size):
+ pass
+
+ def get_icon_size(self):
+ pass
+
+ def __mouse_down_cb(self, event):
+ 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
+
+
+class ButtonStrokeColor(gtk.ToolItem):
+ """Class to manage the Stroke Color of a Button"""
+
+ __gtype_name__ = 'BrushColorToolButton'
+ __gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ tuple())}
+
+ def __init__(self, activity, **kwargs):
+ self._activity = activity
+ self.properties = self._activity.area.tool
+ self._accelerator = None
+ self._tooltip = None
+ self._palette_invoker = ToolInvoker()
+ self._palette = None
+
+ gobject.GObject.__init__(self, **kwargs)
+
+ # The gtk.ToolButton has already added a normal button.
+ # Replace it with a ColorButton
+ self.color_button = BrushButton(has_invoker=False)
+ self.add(self.color_button)
+ self.color_button.set_brush_size(2)
+ self.color_button.set_brush_shape('circle')
+
+ # The following is so that the behaviour on the toolbar is correct.
+ self.color_button.set_relief(gtk.RELIEF_NONE)
+
+ self._palette_invoker.attach_tool(self)
+
+ # This widget just proxies the following properties to the colorbutton
+ self.color_button.connect('notify::color', self.__notify_change)
+ self.color_button.connect('notify::icon-name', self.__notify_change)
+ self.color_button.connect('notify::icon-size', self.__notify_change)
+ self.color_button.connect('notify::title', self.__notify_change)
+ self.color_button.connect('can-activate-accel',
+ self.__button_can_activate_accel_cb)
+
+ def __button_can_activate_accel_cb(self, button, signal_id):
+ # Accept activation via accelerators regardless of this widget's state
+ return True
+
+ def __notify_change(self, widget, pspec):
+ new_color = self.alloc_color(self.get_color())
+ self.color_button.set_color(new_color)
+ self.notify(pspec.name)
+
+ def _color_button_cb(self, widget, pspec):
+ color = self.get_color()
+ self.set_stroke_color(color)
+
+ def alloc_color(self, color):
+ colormap = self._activity.area.get_colormap()
+ return colormap.alloc_color(color.red, color.green, color.blue)
+
+ def create_palette(self):
+ self._palette = self.get_child().create_palette()
+
+ color_palette_hbox = self._palette._picker_hbox
+ content_box = gtk.VBox()
+ size_spinbutton = gtk.SpinButton()
+ # This is where we set restrictions for size:
+ # Initial value, minimum value, maximum value, step
+ adj = gtk.Adjustment(self.properties['line size'], 1.0, 100.0, 1.0)
+ size_spinbutton.set_adjustment(adj)
+ size_spinbutton.set_numeric(True)
+
+ label = gtk.Label(_('Size: '))
+ hbox = gtk.HBox()
+ content_box.pack_start(hbox)
+
+ hbox.pack_start(label)
+ hbox.pack_start(size_spinbutton)
+ size_spinbutton.connect('value-changed', self._on_value_changed)
+
+ # User is able to choose Shapes for 'Brush' and 'Eraser'
+ item1 = gtk.RadioButton(None, _('Circle'))
+ item1.set_active(True)
+ image1 = gtk.Image()
+ pixbuf1 = gtk.gdk.pixbuf_new_from_file_at_size(
+ './icons/tool-shape-ellipse.svg',
+ style.SMALL_ICON_SIZE,
+ style.SMALL_ICON_SIZE)
+ image1.set_from_pixbuf(pixbuf1)
+ item1.set_image(image1)
+
+ item2 = gtk.RadioButton(item1, _('Square'))
+ image2 = gtk.Image()
+ pixbuf2 = gtk.gdk.pixbuf_new_from_file_at_size(
+ './icons/tool-shape-rectangle.svg',
+ style.SMALL_ICON_SIZE,
+ style.SMALL_ICON_SIZE)
+ image2.set_from_pixbuf(pixbuf2)
+ item2.set_image(image2)
+
+ item1.connect('toggled', self._on_toggled, self.properties, 'circle')
+ item2.connect('toggled', self._on_toggled, self.properties, 'square')
+
+ label = gtk.Label(_('Shape'))
+
+ content_box.pack_start(label)
+ content_box.pack_start(item1)
+ content_box.pack_start(item2)
+
+ keep_aspect_checkbutton = gtk.CheckButton(_('Keep aspect'))
+ ratio = self._activity.area.keep_aspect_ratio
+ keep_aspect_checkbutton.set_active(ratio)
+ keep_aspect_checkbutton.connect('toggled',
+ self._keep_aspect_checkbutton_toggled)
+ content_box.pack_start(keep_aspect_checkbutton)
+
+ color_palette_hbox.pack_start(gtk.VSeparator(),
+ padding=style.DEFAULT_SPACING)
+ color_palette_hbox.pack_start(content_box)
+ color_palette_hbox.show_all()
+ return self._palette
+
+ def _keep_aspect_checkbutton_toggled(self, checkbutton):
+ self._activity.area.keep_aspect_ratio = checkbutton.get_active()
+
+ def _on_value_changed(self, spinbutton):
+ size = spinbutton.get_value_as_int()
+ self.properties['line size'] = size
+ self._activity.area.set_tool(self.properties)
+ self.color_button.set_brush_size(self.properties['line size'])
+
+ def _on_toggled(self, radiobutton, tool, shape):
+ if radiobutton.get_active():
+ self.properties['line shape'] = shape
+ self.color_button.set_brush_shape(shape)
+ self.color_button.set_brush_size(self.properties['line size'])
+
+ def get_palette_invoker(self):
+ return self._palette_invoker
+
+ def set_palette_invoker(self, palette_invoker):
+ self._palette_invoker.detach()
+ self._palette_invoker = palette_invoker
+
+ palette_invoker = gobject.property(
+ type=object, setter=set_palette_invoker, getter=get_palette_invoker)
+
+ def set_color(self, color):
+ self.color_button.set_color(color)
+
+ def get_color(self):
+ return self.get_child().props.color
+
+ color = gobject.property(type=object, getter=get_color, setter=set_color)
+
+ def set_title(self, title):
+ self.get_child().props.title = title
+
+ def get_title(self):
+ return self.get_child().props.title
+
+ title = gobject.property(type=str, getter=get_title, setter=set_title)
+
+ def do_expose_event(self, event):
+ child = self.get_child()
+ allocation = self.get_allocation()
+ if self._palette and self._palette.is_up():
+ invoker = self._palette.props.invoker
+ invoker.draw_rectangle(event, self._palette)
+ elif child.state == gtk.STATE_PRELIGHT:
+ child.style.paint_box(event.window, gtk.STATE_PRELIGHT,
+ gtk.SHADOW_NONE, event.area,
+ child, 'toolbutton-prelight',
+ allocation.x, allocation.y,
+ allocation.width, allocation.height)
+
+ gtk.ToolButton.do_expose_event(self, event)