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