system_demo_mode: minor usability improvements
[blender-addons.git] / space_view3d_brush_menus / texture_menu.py
blob01aec61d0c61d1d28caba83f2d02cb7fa702f4d3
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 import bpy
4 from bpy.types import Menu
5 from . import utils_core
8 class TextureMenu(Menu):
9 bl_label = "Texture Options"
10 bl_idname = "VIEW3D_MT_sv3_texture_menu"
12 @classmethod
13 def poll(self, context):
14 return utils_core.get_mode() in (
15 'SCULPT',
16 'VERTEX_PAINT',
17 'TEXTURE_PAINT'
20 def draw(self, context):
21 layout = self.layout
23 if utils_core.get_mode() == 'SCULPT':
24 self.sculpt(layout, context)
26 elif utils_core.get_mode() == 'VERTEX_PAINT':
27 self.vertpaint(layout, context)
29 else:
30 self.texpaint(layout, context)
32 def sculpt(self, layout, context):
33 has_brush = utils_core.get_brush_link(context, types="brush")
34 tex_slot = has_brush.texture_slot if has_brush else None
36 # Menus
37 layout.row().menu(Textures.bl_idname)
38 layout.row().menu(TextureMapMode.bl_idname)
39 layout.row().separator()
41 # Checkboxes
42 if tex_slot:
43 if tex_slot.map_mode != '3D':
44 if tex_slot.map_mode in ('RANDOM', 'VIEW_PLANE', 'AREA_PLANE'):
45 layout.row().prop(tex_slot, "use_rake", toggle=True)
46 layout.row().prop(tex_slot, "use_random", toggle=True)
48 # Sliders
49 layout.row().prop(tex_slot, "angle",
50 text=utils_core.PIW + "Angle", slider=True)
52 if tex_slot.map_mode in ('RANDOM', 'VIEW_PLANE') and tex_slot.use_random:
53 layout.row().prop(tex_slot, "random_angle",
54 text=utils_core.PIW + "Random Angle", slider=True)
56 # Operator
57 if tex_slot.map_mode == 'STENCIL':
58 if has_brush.texture and has_brush.texture.type == 'IMAGE':
59 layout.row().operator("brush.stencil_fit_image_aspect")
61 layout.row().operator("brush.stencil_reset_transform")
63 else:
64 layout.row().label(text="No Texture Slot available", icon="INFO")
66 def vertpaint(self, layout, context):
67 has_brush = utils_core.get_brush_link(context, types="brush")
68 tex_slot = has_brush.texture_slot if has_brush else None
70 # Menus
71 layout.row().menu(Textures.bl_idname)
72 layout.row().menu(TextureMapMode.bl_idname)
74 # Checkboxes
75 if tex_slot:
76 if tex_slot.map_mode != '3D':
77 if tex_slot.map_mode in ('RANDOM', 'VIEW_PLANE'):
78 layout.row().prop(tex_slot, "use_rake", toggle=True)
79 layout.row().prop(tex_slot, "use_random", toggle=True)
81 # Sliders
82 layout.row().prop(tex_slot, "angle",
83 text=utils_core.PIW + "Angle", slider=True)
85 if tex_slot.map_mode in ('RANDOM', 'VIEW_PLANE') and tex_slot.use_random:
86 layout.row().prop(tex_slot, "random_angle",
87 text=utils_core.PIW + "Random Angle", slider=True)
89 # Operator
90 if tex_slot.map_mode == 'STENCIL':
91 if has_brush.texture and has_brush.texture.type == 'IMAGE':
92 layout.row().operator("brush.stencil_fit_image_aspect")
94 layout.row().operator("brush.stencil_reset_transform")
96 else:
97 layout.row().label(text="No Texture Slot available", icon="INFO")
99 def texpaint(self, layout, context):
100 has_brush = utils_core.get_brush_link(context, types="brush")
101 tex_slot = has_brush.texture_slot if has_brush else None
102 mask_tex_slot = has_brush.mask_texture_slot if has_brush else None
104 # Texture Section
105 layout.row().label(text="Texture", icon='TEXTURE')
107 # Menus
108 layout.row().menu(Textures.bl_idname)
109 layout.row().menu(TextureMapMode.bl_idname)
111 # Checkboxes
112 if tex_slot:
113 if tex_slot.map_mode != '3D':
114 if tex_slot.map_mode in ('RANDOM', 'VIEW_PLANE'):
115 layout.row().prop(tex_slot, "use_rake", toggle=True)
116 layout.row().prop(tex_slot, "use_random", toggle=True)
118 # Sliders
119 layout.row().prop(tex_slot, "angle",
120 text=utils_core.PIW + "Angle", slider=True)
122 if tex_slot.map_mode in ('RANDOM', 'VIEW_PLANE') and tex_slot.use_random:
123 layout.row().prop(tex_slot, "random_angle",
124 text=utils_core.PIW + "Random Angle", slider=True)
126 # Operator
127 if tex_slot.map_mode == 'STENCIL':
128 if has_brush.texture and has_brush.texture.type == 'IMAGE':
129 layout.row().operator("brush.stencil_fit_image_aspect")
131 layout.row().operator("brush.stencil_reset_transform")
133 else:
134 layout.row().label(text="No Texture Slot available", icon="INFO")
136 layout.row().separator()
138 # Texture Mask Section
139 layout.row().label(text="Texture Mask", icon='MOD_MASK')
141 # Menus
142 layout.row().menu(MaskTextures.bl_idname)
143 layout.row().menu(MaskMapMode.bl_idname)
144 layout.row().menu(MaskPressureModeMenu.bl_idname)
146 # Checkboxes
147 if mask_tex_slot:
148 if mask_tex_slot.mask_map_mode in ('RANDOM', 'VIEW_PLANE'):
149 layout.row().prop(mask_tex_slot, "use_rake", toggle=True)
150 layout.row().prop(mask_tex_slot, "use_random", toggle=True)
152 # Sliders
153 layout.row().prop(mask_tex_slot, "angle",
154 text=utils_core.PIW + "Angle", icon_value=5, slider=True)
156 if mask_tex_slot.mask_map_mode in ('RANDOM', 'VIEW_PLANE') and mask_tex_slot.use_random:
157 layout.row().prop(mask_tex_slot, "random_angle",
158 text=utils_core.PIW + "Random Angle", slider=True)
160 # Operator
161 if mask_tex_slot.mask_map_mode == 'STENCIL':
162 if has_brush.mask_texture and has_brush.mask_texture.type == 'IMAGE':
163 layout.row().operator("brush.stencil_fit_image_aspect")
165 prop = layout.row().operator("brush.stencil_reset_transform")
166 prop.mask = True
168 else:
169 layout.row().label(text="Mask Texture not available", icon="INFO")
172 class Textures(Menu):
173 bl_label = "Brush Texture"
174 bl_idname = "VIEW3D_MT_sv3_texture_list"
176 def init(self):
177 if utils_core.get_mode() == 'SCULPT':
178 datapath = "tool_settings.sculpt.brush.texture"
180 elif utils_core.get_mode() == 'VERTEX_PAINT':
181 datapath = "tool_settings.vertex_paint.brush.texture"
183 elif utils_core.get_mode() == 'TEXTURE_PAINT':
184 datapath = "tool_settings.image_paint.brush.texture"
186 else:
187 datapath = ""
189 return datapath
191 def draw(self, context):
192 datapath = self.init()
193 has_brush = utils_core.get_brush_link(context, types="brush")
194 current_texture = eval("bpy.context.{}".format(datapath)) if \
195 has_brush else None
196 layout = self.layout
198 # get the current texture's name
199 if current_texture:
200 current_texture = current_texture.name
202 layout.row().label(text="Brush Texture")
203 layout.row().separator()
205 # add an item to set the texture to None
206 utils_core.menuprop(layout.row(), "None", "None",
207 datapath, icon='RADIOBUT_OFF', disable=True,
208 disable_icon='RADIOBUT_ON',
209 custom_disable_exp=(None, current_texture),
210 path=True)
212 # add the menu items
213 for item in bpy.data.textures:
214 utils_core.menuprop(layout.row(), item.name,
215 'bpy.data.textures["%s"]' % item.name,
216 datapath, icon='RADIOBUT_OFF',
217 disable=True,
218 disable_icon='RADIOBUT_ON',
219 custom_disable_exp=(item.name, current_texture),
220 path=True)
223 class TextureMapMode(Menu):
224 bl_label = "Brush Mapping"
225 bl_idname = "VIEW3D_MT_sv3_texture_map_mode"
227 def draw(self, context):
228 layout = self.layout
229 has_brush = utils_core.get_brush_link(context, types="brush")
231 layout.row().label(text="Brush Mapping")
232 layout.row().separator()
234 if has_brush:
235 if utils_core.get_mode() == 'SCULPT':
236 path = "tool_settings.sculpt.brush.texture_slot.map_mode"
238 # add the menu items
239 for item in has_brush. \
240 texture_slot.bl_rna.properties['map_mode'].enum_items:
241 utils_core.menuprop(
242 layout.row(), item.name, item.identifier, path,
243 icon='RADIOBUT_OFF',
244 disable=True,
245 disable_icon='RADIOBUT_ON'
247 elif utils_core.get_mode() == 'VERTEX_PAINT':
248 path = "tool_settings.vertex_paint.brush.texture_slot.map_mode"
250 # add the menu items
251 for item in has_brush. \
252 texture_slot.bl_rna.properties['map_mode'].enum_items:
253 utils_core.menuprop(
254 layout.row(), item.name, item.identifier, path,
255 icon='RADIOBUT_OFF',
256 disable=True,
257 disable_icon='RADIOBUT_ON'
259 else:
260 path = "tool_settings.image_paint.brush.texture_slot.map_mode"
262 # add the menu items
263 for item in has_brush. \
264 texture_slot.bl_rna.properties['map_mode'].enum_items:
265 utils_core.menuprop(
266 layout.row(), item.name, item.identifier, path,
267 icon='RADIOBUT_OFF',
268 disable=True,
269 disable_icon='RADIOBUT_ON'
271 else:
272 layout.row().label(text="No brushes available", icon="INFO")
275 class MaskTextures(Menu):
276 bl_label = "Mask Texture"
277 bl_idname = "VIEW3D_MT_sv3_mask_texture_list"
279 def draw(self, context):
280 layout = self.layout
281 datapath = "tool_settings.image_paint.brush.mask_texture"
282 has_brush = utils_core.get_brush_link(context, types="brush")
283 current_texture = eval("bpy.context.{}".format(datapath)) if \
284 has_brush else None
286 layout.row().label(text="Mask Texture")
287 layout.row().separator()
289 if has_brush:
290 # get the current texture's name
291 if current_texture:
292 current_texture = current_texture.name
294 # add an item to set the texture to None
295 utils_core.menuprop(
296 layout.row(), "None", "None",
297 datapath, icon='RADIOBUT_OFF', disable=True,
298 disable_icon='RADIOBUT_ON',
299 custom_disable_exp=(None, current_texture),
300 path=True
303 # add the menu items
304 for item in bpy.data.textures:
305 utils_core.menuprop(
306 layout.row(), item.name, 'bpy.data.textures["%s"]' % item.name,
307 datapath, icon='RADIOBUT_OFF', disable=True,
308 disable_icon='RADIOBUT_ON',
309 custom_disable_exp=(item.name, current_texture),
310 path=True
312 else:
313 layout.row().label(text="No brushes available", icon="INFO")
316 class MaskMapMode(Menu):
317 bl_label = "Mask Mapping"
318 bl_idname = "VIEW3D_MT_sv3_mask_map_mode"
320 def draw(self, context):
321 layout = self.layout
322 path = "tool_settings.image_paint.brush.mask_texture_slot.mask_map_mode"
323 has_brush = utils_core.get_brush_link(context, types="brush")
325 layout.row().label(text="Mask Mapping")
326 layout.row().separator()
327 if has_brush:
328 items = has_brush. \
329 mask_texture_slot.bl_rna.properties['mask_map_mode'].enum_items
330 # add the menu items
331 for item in items:
332 utils_core.menuprop(
333 layout.row(), item.name, item.identifier, path,
334 icon='RADIOBUT_OFF',
335 disable=True,
336 disable_icon='RADIOBUT_ON'
338 else:
339 layout.row().label(text="No brushes available", icon="INFO")
342 class TextureAngleSource(Menu):
343 bl_label = "Texture Angle Source"
344 bl_idname = "VIEW3D_MT_sv3_texture_angle_source"
346 def draw(self, context):
347 layout = self.layout
348 has_brush = utils_core.get_brush_link(context, types="brush")
350 if has_brush:
351 if utils_core.get_mode() == 'SCULPT':
352 items = has_brush. \
353 bl_rna.properties['texture_angle_source_random'].enum_items
354 path = "tool_settings.sculpt.brush.texture_angle_source_random"
356 elif utils_core.get_mode() == 'VERTEX_PAINT':
357 items = has_brush. \
358 bl_rna.properties['texture_angle_source_random'].enum_items
359 path = "tool_settings.vertex_paint.brush.texture_angle_source_random"
361 else:
362 items = has_brush. \
363 bl_rna.properties['texture_angle_source_random'].enum_items
364 path = "tool_settings.image_paint.brush.texture_angle_source_random"
366 # add the menu items
367 for item in items:
368 utils_core.menuprop(
369 layout.row(), item[0], item[1], path,
370 icon='RADIOBUT_OFF',
371 disable=True,
372 disable_icon='RADIOBUT_ON'
374 else:
375 layout.row().label(text="No brushes available", icon="INFO")
377 class MaskPressureModeMenu(Menu):
378 bl_label = "Mask Pressure Mode"
379 bl_idname = "VIEW3D_MT_sv3_mask_pressure_mode_menu"
381 def draw(self, context):
382 layout = self.layout
383 path = "tool_settings.image_paint.brush.use_pressure_masking"
385 layout.row().label(text="Mask Pressure Mode")
386 layout.row().separator()
388 # add the menu items
389 for item in context.tool_settings.image_paint.brush. \
390 bl_rna.properties['use_pressure_masking'].enum_items:
391 utils_core.menuprop(
392 layout.row(), item.name, item.identifier, path,
393 icon='RADIOBUT_OFF',
394 disable=True,
395 disable_icon='RADIOBUT_ON'
399 classes = (
400 TextureMenu,
401 Textures,
402 TextureMapMode,
403 MaskTextures,
404 MaskMapMode,
405 TextureAngleSource,
406 MaskPressureModeMenu
409 def register():
410 for cls in classes:
411 bpy.utils.register_class(cls)
413 def unregister():
414 for cls in classes:
415 bpy.utils.unregister_class(cls)