class ColorToolButton(Gtk.ToolItem): # This not ideal. It would be better to subclass Gtk.ToolButton, however # the python bindings do not seem to be powerfull enough for that. # (As we need to change a variable in the class structure.) __gtype_name__ = 'SugarColorToolButton' __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple())} def __init__(self, icon_name='color-preview', **kwargs): 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 color_button = _ColorButton(icon_name=icon_name, has_invoker=False) self.add(color_button) color_button.show() # The following is so that the behaviour on the toolbar is correct. color_button.set_relief(Gtk.ReliefStyle.NONE) color_button.icon_size = style.STANDARD_ICON_SIZE 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 color_button.connect('notify::color', self.__notify_change) color_button.connect('notify::icon-name', self.__notify_change) color_button.connect('notify::icon-size', self.__notify_change) color_button.connect('notify::title', self.__notify_change) color_button.connect('color-set', self.__color_set_cb) 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 set_accelerator(self, accelerator): ''' Sets keyboard shortcut that activates this button. Args: accelerator(string): accelerator to be set. Should be in form <modifier>Letter Find about format here : https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse Example: set_accelerator(self, 'accel') ''' self._accelerator = accelerator setup_accelerator(self) def get_accelerator(self): ''' Returns the above accelerator string. ''' return self._accelerator accelerator = GObject.Property(type=str, setter=set_accelerator, getter=get_accelerator) def create_palette(self): ''' The create_palette function is called when the palette needs to be invoked. For example, when the user has right clicked the icon or the user has hovered over the icon for a long time. The create_palette will only be called once or zero times. The palette returned will be stored and re-used if the user invokes the palette multiple times. Your create_palette implementation does not need to :any:`Gtk.Widget.show` the palette, as this will be done by the invoker. However, you still need to show the menu items, etc that you place in the palette. Returns: sugar3.graphics.palette.Palette, or None to indicate that you do not want a palette shown The default implementation returns None, to indicate no palette should be shown. ''' self._palette = self.get_child().create_palette() return self._palette 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_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): ''' Sets the color of the colorbutton ''' self.get_child().props.color = color def get_color(self): ''' Gets the above set color string. ''' return self.get_child().props.color color = GObject.Property(type=object, getter=get_color, setter=set_color) def set_icon_name(self, icon_name): ''' Sets the icon for the tool button from a named themed icon. If it is none then no icon will be shown. Args: icon_name(string): The name for a themed icon. It can be set as 'None' too. Example: set_icon_name('view-radial') ''' self.get_child().props.icon_name = icon_name def get_icon_name(self): ''' The get_icon_name() method returns the value of the icon_name property that contains the name of a themed icon or None. ''' return self.get_child().props.icon_name icon_name = GObject.Property(type=str, getter=get_icon_name, setter=set_icon_name) def set_icon_size(self, icon_size): ''' Sets the size of icons in the colorbutton. ''' self.get_child().props.icon_size = icon_size def get_icon_size(self): ''' Gets the size of icons in the colorbutton. ''' return self.get_child().props.icon_size icon_size = GObject.Property(type=int, getter=get_icon_size, setter=set_icon_size) def set_title(self, title): ''' The set_title() method sets the "title" property to the value of title. The "title" property contains the string that is used to set the colorbutton title. ''' self.get_child().props.title = title def get_title(self): ''' Return the above title string. ''' return self.get_child().props.title title = GObject.Property(type=str, getter=get_title, setter=set_title) def do_draw(self, cr): if self._palette and self._palette.is_up(): allocation = self.get_allocation() # draw a black background, has been done by the engine before cr.set_source_rgb(0, 0, 0) cr.rectangle(0, 0, allocation.width, allocation.height) cr.paint() Gtk.ToolItem.do_draw(self, cr) if self._palette and self._palette.is_up(): invoker = self._palette.props.invoker invoker.draw_rectangle(cr, self._palette) return False def __notify_change(self, widget, pspec): self.notify(pspec.name) def __color_set_cb(self, widget): self.emit('color-set')
class ColorToolButton(Gtk.ToolItem): # This not ideal. It would be better to subclass Gtk.ToolButton, however # the python bindings do not seem to be powerfull enough for that. # (As we need to change a variable in the class structure.) __gtype_name__ = 'SugarColorToolButton' __gsignals__ = { 'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple()) } def __init__(self, icon_name='color-preview', **kwargs): 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 color_button = _ColorButton(icon_name=icon_name, has_invoker=False) self.add(color_button) color_button.show() # The following is so that the behaviour on the toolbar is correct. color_button.set_relief(Gtk.ReliefStyle.NONE) color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR 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 color_button.connect('notify::color', self.__notify_change) color_button.connect('notify::icon-name', self.__notify_change) color_button.connect('notify::icon-size', self.__notify_change) color_button.connect('notify::title', self.__notify_change) color_button.connect('color-set', self.__color_set_cb) 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 set_accelerator(self, accelerator): self._accelerator = accelerator setup_accelerator(self) def get_accelerator(self): return self._accelerator accelerator = GObject.property(type=str, setter=set_accelerator, getter=get_accelerator) def create_palette(self): self._palette = self.get_child().create_palette() return self._palette 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_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): self.get_child().props.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_icon_name(self, icon_name): self.get_child().props.icon_name = icon_name def get_icon_name(self): return self.get_child().props.icon_name icon_name = GObject.property(type=str, getter=get_icon_name, setter=set_icon_name) def set_icon_size(self, icon_size): self.get_child().props.icon_size = icon_size def get_icon_size(self): return self.get_child().props.icon_size icon_size = GObject.property(type=int, getter=get_icon_size, setter=set_icon_size) 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_draw(self, cr): if self._palette and self._palette.is_up(): allocation = self.get_allocation() # draw a black background, has been done by the engine before cr.set_source_rgb(0, 0, 0) cr.rectangle(0, 0, allocation.width, allocation.height) cr.paint() Gtk.ToolItem.do_draw(self, cr) if self._palette and self._palette.is_up(): invoker = self._palette.props.invoker invoker.draw_rectangle(cr, self._palette) return False def __notify_change(self, widget, pspec): self.notify(pspec.name) def __color_set_cb(self, widget): self.emit('color-set')
class ButtonStrokeColor(Gtk.ToolItem): """Class to manage the Stroke Color of a Button""" __gtype_name__ = 'BrushColorToolButton' __gsignals__ = { 'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple()) } def __init__(self, activity, **kwargs): Gtk.ToolItem.__init__(self, **kwargs) self._activity = activity self.properties = self._activity.area.tool self._accelerator = None self._tooltip = None self._palette_invoker = ToolInvoker() self._palette = None self._selected_tool = None # 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(self.properties['line size']) self.color_button.set_brush_shape(self.properties['line shape']) self.color_button.set_stamp_size(20) # The following is so that the behaviour on the toolbar is correct. self.color_button.set_relief(Gtk.ReliefStyle.NONE) 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 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) self.create_palette() 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): self.color_button.set_color(self.get_color()) self.notify(pspec.name) def _color_button_cb(self, widget, pspec): color = self.get_color() self.set_stroke_color(color) def create_palette(self): self._palette = self.get_child().create_palette() color_palette_hbox = self._palette._picker_hbox self.custom_box = Gtk.VBox() self.vbox_brush_options = Gtk.VBox() # 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) self.size_scale = Gtk.HScale() self.size_scale.set_adjustment(adj) self.size_scale.set_draw_value(False) self.size_scale.set_size_request(style.zoom(200), -1) self.size_label = Gtk.Label(label=_('Size')) self.size_label.props.halign = Gtk.Align.START self.vbox_brush_options.pack_start(self.size_label, True, True, 0) self.vbox_brush_options.pack_start(self.size_scale, True, True, 0) self.size_scale.connect('value-changed', self._on_value_changed) # Control alpha alpha = self.properties['alpha'] * 100 adj_alpha = Gtk.Adjustment(alpha, 10.0, 100.0, 1.0) self.alpha_scale = Gtk.HScale() self.alpha_scale.set_adjustment(adj_alpha) self.alpha_scale.set_draw_value(False) self.alpha_scale.set_size_request(style.zoom(200), -1) self.alpha_label = Gtk.Label(label=_('Opacity')) self.alpha_label.props.halign = Gtk.Align.START self.vbox_brush_options.pack_start(self.alpha_label, True, True, 0) self.vbox_brush_options.pack_start(self.alpha_scale, True, True, 0) self.alpha_scale.connect('value-changed', self._on_alpha_changed) # User is able to choose Shapes for 'Brush' and 'Eraser' self.shape_box = Gtk.HBox() self.custom_box.pack_start(self.vbox_brush_options, True, True, 0) item1 = RadioToolButton() item1.set_icon_name('tool-shape-ellipse') item2 = RadioToolButton() item2.set_icon_name('tool-shape-rectangle') item2.props.group = item1 if self.properties['line shape'] == 'circle': item1.set_active(True) else: item2.set_active(True) item1.connect('toggled', self._on_toggled, self.properties, 'circle') item2.connect('toggled', self._on_toggled, self.properties, 'square') self.shape_box.pack_start(Gtk.Label(_('Shape')), True, True, 0) self.shape_box.pack_start(item1, True, True, 0) self.shape_box.pack_start(item2, True, True, 0) self.vbox_brush_options.pack_start(self.shape_box, True, True, 0) self.keep_aspect_checkbutton = Gtk.CheckButton(_('Keep aspect')) ratio = self._activity.area.keep_aspect_ratio self.keep_aspect_checkbutton.set_active(ratio) self.keep_aspect_checkbutton.connect( 'toggled', self._keep_aspect_checkbutton_toggled) self.vbox_brush_options.pack_start(self.keep_aspect_checkbutton, True, True, 0) self.custom_separator = Gtk.VSeparator() color_palette_hbox.pack_start(self.custom_separator, True, True, padding=style.DEFAULT_SPACING) color_palette_hbox.pack_start(self.custom_box, True, True, padding=style.DEFAULT_SPACING) color_palette_hbox.show_all() self._update_palette() return self._palette def _keep_aspect_checkbutton_toggled(self, checkbutton): self._activity.area.keep_aspect_ratio = checkbutton.get_active() def _update_palette(self): tool_name = self._selected_tool show_controls = () show_colors = False if tool_name == 'brush' or tool_name is None: title = _('Brush properties') show_colors = True show_controls = (self.size_label, self.size_scale, self.shape_box, self.alpha_label, self.alpha_scale) elif tool_name in ('stamp', 'load-stamp'): show_controls = (self.size_label, self.size_scale) title = _('Stamp properties') elif tool_name == 'eraser': show_controls = (self.size_label, self.size_scale, self.shape_box) title = _('Eraser properties') elif tool_name == 'bucket': show_colors = True title = _('Bucket properties') elif tool_name == 'picker': title = _('Picker properties') elif tool_name == 'marquee-rectangular': title = _('Select Area') show_controls = (self.keep_aspect_checkbutton, ) else: title = '' self._palette._picker_hbox.show_all() # Hide palette color widgets except size: if not show_colors: palette_children = self._palette._picker_hbox.get_children() for ch in palette_children: if ch != self.custom_box: ch.hide() elif not show_controls: self.custom_separator.hide() self.vbox_brush_options.show_all() controls = self.vbox_brush_options.get_children() for control in controls: if control not in show_controls: control.hide() # Change title: self.set_title(title) self._palette._picker_hbox.resize_children() self._palette._picker_hbox.queue_draw() def update_stamping(self): if self.color_button.is_stamping(): self.size_scale.set_value(self.color_button.stamp_size) else: self.size_scale.set_value(self.color_button.brush_size) self.size_scale.queue_draw() self._update_palette() def _on_alpha_changed(self, scale): alpha = scale.get_value() / 100.0 self._activity.area.set_alpha(alpha) self.color_button.set_alpha(alpha) def _on_value_changed(self, scale): size = int(scale.get_value()) if self.color_button.is_stamping(): # avoid stamps too small if size > 5: self.properties['stamp size'] = size resized_stamp = self._activity.area.resize_stamp(size) self.color_button.set_resized_stamp(resized_stamp) self.color_button.set_stamp_size(self.properties['stamp size']) else: self.properties['line size'] = size self.color_button.set_brush_size(self.properties['line size']) self._activity.area.set_tool(self.properties) 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_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): 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 get_selected_tool(self): return self._selected_tool def set_selected_tool(self, tool_name): self._selected_tool = tool_name def do_draw(self, cr): if self._palette and self._palette.is_up(): allocation = self.get_allocation() # draw a black background, has been done by the engine before cr.set_source_rgb(0, 0, 0) cr.rectangle(0, 0, allocation.width, allocation.height) cr.paint() Gtk.ToolItem.do_draw(self, cr) if self._palette and self._palette.is_up(): invoker = self._palette.props.invoker invoker.draw_rectangle(cr, self._palette) return False
class ButtonStrokeColor(Gtk.ToolItem): """Class to manage the Stroke Color of a Button""" __gtype_name__ = 'BrushColorToolButton' __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, 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 self._selected_tool = 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(self.properties['line size']) self.color_button.set_brush_shape(self.properties['line shape']) self.color_button.set_stamp_size(20) # The following is so that the behaviour on the toolbar is correct. self.color_button.set_relief(Gtk.ReliefStyle.NONE) 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 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) self.create_palette() 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): self.color_button.set_color(self.get_color()) self.notify(pspec.name) def _color_button_cb(self, widget, pspec): color = self.get_color() self.set_stroke_color(color) def create_palette(self): self._palette = self.get_child().create_palette() color_palette_hbox = self._palette._picker_hbox self.custom_box = Gtk.VBox() self.vbox_brush_options = Gtk.VBox() # 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) self.size_scale = Gtk.HScale() self.size_scale.set_adjustment(adj) self.size_scale.set_draw_value(False) self.size_scale.set_size_request(style.zoom(200), -1) self.size_label = Gtk.Label(label=_('Size')) self.size_label.props.halign = Gtk.Align.START self.vbox_brush_options.pack_start(self.size_label, True, True, 0) self.vbox_brush_options.pack_start(self.size_scale, True, True, 0) self.size_scale.connect('value-changed', self._on_value_changed) # Control alpha alpha = self.properties['alpha'] * 100 adj_alpha = Gtk.Adjustment(alpha, 10.0, 100.0, 1.0) self.alpha_scale = Gtk.HScale() self.alpha_scale.set_adjustment(adj_alpha) self.alpha_scale.set_draw_value(False) self.alpha_scale.set_size_request(style.zoom(200), -1) self.alpha_label = Gtk.Label(label=_('Opacity')) self.alpha_label.props.halign = Gtk.Align.START self.vbox_brush_options.pack_start(self.alpha_label, True, True, 0) self.vbox_brush_options.pack_start(self.alpha_scale, True, True, 0) self.alpha_scale.connect('value-changed', self._on_alpha_changed) # User is able to choose Shapes for 'Brush' and 'Eraser' self.shape_box = Gtk.HBox() self.custom_box.pack_start(self.vbox_brush_options, True, True, 0) item1 = RadioToolButton() item1.set_icon_name('tool-shape-ellipse') item2 = RadioToolButton() item2.set_icon_name('tool-shape-rectangle') item2.props.group = item1 if self.properties['line shape'] == 'circle': item1.set_active(True) else: item2.set_active(True) item1.connect('toggled', self._on_toggled, self.properties, 'circle') item2.connect('toggled', self._on_toggled, self.properties, 'square') self.shape_box.pack_start(Gtk.Label(_('Shape')), True, True, 0) self.shape_box.pack_start(item1, True, True, 0) self.shape_box.pack_start(item2, True, True, 0) self.vbox_brush_options.pack_start(self.shape_box, True, True, 0) self.keep_aspect_checkbutton = Gtk.CheckButton(_('Keep aspect')) ratio = self._activity.area.keep_aspect_ratio self.keep_aspect_checkbutton.set_active(ratio) self.keep_aspect_checkbutton.connect( 'toggled', self._keep_aspect_checkbutton_toggled) self.vbox_brush_options.pack_start(self.keep_aspect_checkbutton, True, True, 0) self.custom_separator = Gtk.VSeparator() color_palette_hbox.pack_start(self.custom_separator, True, True, padding=style.DEFAULT_SPACING) color_palette_hbox.pack_start(self.custom_box, True, True, padding=style.DEFAULT_SPACING) color_palette_hbox.show_all() self._update_palette() return self._palette def _keep_aspect_checkbutton_toggled(self, checkbutton): self._activity.area.keep_aspect_ratio = checkbutton.get_active() def _update_palette(self): tool_name = self._selected_tool show_controls = () show_colors = False if tool_name == 'brush' or tool_name is None: title = _('Brush properties') show_colors = True show_controls = (self.size_label, self.size_scale, self.shape_box, self.alpha_label, self.alpha_scale) elif tool_name == 'stamp': show_controls = (self.size_label, self.size_scale) title = _('Stamp properties') elif tool_name == 'eraser': show_controls = (self.size_label, self.size_scale, self.shape_box) title = _('Eraser properties') elif tool_name == 'bucket': show_colors = True title = _('Bucket properties') elif tool_name == 'picker': title = _('Picker properties') elif tool_name == 'marquee-rectangular': title = _('Select Area') show_controls = (self.keep_aspect_checkbutton,) else: title = '' self._palette._picker_hbox.show_all() # Hide palette color widgets except size: if not show_colors: palette_children = self._palette._picker_hbox.get_children() for ch in palette_children: if ch != self.custom_box: ch.hide() elif not show_controls: self.custom_separator.hide() self.vbox_brush_options.show_all() controls = self.vbox_brush_options.get_children() for control in controls: if control not in show_controls: control.hide() # Change title: self.set_title(title) self._palette._picker_hbox.resize_children() self._palette._picker_hbox.queue_draw() def update_stamping(self): if self.color_button.is_stamping(): self.size_scale.set_value(self.color_button.stamp_size) else: self.size_scale.set_value(self.color_button.brush_size) self._update_palette() def _on_alpha_changed(self, scale): alpha = scale.get_value() / 100.0 self._activity.area.set_alpha(alpha) self.color_button.set_alpha(alpha) def _on_value_changed(self, scale): size = int(scale.get_value()) if self.color_button.is_stamping(): # avoid stamps too small if size > 5: self.properties['stamp size'] = size resized_stamp = self._activity.area.resize_stamp(size) self.color_button.set_resized_stamp(resized_stamp) self.color_button.set_stamp_size(self.properties['stamp size']) else: self.properties['line size'] = size self.color_button.set_brush_size(self.properties['line size']) self._activity.area.set_tool(self.properties) 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_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): 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 get_selected_tool(self): return self._selected_tool def set_selected_tool(self, tool_name): self._selected_tool = tool_name def do_draw(self, cr): if self._palette and self._palette.is_up(): allocation = self.get_allocation() # draw a black background, has been done by the engine before cr.set_source_rgb(0, 0, 0) cr.rectangle(0, 0, allocation.width, allocation.height) cr.paint() Gtk.ToolItem.do_draw(self, cr) if self._palette and self._palette.is_up(): invoker = self._palette.props.invoker invoker.draw_rectangle(cr, self._palette) return False
class ColorToolButton(Gtk.ToolItem): # This not ideal. It would be better to subclass Gtk.ToolButton, however # the python bindings do not seem to be powerfull enough for that. # (As we need to change a variable in the class structure.) __gtype_name__ = 'SugarColorToolButton' __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple())} def __init__(self, icon_name='color-preview', **kwargs): 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 color_button = _ColorButton(icon_name=icon_name, has_invoker=False) self.add(color_button) color_button.show() # The following is so that the behaviour on the toolbar is correct. color_button.set_relief(Gtk.ReliefStyle.NONE) color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR 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 color_button.connect('notify::color', self.__notify_change) color_button.connect('notify::icon-name', self.__notify_change) color_button.connect('notify::icon-size', self.__notify_change) color_button.connect('notify::title', self.__notify_change) color_button.connect('color-set', self.__color_set_cb) 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 set_accelerator(self, accelerator): self._accelerator = accelerator setup_accelerator(self) def get_accelerator(self): return self._accelerator accelerator = GObject.property(type=str, setter=set_accelerator, getter=get_accelerator) def create_palette(self): self._palette = self.get_child().create_palette() return self._palette 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_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): self.get_child().props.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_icon_name(self, icon_name): self.get_child().props.icon_name = icon_name def get_icon_name(self): return self.get_child().props.icon_name icon_name = GObject.property(type=str, getter=get_icon_name, setter=set_icon_name) def set_icon_size(self, icon_size): self.get_child().props.icon_size = icon_size def get_icon_size(self): return self.get_child().props.icon_size icon_size = GObject.property(type=int, getter=get_icon_size, setter=set_icon_size) 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_draw(self, cr): if self._palette and self._palette.is_up(): allocation = self.get_allocation() # draw a black background, has been done by the engine before cr.set_source_rgb(0, 0, 0) cr.rectangle(0, 0, allocation.width, allocation.height) cr.paint() Gtk.ToolItem.do_draw(self, cr) if self._palette and self._palette.is_up(): invoker = self._palette.props.invoker invoker.draw_rectangle(cr, self._palette) return False def __notify_change(self, widget, pspec): self.notify(pspec.name) def __color_set_cb(self, widget): self.emit('color-set')
class ColorToolButton(Gtk.ToolItem): # This not ideal. It would be better to subclass Gtk.ToolButton, however # the python bindings do not seem to be powerfull enough for that. # (As we need to change a variable in the class structure.) __gtype_name__ = 'SugarColorToolButton' __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple())} def __init__(self, icon_name='color-preview', **kwargs): 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 color_button = _ColorButton(icon_name=icon_name, has_invoker=False) self.add(color_button) color_button.show() # The following is so that the behaviour on the toolbar is correct. color_button.set_relief(Gtk.ReliefStyle.NONE) color_button.icon_size = Gtk.IconSize.LARGE_TOOLBAR 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 color_button.connect('notify::color', self.__notify_change) color_button.connect('notify::icon-name', self.__notify_change) color_button.connect('notify::icon-size', self.__notify_change) color_button.connect('notify::title', self.__notify_change) color_button.connect('color-set', self.__color_set_cb) 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 set_accelerator(self, accelerator): ''' Sets keyboard shortcut that activates this button. Args: accelerator(string): accelerator to be set. Should be in form <modifier>Letter Find about format here : https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse Example: set_accelerator(self, 'accel') ''' self._accelerator = accelerator setup_accelerator(self) def get_accelerator(self): ''' Returns the above accelerator string. ''' return self._accelerator accelerator = GObject.Property(type=str, setter=set_accelerator, getter=get_accelerator) def create_palette(self): ''' The create_palette function is called when the palette needs to be invoked. For example, when the user has right clicked the icon or the user has hovered over the icon for a long time. The create_palette will only be called once or zero times. The palette returned will be stored and re-used if the user invokes the palette multiple times. Your create_palette implementation does not need to :any:`Gtk.Widget.show` the palette, as this will be done by the invoker. However, you still need to show the menu items, etc that you place in the palette. Returns: sugar3.graphics.palette.Palette, or None to indicate that you do not want a palette shown The default implementation returns None, to indicate no palette should be shown. ''' self._palette = self.get_child().create_palette() return self._palette 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_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): ''' Sets the color of the colorbutton ''' self.get_child().props.color = color def get_color(self): ''' Gets the above set color string. ''' return self.get_child().props.color color = GObject.Property(type=object, getter=get_color, setter=set_color) def set_icon_name(self, icon_name): ''' Sets the icon for the tool button from a named themed icon. If it is none then no icon will be shown. Args: icon_name(string): The name for a themed icon. It can be set as 'None' too. Example: set_icon_name('view-radial') ''' self.get_child().props.icon_name = icon_name def get_icon_name(self): ''' The get_icon_name() method returns the value of the icon_name property that contains the name of a themed icon or None. ''' return self.get_child().props.icon_name icon_name = GObject.Property(type=str, getter=get_icon_name, setter=set_icon_name) def set_icon_size(self, icon_size): ''' Sets the size of icons in the colorbutton. ''' self.get_child().props.icon_size = icon_size def get_icon_size(self): ''' Gets the size of icons in the colorbutton. ''' return self.get_child().props.icon_size icon_size = GObject.Property(type=int, getter=get_icon_size, setter=set_icon_size) def set_title(self, title): ''' The set_title() method sets the "title" property to the value of title. The "title" property contains the string that is used to set the colorbutton title. ''' self.get_child().props.title = title def get_title(self): ''' Return the above title string. ''' return self.get_child().props.title title = GObject.Property(type=str, getter=get_title, setter=set_title) def do_draw(self, cr): if self._palette and self._palette.is_up(): allocation = self.get_allocation() # draw a black background, has been done by the engine before cr.set_source_rgb(0, 0, 0) cr.rectangle(0, 0, allocation.width, allocation.height) cr.paint() Gtk.ToolItem.do_draw(self, cr) if self._palette and self._palette.is_up(): invoker = self._palette.props.invoker invoker.draw_rectangle(cr, self._palette) return False def __notify_change(self, widget, pspec): self.notify(pspec.name) def __color_set_cb(self, widget): self.emit('color-set')