1# tool_brush.py 2# 3# Copyright 2018-2021 Romain F. T. 4# 5# This program is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18from gi.repository import Gdk 19from .abstract_classic_tool import AbstractClassicTool 20 21from .brush_simple import BrushSimple 22from .brush_airbrush import BrushAirbrush 23from .brush_nib import BrushNib 24from .brush_hairy import BrushHairy 25 26class ToolBrush(AbstractClassicTool): 27 __gtype_name__ = 'ToolBrush' 28 29 def __init__(self, window, **kwargs): 30 super().__init__('brush', _("Brush"), 'tool-brush-symbolic', window) 31 self.use_operator = True 32 self._last_use_pressure = False 33 34 self._brushes_dict = { 35 'simple': BrushSimple('simple', self), 36 'airbrush': BrushAirbrush('airbrush', self), 37 'calligraphic': BrushNib('calligraphic', self), 38 'hairy': BrushHairy('hairy', self), 39 } 40 41 self._brush_type = 'simple' 42 self._brush_dir = 'right' 43 self.add_tool_action_enum('brush-type', self._brush_type) 44 self.add_tool_action_enum('brush-dir', self._brush_dir) 45 46 def get_options_label(self): 47 return _("Brush options") 48 49 def get_edition_status(self): 50 self._brush_type = self.get_option_value('brush-type') 51 self._brush_dir = self.get_option_value('brush-dir') 52 53 enable_direction = self._brush_type == 'calligraphic' 54 self.set_action_sensitivity('brush-dir', enable_direction) 55 56 active_brush = self._brushes_dict[self._brush_type] 57 return active_brush._get_status(self._last_use_pressure, self._brush_dir) 58 59 ############################################################################ 60 61 def on_press_on_area(self, event, surface, event_x, event_y): 62 self.set_common_values(event.button, event_x, event_y) 63 self._manual_path = [] 64 self._add_pressured_point(event_x, event_y, event) 65 self._last_use_pressure = self._manual_path[0]['p'] is not None 66 67 def on_motion_on_area(self, event, surface, event_x, event_y): 68 self._add_pressured_point(event_x, event_y, event) 69 operation = self.build_operation() 70 self.do_tool_operation(operation) 71 72 def on_release_on_area(self, event, surface, event_x, event_y): 73 self._add_pressured_point(event_x, event_y, event) 74 operation = self.build_operation() 75 operation['is_preview'] = False 76 self.apply_operation(operation) 77 78 ############################################################################ 79 80 def _add_pressured_point(self, event_x, event_y, event): 81 new_point = { 82 'x': event_x, 83 'y': event_y, 84 'p': self._get_pressure(event) 85 } 86 self._manual_path.append(new_point) 87 88 def _get_pressure(self, event): 89 device = event.get_source_device() 90 # print(device) 91 if device is None: 92 return None 93 # source = device.get_source() 94 # print(source) # J'ignore s'il faut faire quelque chose de cette info 95 96 tool = event.get_device_tool() 97 # print(tool) # ça indique qu'on a ici un appareil dédié au dessin (vaut 98 # `None` si c'est pas le cas). Autrement on peut avoir des valeurs comme 99 # Gdk.DeviceToolType.PEN, .ERASER, .BRUSH, .PENCIL, ou .AIRBRUSH, et 100 # aussi (même si jsuis pas sûr ce soit pertinent) .UNKNOWN, .MOUSE et 101 # .LENS, on pourrait adapter le comportement (couleur/opérateur/etc.) 102 # à cette information à l'avenir. 103 104 pressure = event.get_axis(Gdk.AxisUse.PRESSURE) 105 # print(pressure) 106 if pressure is None: 107 return None 108 return pressure 109 110 ############################################################################ 111 112 def build_operation(self): 113 operation = { 114 'tool_id': self.id, 115 'brush_id': self._brush_type, 116 'nib_dir': self._brush_dir, 117 'rgba': self.main_color, 118 'operator': self._operator, 119 'line_width': self.tool_width, 120 'antialias': self._use_antialias, 121 'is_preview': True, 122 'path': self._manual_path 123 } 124 return operation 125 126 def do_tool_operation(self, operation): 127 if operation['path'] is None or len(operation['path']) < 1: 128 return 129 cairo_context = self.start_tool_operation(operation) 130 131 active_brush = self._brushes_dict[operation['brush_id']] 132 active_brush.do_brush_operation(cairo_context, operation) 133 134 ############################################################################ 135################################################################################ 136 137