Images: make it harder to accidentally undo image texture painting changes
[blender.git] / release / scripts / startup / bl_ui / space_image.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 from bpy.types import (
22     Header,
23     Menu,
24     Panel,
25     UIList,
26 )
27 from .properties_paint_common import (
28     UnifiedPaintPanel,
29     brush_texture_settings,
30     brush_texpaint_common,
31     brush_texpaint_common_color,
32     brush_texpaint_common_gradient,
33     brush_texpaint_common_clone,
34     brush_texpaint_common_options,
35     brush_mask_texture_settings,
36 )
37 from .properties_grease_pencil_common import (
38     AnnotationDataPanel,
39 )
40 from .space_toolsystem_common import (
41     ToolActivePanelHelper,
42 )
43
44 from bpy.app.translations import pgettext_iface as iface_
45
46
47 class ImagePaintPanel(UnifiedPaintPanel):
48     bl_space_type = 'IMAGE_EDITOR'
49     bl_region_type = 'UI'
50
51
52 class BrushButtonsPanel(UnifiedPaintPanel):
53     bl_space_type = 'IMAGE_EDITOR'
54     bl_region_type = 'UI'
55
56     @classmethod
57     def poll(cls, context):
58         tool_settings = context.tool_settings.image_paint
59         return tool_settings.brush
60
61
62 class IMAGE_PT_active_tool(ToolActivePanelHelper, Panel):
63     bl_space_type = 'IMAGE_EDITOR'
64     bl_region_type = 'UI'
65     bl_category = "Tool"
66
67
68 class IMAGE_MT_view(Menu):
69     bl_label = "View"
70
71     def draw(self, context):
72         layout = self.layout
73
74         sima = context.space_data
75         uv = sima.uv_editor
76         tool_settings = context.tool_settings
77         paint = tool_settings.image_paint
78
79         show_uvedit = sima.show_uvedit
80         show_render = sima.show_render
81
82         layout.prop(sima, "show_region_toolbar")
83         layout.prop(sima, "show_region_ui")
84         layout.prop(sima, "show_region_hud")
85
86         layout.separator()
87
88         layout.prop(sima, "use_realtime_update")
89         if show_uvedit:
90             layout.prop(tool_settings, "show_uv_local_view")
91
92         layout.prop(uv, "show_metadata")
93
94         if paint.brush and (context.image_paint_object or sima.mode == 'PAINT'):
95             layout.prop(uv, "show_texpaint")
96             layout.prop(tool_settings, "show_uv_local_view", text="Show Same Material")
97
98         layout.separator()
99
100         layout.operator("image.view_zoom_in")
101         layout.operator("image.view_zoom_out")
102
103         layout.separator()
104
105         layout.menu("IMAGE_MT_view_zoom")
106
107         layout.separator()
108
109         if show_uvedit:
110             layout.operator("image.view_selected")
111
112         layout.operator("image.view_all")
113         layout.operator("image.view_all", text="View Fit").fit_view = True
114
115         layout.separator()
116
117         if show_render:
118             layout.operator("image.render_border")
119             layout.operator("image.clear_render_border")
120
121             layout.separator()
122
123             layout.operator("image.cycle_render_slot", text="Render Slot Cycle Next")
124             layout.operator("image.cycle_render_slot", text="Render Slot Cycle Previous").reverse = True
125             layout.separator()
126
127         layout.menu("INFO_MT_area")
128
129
130 class IMAGE_MT_view_zoom(Menu):
131     bl_label = "Fractional Zoom"
132
133     def draw(self, _context):
134         layout = self.layout
135
136         ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
137
138         for i, (a, b) in enumerate(ratios):
139             if i in {3, 4}:  # Draw separators around Zoom 1:1.
140                 layout.separator()
141
142             layout.operator(
143                 "image.view_zoom_ratio",
144                 text=iface_(f"Zoom {a:d}:{b:d}"),
145                 translate=False,
146             ).ratio = a / b
147
148
149 class IMAGE_MT_select(Menu):
150     bl_label = "Select"
151
152     def draw(self, _context):
153         layout = self.layout
154
155         layout.operator("uv.select_all", text="All").action = 'SELECT'
156         layout.operator("uv.select_all", text="None").action = 'DESELECT'
157         layout.operator("uv.select_all", text="Invert").action = 'INVERT'
158
159         layout.separator()
160
161         layout.operator("uv.select_box").pinned = False
162         layout.operator("uv.select_box", text="Box Select Pinned").pinned = True
163         layout.operator("uv.select_circle")
164
165         layout.separator()
166
167         layout.operator("uv.select_less", text="Less")
168         layout.operator("uv.select_more", text="More")
169
170         layout.separator()
171
172         layout.operator("uv.select_pinned")
173         layout.operator("uv.select_linked")
174
175         layout.separator()
176
177         layout.operator("uv.select_split")
178
179
180 class IMAGE_MT_brush(Menu):
181     bl_label = "Brush"
182
183     def draw(self, context):
184         layout = self.layout
185         tool_settings = context.tool_settings
186         settings = tool_settings.image_paint
187         brush = settings.brush
188
189         ups = context.tool_settings.unified_paint_settings
190         layout.prop(ups, "use_unified_size", text="Unified Size")
191         layout.prop(ups, "use_unified_strength", text="Unified Strength")
192         layout.prop(ups, "use_unified_color", text="Unified Color")
193         layout.separator()
194
195         # Brush tool.
196         layout.prop_menu_enum(brush, "image_tool")
197
198
199 class IMAGE_MT_image(Menu):
200     bl_label = "Image"
201
202     def draw(self, context):
203         layout = self.layout
204
205         sima = context.space_data
206         ima = sima.image
207         show_render = sima.show_render
208
209         layout.operator("image.new", text="New")
210         layout.operator("image.open", text="Open...", icon='FILE_FOLDER')
211
212         layout.operator("image.read_viewlayers")
213
214         if ima:
215             layout.separator()
216
217             if not show_render:
218                 layout.operator("image.replace", text="Replace...")
219                 layout.operator("image.reload", text="Reload")
220
221             layout.operator("image.external_edit", text="Edit Externally")
222
223         layout.separator()
224
225         if ima:
226             layout.operator("image.save", text="Save", icon='FILE_TICK')
227             layout.operator("image.save_as", text="Save As...")
228             layout.operator("image.save_as", text="Save a Copy...").copy = True
229
230         if ima and ima.source == 'SEQUENCE':
231             layout.operator("image.save_sequence")
232
233         layout.operator("image.save_all_modified", text="Save All Images")
234
235         if ima:
236             layout.separator()
237
238             layout.menu("IMAGE_MT_image_invert")
239
240             if not show_render:
241                 layout.separator()
242                 if ima.packed_file:
243                     layout.operator("image.pack", text="Repack")
244                     layout.operator("image.unpack", text="Unpack")
245                 else:
246                     layout.operator("image.pack", text="Pack")
247
248
249 class IMAGE_MT_image_invert(Menu):
250     bl_label = "Invert"
251
252     def draw(self, _context):
253         layout = self.layout
254
255         props = layout.operator("image.invert", text="Invert Image Colors", icon='IMAGE_RGB')
256         props.invert_r = True
257         props.invert_g = True
258         props.invert_b = True
259
260         layout.separator()
261
262         layout.operator("image.invert", text="Invert Red Channel", icon='COLOR_RED').invert_r = True
263         layout.operator("image.invert", text="Invert Green Channel", icon='COLOR_GREEN').invert_g = True
264         layout.operator("image.invert", text="Invert Blue Channel", icon='COLOR_BLUE').invert_b = True
265         layout.operator("image.invert", text="Invert Alpha Channel", icon='IMAGE_ALPHA').invert_a = True
266
267
268 class IMAGE_MT_uvs_showhide(Menu):
269     bl_label = "Show/Hide Faces"
270
271     def draw(self, _context):
272         layout = self.layout
273
274         layout.operator("uv.reveal")
275         layout.operator("uv.hide", text="Hide Selected").unselected = False
276         layout.operator("uv.hide", text="Hide Unselected").unselected = True
277
278
279 class IMAGE_MT_uvs_transform(Menu):
280     bl_label = "Transform"
281
282     def draw(self, _context):
283         layout = self.layout
284
285         layout.operator("transform.translate")
286         layout.operator("transform.rotate")
287         layout.operator("transform.resize")
288
289         layout.separator()
290
291         layout.operator("transform.shear")
292
293
294 class IMAGE_MT_uvs_snap(Menu):
295     bl_label = "Snap"
296
297     def draw(self, _context):
298         layout = self.layout
299
300         layout.operator_context = 'EXEC_REGION_WIN'
301
302         layout.operator("uv.snap_selected", text="Selected to Pixels").target = 'PIXELS'
303         layout.operator("uv.snap_selected", text="Selected to Cursor").target = 'CURSOR'
304         layout.operator("uv.snap_selected", text="Selected to Cursor (Offset)").target = 'CURSOR_OFFSET'
305         layout.operator("uv.snap_selected", text="Selected to Adjacent Unselected").target = 'ADJACENT_UNSELECTED'
306
307         layout.separator()
308
309         layout.operator("uv.snap_cursor", text="Cursor to Pixels").target = 'PIXELS'
310         layout.operator("uv.snap_cursor", text="Cursor to Selected").target = 'SELECTED'
311
312
313 class IMAGE_MT_uvs_mirror(Menu):
314     bl_label = "Mirror"
315
316     def draw(self, _context):
317         layout = self.layout
318
319         layout.operator("mesh.faces_mirror_uv")
320
321         layout.separator()
322
323         layout.operator_context = 'EXEC_REGION_WIN'
324
325         layout.operator("transform.mirror", text="X Axis").constraint_axis[0] = True
326         layout.operator("transform.mirror", text="Y Axis").constraint_axis[1] = True
327
328
329 class IMAGE_MT_uvs_weldalign(Menu):
330     bl_label = "Weld/Align"
331
332     def draw(self, _context):
333         layout = self.layout
334
335         layout.operator("uv.weld")  # W, 1.
336         layout.operator("uv.remove_doubles")
337         layout.operator_enum("uv.align", "axis")  # W, 2/3/4.
338
339
340 class IMAGE_MT_uvs(Menu):
341     bl_label = "UV"
342
343     def draw(self, context):
344         layout = self.layout
345
346         sima = context.space_data
347         uv = sima.uv_editor
348         tool_settings = context.tool_settings
349
350         layout.menu("IMAGE_MT_uvs_transform")
351         layout.menu("IMAGE_MT_uvs_mirror")
352         layout.menu("IMAGE_MT_uvs_snap")
353
354         layout.prop_menu_enum(uv, "pixel_snap_mode")
355         layout.prop(uv, "lock_bounds")
356
357         layout.separator()
358
359         layout.prop(uv, "use_live_unwrap")
360         layout.operator("uv.unwrap")
361
362         layout.separator()
363
364         layout.operator("uv.pin").clear = False
365         layout.operator("uv.pin", text="Unpin").clear = True
366
367         layout.separator()
368
369         layout.operator("uv.mark_seam").clear = False
370         layout.operator("uv.mark_seam", text="Clear Seam").clear = True
371         layout.operator("uv.seams_from_islands")
372
373         layout.separator()
374
375         layout.operator("uv.pack_islands")
376         layout.operator("uv.average_islands_scale")
377
378         layout.separator()
379
380         layout.operator("uv.minimize_stretch")
381         layout.operator("uv.stitch")
382         layout.menu("IMAGE_MT_uvs_weldalign")
383
384         layout.separator()
385
386         layout.menu("IMAGE_MT_uvs_showhide")
387
388         layout.separator()
389
390
391 class IMAGE_MT_uvs_select_mode(Menu):
392     bl_label = "UV Select Mode"
393
394     def draw(self, context):
395         layout = self.layout
396
397         layout.operator_context = 'INVOKE_REGION_WIN'
398         tool_settings = context.tool_settings
399
400         # Do smart things depending on whether uv_select_sync is on.
401
402         if tool_settings.use_uv_select_sync:
403             props = layout.operator("wm.context_set_value", text="Vertex", icon='VERTEXSEL')
404             props.value = "(True, False, False)"
405             props.data_path = "tool_settings.mesh_select_mode"
406
407             props = layout.operator("wm.context_set_value", text="Edge", icon='EDGESEL')
408             props.value = "(False, True, False)"
409             props.data_path = "tool_settings.mesh_select_mode"
410
411             props = layout.operator("wm.context_set_value", text="Face", icon='FACESEL')
412             props.value = "(False, False, True)"
413             props.data_path = "tool_settings.mesh_select_mode"
414
415         else:
416             props = layout.operator("wm.context_set_string", text="Vertex", icon='UV_VERTEXSEL')
417             props.value = 'VERTEX'
418             props.data_path = "tool_settings.uv_select_mode"
419
420             props = layout.operator("wm.context_set_string", text="Edge", icon='UV_EDGESEL')
421             props.value = 'EDGE'
422             props.data_path = "tool_settings.uv_select_mode"
423
424             props = layout.operator("wm.context_set_string", text="Face", icon='UV_FACESEL')
425             props.value = 'FACE'
426             props.data_path = "tool_settings.uv_select_mode"
427
428             props = layout.operator("wm.context_set_string", text="Island", icon='UV_ISLANDSEL')
429             props.value = 'ISLAND'
430             props.data_path = "tool_settings.uv_select_mode"
431
432
433 class IMAGE_MT_uvs_context_menu(Menu):
434     bl_label = "UV Context Menu"
435
436     def draw(self, context):
437         layout = self.layout
438
439         sima = context.space_data
440
441         # UV Edit Mode
442         if sima.show_uvedit:
443             # Add
444             layout.operator("uv.unwrap")
445             layout.operator("uv.follow_active_quads")
446
447             layout.separator()
448
449             # Modify
450             layout.operator("uv.pin").clear = False
451             layout.operator("uv.pin", text="Unpin").clear = True
452
453             layout.separator()
454
455             layout.menu("IMAGE_MT_uvs_snap")
456
457             layout.operator("transform.mirror", text="Mirror X").constraint_axis[0] = True
458             layout.operator("transform.mirror", text="Mirror Y").constraint_axis[1] = True
459
460             layout.separator()
461
462             layout.operator_enum("uv.align", "axis")  # W, 2/3/4.
463
464             layout.separator()
465
466             # Remove
467             layout.operator("uv.remove_doubles", text="Remove Double UVs")
468             layout.operator("uv.stitch")
469             layout.operator("uv.weld")
470
471
472 class IMAGE_MT_pivot_pie(Menu):
473     bl_label = "Pivot Point"
474
475     def draw(self, context):
476         layout = self.layout
477         pie = layout.menu_pie()
478
479         pie.prop_enum(context.space_data, "pivot_point", value='CENTER')
480         pie.prop_enum(context.space_data, "pivot_point", value='CURSOR')
481         pie.prop_enum(context.space_data, "pivot_point", value='INDIVIDUAL_ORIGINS')
482         pie.prop_enum(context.space_data, "pivot_point", value='MEDIAN')
483
484
485 class IMAGE_MT_uvs_snap_pie(Menu):
486     bl_label = "Snap"
487
488     def draw(self, _context):
489         layout = self.layout
490         pie = layout.menu_pie()
491
492         layout.operator_context = 'EXEC_REGION_WIN'
493
494         pie.operator("uv.snap_selected", text="Selected to Pixels", icon='RESTRICT_SELECT_OFF').target = 'PIXELS'
495         pie.operator("uv.snap_cursor", text="Cursor to Pixels", icon='PIVOT_CURSOR').target = 'PIXELS'
496         pie.operator("uv.snap_cursor", text="Cursor to Selected", icon='PIVOT_CURSOR').target = 'SELECTED'
497         pie.operator("uv.snap_selected", text="Selected to Cursor", icon='RESTRICT_SELECT_OFF').target = 'CURSOR'
498         pie.operator("uv.snap_selected", text="Selected to Cursor (Offset)", icon='RESTRICT_SELECT_OFF').target = 'CURSOR_OFFSET'
499         pie.operator("uv.snap_selected", text="Selected to Adjacent Unselected", icon='RESTRICT_SELECT_OFF').target = 'ADJACENT_UNSELECTED'
500
501
502 class IMAGE_HT_tool_header(Header):
503     bl_space_type = 'IMAGE_EDITOR'
504     bl_region_type = "TOOL_HEADER"
505
506     def draw(self, context):
507         layout = self.layout
508
509         layout.template_header()
510
511         self.draw_tool_settings(context)
512
513         sima = context.space_data
514
515         layout.separator_spacer()
516
517         IMAGE_HT_header.draw_xform_template(layout, context)
518
519         layout.separator_spacer()
520
521         self.draw_mode_settings(context)
522
523     def draw_tool_settings(self, context):
524         layout = self.layout
525
526         # Active Tool
527         # -----------
528         from .space_toolsystem_common import ToolSelectPanelHelper
529         tool = ToolSelectPanelHelper.draw_active_tool_header(context, layout)
530         tool_mode = context.mode if tool is None else tool.mode
531
532         # Object Mode Options
533         # -------------------
534
535         # Example of how tool_settings can be accessed as pop-overs.
536
537         # TODO(campbell): editing options should be after active tool options
538         # (obviously separated for from the users POV)
539         draw_fn = getattr(_draw_tool_settings_context_mode, tool_mode, None)
540         if draw_fn is not None:
541             draw_fn(context, layout, tool)
542
543         if tool_mode == 'PAINT':
544             if (tool is not None) and tool.has_datablock:
545                 layout.popover_group(space_type='IMAGE_EDITOR', region_type='UI', context=".paint_common_2d", category="")
546         elif tool_mode == 'UV':
547             if (tool is not None) and tool.has_datablock:
548                 layout.popover_group(space_type='IMAGE_EDITOR', region_type='UI', context=".uv_sculpt", category="")
549
550     def draw_mode_settings(self, context):
551         layout = self.layout
552
553         # Active Tool
554         # -----------
555         from .space_toolsystem_common import ToolSelectPanelHelper
556         tool = ToolSelectPanelHelper.tool_active_from_context(context)
557         tool_mode = context.mode if tool is None else tool.mode
558
559         if tool_mode == 'PAINT':
560             layout.popover_group(space_type='IMAGE_EDITOR', region_type='UI', context=".imagepaint_2d", category="")
561
562
563 class _draw_tool_settings_context_mode:
564     @staticmethod
565     def UV(context, layout, tool):
566         if tool and tool.has_datablock:
567             if context.mode == 'EDIT_MESH':
568                 tool_settings = context.tool_settings
569                 uv_sculpt = tool_settings.uv_sculpt
570                 brush = uv_sculpt.brush
571                 if brush:
572                     from .properties_paint_common import UnifiedPaintPanel
573
574                     row = layout.row(align=True)
575                     UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True)
576                     UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size", text="")
577
578                     row = layout.row(align=True)
579                     UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True)
580                     UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength", text="")
581
582     @staticmethod
583     def PAINT(context, layout, tool):
584         if (tool is None) or (not tool.has_datablock):
585             return
586
587         paint = context.tool_settings.image_paint
588         layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
589
590         brush = paint.brush
591         if brush is None:
592             return
593
594         from .properties_paint_common import (
595             UnifiedPaintPanel,
596             brush_basic_texpaint_settings,
597         )
598         capabilities = brush.image_paint_capabilities
599         if capabilities.has_color:
600             UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
601         brush_basic_texpaint_settings(layout, context, brush, compact=True)
602
603
604 class IMAGE_HT_header(Header):
605     bl_space_type = 'IMAGE_EDITOR'
606
607     @staticmethod
608     def draw_xform_template(layout, context):
609         sima = context.space_data
610         show_uvedit = sima.show_uvedit
611         show_maskedit = sima.show_maskedit
612
613         if show_uvedit or show_maskedit:
614             layout.prop(sima, "pivot_point", icon_only=True)
615
616         if show_uvedit:
617             tool_settings = context.tool_settings
618
619             # Snap.
620             row = layout.row(align=True)
621             row.prop(tool_settings, "use_snap", text="")
622             row.prop(tool_settings, "snap_uv_element", icon_only=True)
623             if tool_settings.snap_uv_element != 'INCREMENT':
624                 row.prop(tool_settings, "snap_target", text="")
625
626             # Proportional Editing
627             row = layout.row(align=True)
628             row.prop(tool_settings, "use_proportional_edit", icon_only=True)
629             sub = row.row(align=True)
630             sub.active = tool_settings.use_proportional_edit
631             sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
632
633     def draw(self, context):
634         layout = self.layout
635
636         sima = context.space_data
637         ima = sima.image
638         iuser = sima.image_user
639         tool_settings = context.tool_settings
640         show_region_tool_header = sima.show_region_tool_header
641
642         show_render = sima.show_render
643         show_uvedit = sima.show_uvedit
644         show_maskedit = sima.show_maskedit
645
646         if not show_region_tool_header:
647             layout.template_header()
648
649         if sima.mode != 'UV':
650             layout.prop(sima, "ui_mode", text="")
651
652         # UV editing.
653         if show_uvedit:
654             uvedit = sima.uv_editor
655
656             layout.prop(tool_settings, "use_uv_select_sync", text="")
657
658             if tool_settings.use_uv_select_sync:
659                 layout.template_edit_mode_selection()
660             else:
661                 layout.prop(tool_settings, "uv_select_mode", text="", expand=True)
662                 layout.prop(uvedit, "sticky_select_mode", icon_only=True)
663
664         MASK_MT_editor_menus.draw_collapsible(context, layout)
665
666         layout.separator_spacer()
667
668         if not show_region_tool_header:
669             IMAGE_HT_header.draw_xform_template(layout, context)
670
671         layout.template_ID(sima, "image", new="image.new", open="image.open")
672
673         if show_maskedit:
674             row = layout.row()
675             row.template_ID(sima, "mask", new="mask.new")
676
677         if not show_render:
678             layout.prop(sima, "use_image_pin", text="")
679
680         layout.separator_spacer()
681
682         if show_uvedit:
683             uvedit = sima.uv_editor
684
685             mesh = context.edit_object.data
686             layout.prop_search(mesh.uv_layers, "active", mesh, "uv_layers", text="")
687
688         if ima:
689             if ima.is_stereo_3d:
690                 row = layout.row()
691                 row.prop(sima, "show_stereo_3d", text="")
692
693             # layers.
694             layout.template_image_layers(ima, iuser)
695
696             # draw options.
697             row = layout.row()
698             row.prop(sima, "display_channels", icon_only=True)
699
700             row = layout.row(align=True)
701             if ima.type == 'COMPOSITE':
702                 row.operator("image.record_composite", icon='REC')
703             if ima.type == 'COMPOSITE' and ima.source in {'MOVIE', 'SEQUENCE'}:
704                 row.operator("image.play_composite", icon='PLAY')
705
706
707 class MASK_MT_editor_menus(Menu):
708     bl_idname = "MASK_MT_editor_menus"
709     bl_label = ""
710
711     def draw(self, context):
712         layout = self.layout
713         sima = context.space_data
714         ima = sima.image
715
716         show_uvedit = sima.show_uvedit
717         show_maskedit = sima.show_maskedit
718         show_paint = sima.show_paint
719
720         layout.menu("IMAGE_MT_view")
721
722         if show_uvedit:
723             layout.menu("IMAGE_MT_select")
724         if show_maskedit:
725             layout.menu("MASK_MT_select")
726         if show_paint:
727             layout.menu("IMAGE_MT_brush")
728
729         if ima and ima.is_dirty:
730             layout.menu("IMAGE_MT_image", text="Image*")
731         else:
732             layout.menu("IMAGE_MT_image", text="Image")
733
734         if show_uvedit:
735             layout.menu("IMAGE_MT_uvs")
736         if show_maskedit:
737             layout.menu("MASK_MT_add")
738             layout.menu("MASK_MT_mask")
739
740
741 # -----------------------------------------------------------------------------
742 # Mask (similar code in space_clip.py, keep in sync)
743 # note! - panel placement does _not_ fit well with image panels... need to fix.
744
745 from .properties_mask_common import (
746     MASK_PT_mask,
747     MASK_PT_layers,
748     MASK_PT_spline,
749     MASK_PT_point,
750     MASK_PT_display,
751 )
752
753
754 class IMAGE_PT_mask(MASK_PT_mask, Panel):
755     bl_space_type = 'IMAGE_EDITOR'
756     bl_region_type = 'UI'
757     bl_category = "Image"
758
759
760 class IMAGE_PT_mask_layers(MASK_PT_layers, Panel):
761     bl_space_type = 'IMAGE_EDITOR'
762     bl_region_type = 'UI'
763     bl_category = "Image"
764
765
766 class IMAGE_PT_mask_display(MASK_PT_display, Panel):
767     bl_space_type = 'IMAGE_EDITOR'
768     bl_region_type = 'UI'
769     bl_category = "Image"
770
771
772 class IMAGE_PT_active_mask_spline(MASK_PT_spline, Panel):
773     bl_space_type = 'IMAGE_EDITOR'
774     bl_region_type = 'UI'
775     bl_category = "Image"
776
777
778 class IMAGE_PT_active_mask_point(MASK_PT_point, Panel):
779     bl_space_type = 'IMAGE_EDITOR'
780     bl_region_type = 'UI'
781     bl_category = "Image"
782
783
784 # --- end mask ---
785
786
787 class IMAGE_PT_image_properties(Panel):
788     bl_space_type = 'IMAGE_EDITOR'
789     bl_region_type = 'UI'
790     bl_category = "Image"
791     bl_label = "Image"
792
793     @classmethod
794     def poll(cls, context):
795         sima = context.space_data
796         return (sima.image)
797
798     def draw(self, context):
799         layout = self.layout
800
801         sima = context.space_data
802         iuser = sima.image_user
803
804         layout.template_image(sima, "image", iuser, multiview=True)
805
806
807 class IMAGE_PT_view_display(Panel):
808     bl_space_type = 'IMAGE_EDITOR'
809     bl_region_type = 'UI'
810     bl_label = "Display"
811     bl_category = "View"
812
813     @classmethod
814     def poll(cls, context):
815         sima = context.space_data
816         return (sima and (sima.image or sima.show_uvedit))
817
818     def draw(self, context):
819         layout = self.layout
820         layout.use_property_split = True
821
822         sima = context.space_data
823         ima = sima.image
824
825         show_uvedit = sima.show_uvedit
826         uvedit = sima.uv_editor
827
828         col = layout.column()
829
830         if ima:
831             col.prop(ima, "display_aspect", text="Aspect Ratio")
832             col.prop(sima, "show_repeat", text="Repeat Image")
833
834         if show_uvedit:
835             col.prop(uvedit, "show_pixel_coords", text="Pixel Coordinates")
836
837
838 class IMAGE_PT_view_display_uv_edit_overlays(Panel):
839     bl_space_type = 'IMAGE_EDITOR'
840     bl_region_type = 'UI'
841     bl_label = "Overlays"
842     bl_parent_id = 'IMAGE_PT_view_display'
843     bl_category = "View"
844     bl_options = {'DEFAULT_CLOSED'}
845
846     @classmethod
847     def poll(cls, context):
848         sima = context.space_data
849         return (sima and (sima.show_uvedit))
850
851     def draw(self, context):
852         layout = self.layout
853         layout.use_property_split = True
854         layout.use_property_decorate = False
855
856         sima = context.space_data
857         uvedit = sima.uv_editor
858
859         col = layout.column()
860
861         col.prop(uvedit, "edge_display_type", text="Display As")
862         col.prop(uvedit, "show_edges", text="Edges")
863         col.prop(uvedit, "show_faces", text="Faces")
864
865         col = layout.column()
866         col.prop(uvedit, "show_smooth_edges", text="Smooth")
867         col.prop(uvedit, "show_modified_edges", text="Modified")
868
869
870 class IMAGE_PT_view_display_uv_edit_overlays_stretch(Panel):
871     bl_space_type = 'IMAGE_EDITOR'
872     bl_region_type = 'UI'
873     bl_label = "Stretching"
874     bl_parent_id = 'IMAGE_PT_view_display_uv_edit_overlays'
875     bl_category = "View"
876     bl_options = {'DEFAULT_CLOSED'}
877
878     @classmethod
879     def poll(cls, context):
880         sima = context.space_data
881         return (sima and (sima.show_uvedit))
882
883     def draw_header(self, context):
884         sima = context.space_data
885         uvedit = sima.uv_editor
886         self.layout.prop(uvedit, "show_stretch", text="")
887
888     def draw(self, context):
889         layout = self.layout
890         layout.use_property_split = True
891
892         sima = context.space_data
893         uvedit = sima.uv_editor
894
895         layout.active = uvedit.show_stretch
896         layout.prop(uvedit, "display_stretch_type", text="Type")
897
898
899 class IMAGE_UL_render_slots(UIList):
900     def draw_item(self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index):
901         slot = item
902         layout.prop(slot, "name", text="", emboss=False)
903
904
905 class IMAGE_PT_render_slots(Panel):
906     bl_space_type = 'IMAGE_EDITOR'
907     bl_region_type = 'UI'
908     bl_category = "Image"
909     bl_label = "Render Slots"
910
911     @classmethod
912     def poll(cls, context):
913         sima = context.space_data
914         return (sima and sima.image and sima.show_render)
915
916     def draw(self, context):
917         layout = self.layout
918
919         sima = context.space_data
920         ima = sima.image
921
922         row = layout.row()
923
924         col = row.column()
925         col.template_list(
926             "IMAGE_UL_render_slots", "render_slots", ima,
927             "render_slots", ima.render_slots, "active_index", rows=3
928         )
929
930         col = row.column(align=True)
931         col.operator("image.add_render_slot", icon='ADD', text="")
932         col.operator("image.remove_render_slot", icon='REMOVE', text="")
933
934         col.separator()
935
936         col.operator("image.clear_render_slot", icon='X', text="")
937
938
939 class IMAGE_PT_paint(Panel, ImagePaintPanel):
940     bl_label = "Brush"
941     bl_context = ".paint_common_2d"
942     bl_category = "Tool"
943
944     def draw(self, context):
945         layout = self.layout
946
947         layout.use_property_split = True
948         layout.use_property_decorate = False
949
950         settings = context.tool_settings.image_paint
951         brush = settings.brush
952
953         col = layout.column()
954         col.template_ID_preview(settings, "brush", new="brush.add", rows=2, cols=6)
955
956         if brush:
957             brush_texpaint_common(self, context, layout, brush, settings)
958
959
960 class IMAGE_PT_paint_color(Panel, ImagePaintPanel):
961     bl_category = "Tool"
962     bl_context = ".paint_common_2d"
963     bl_parent_id = "IMAGE_PT_paint"
964     bl_label = "Color Picker"
965
966     @classmethod
967     def poll(cls, context):
968         settings = context.tool_settings.image_paint
969         brush = settings.brush
970         capabilities = brush.image_paint_capabilities
971
972         return capabilities.has_color
973
974     def draw(self, context):
975         layout = self.layout
976         settings = context.tool_settings.image_paint
977         brush = settings.brush
978
979         layout.active = not brush.use_gradient
980
981         brush_texpaint_common_color(self, context, layout, brush, settings, True)
982
983
984 class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel):
985     bl_category = "Tool"
986     bl_context = ".paint_common_2d"
987     bl_parent_id = "IMAGE_PT_paint"
988     bl_label = "Color Palette"
989     bl_options = {'DEFAULT_CLOSED'}
990
991     @classmethod
992     def poll(cls, context):
993         settings = context.tool_settings.image_paint
994         brush = settings.brush
995         capabilities = brush.image_paint_capabilities
996
997         return capabilities.has_color
998
999     def draw(self, context):
1000         layout = self.layout
1001         settings = context.tool_settings.image_paint
1002
1003         layout.template_ID(settings, "palette", new="palette.new")
1004         if settings.palette:
1005             layout.template_palette(settings, "palette", color=True)
1006
1007
1008 class IMAGE_PT_paint_gradient(Panel, ImagePaintPanel):
1009     bl_category = "Tool"
1010     bl_context = ".paint_common_2d"
1011     bl_parent_id = "IMAGE_PT_paint"
1012     bl_label = "Gradient"
1013     bl_options = {'DEFAULT_CLOSED'}
1014
1015     @classmethod
1016     def poll(cls, context):
1017         settings = context.tool_settings.image_paint
1018         brush = settings.brush
1019         capabilities = brush.image_paint_capabilities
1020
1021         return capabilities.has_color
1022
1023     def draw_header(self, context):
1024         settings = context.tool_settings.image_paint
1025         brush = settings.brush
1026         self.layout.prop(brush, "use_gradient", text="")
1027
1028     def draw(self, context):
1029         layout = self.layout
1030         layout.use_property_split = False
1031         layout.use_property_decorate = False  # No animation.
1032         settings = context.tool_settings.image_paint
1033         brush = settings.brush
1034
1035         layout.active = brush.use_gradient
1036
1037         brush_texpaint_common_gradient(self, context, layout, brush, settings, True)
1038
1039
1040 class IMAGE_PT_paint_clone(Panel, ImagePaintPanel):
1041     bl_category = "Tool"
1042     bl_context = ".paint_common_2d"
1043     bl_parent_id = "IMAGE_PT_paint"
1044     bl_label = "Clone from Image/UV Map"
1045     bl_options = {'DEFAULT_CLOSED'}
1046
1047     @classmethod
1048     def poll(cls, context):
1049         settings = context.tool_settings.image_paint
1050         brush = settings.brush
1051
1052         return brush.image_tool == 'CLONE'
1053
1054     def draw_header(self, context):
1055         settings = context.tool_settings.image_paint
1056         self.layout.prop(settings, "use_clone_layer", text="")
1057
1058     def draw(self, context):
1059         layout = self.layout
1060         settings = context.tool_settings.image_paint
1061         brush = settings.brush
1062
1063         layout.active = settings.use_clone_layer
1064
1065         brush_texpaint_common_clone(self, context, layout, brush, settings, True)
1066
1067
1068 class IMAGE_PT_paint_options(Panel, ImagePaintPanel):
1069     bl_category = "Tool"
1070     bl_context = ".paint_common_2d"
1071     bl_parent_id = "IMAGE_PT_paint"
1072     bl_label = "Options"
1073     bl_options = {'DEFAULT_CLOSED'}
1074
1075     @classmethod
1076     def poll(cls, context):
1077         settings = context.tool_settings.image_paint
1078         brush = settings.brush
1079         capabilities = brush.image_paint_capabilities
1080
1081         return capabilities.has_color
1082
1083     def draw(self, context):
1084         layout = self.layout
1085         settings = context.tool_settings.image_paint
1086         brush = settings.brush
1087
1088         layout.use_property_split = True
1089         layout.use_property_decorate = False  # No animation.
1090
1091         brush_texpaint_common_options(self, context, layout, brush, settings, True)
1092
1093
1094 class IMAGE_PT_tools_brush_display(BrushButtonsPanel, Panel):
1095     bl_label = "Display"
1096     bl_context = ".paint_common_2d"
1097     bl_options = {'DEFAULT_CLOSED'}
1098     bl_category = "Tool"
1099
1100     def draw(self, context):
1101         layout = self.layout
1102
1103         layout.use_property_split = True
1104         layout.use_property_decorate = False
1105
1106         tool_settings = context.tool_settings.image_paint
1107         brush = tool_settings.brush
1108         tex_slot = brush.texture_slot
1109         tex_slot_mask = brush.mask_texture_slot
1110
1111         col = layout.column()
1112
1113         row = col.row(align=True)
1114
1115         sub = row.row(align=True)
1116         sub.prop(brush, "cursor_overlay_alpha", text="Curve Alpha")
1117         sub.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA')
1118         row.prop(
1119             brush, "use_cursor_overlay", text="", toggle=True,
1120             icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON',
1121         )
1122
1123         col.active = brush.brush_capabilities.has_overlay
1124
1125         row = col.row(align=True)
1126
1127         sub = row.row(align=True)
1128         sub.prop(brush, "texture_overlay_alpha", text="Texture Alpha")
1129         sub.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA')
1130         if tex_slot.map_mode != 'STENCIL':
1131             row.prop(
1132                 brush, "use_primary_overlay", text="", toggle=True,
1133                 icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON',
1134             )
1135
1136         row = col.row(align=True)
1137
1138         sub = row.row(align=True)
1139         sub.prop(brush, "mask_overlay_alpha", text="Mask Texture Alpha")
1140         sub.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA')
1141         if tex_slot_mask.map_mode != 'STENCIL':
1142             row.prop(
1143                 brush, "use_secondary_overlay", text="", toggle=True,
1144                 icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON',
1145             )
1146
1147
1148 class IMAGE_PT_tools_brush_display_show_brush(BrushButtonsPanel, Panel):
1149     bl_context = ".paint_common_2d"  # dot on purpose (access from topbar)
1150     bl_label = "Show Brush"
1151     bl_parent_id = "IMAGE_PT_tools_brush_display"
1152     bl_category = "Tool"
1153     bl_options = {'DEFAULT_CLOSED'}
1154
1155     def draw_header(self, context):
1156         settings = context.tool_settings.image_paint
1157
1158         self.layout.prop(settings, "show_brush", text="")
1159
1160     def draw(self, context):
1161         layout = self.layout
1162
1163         layout.use_property_split = True
1164         layout.use_property_decorate = False
1165
1166         settings = context.tool_settings.image_paint
1167         brush = settings.brush
1168
1169         col = layout.column()
1170         col.active = settings.show_brush
1171
1172         if context.sculpt_object and context.tool_settings.sculpt:
1173             if brush.sculpt_capabilities.has_secondary_color:
1174                 col.prop(brush, "cursor_color_add", text="Add")
1175                 col.prop(brush, "cursor_color_subtract", text="Subtract")
1176             else:
1177                 col.prop(brush, "cursor_color_add", text="Color")
1178         else:
1179             col.prop(brush, "cursor_color_add", text="Color")
1180
1181
1182 class IMAGE_PT_tools_brush_display_custom_icon(BrushButtonsPanel, Panel):
1183     bl_context = ".paint_common_2d"  # dot on purpose (access from topbar)
1184     bl_label = "Custom Icon"
1185     bl_parent_id = "IMAGE_PT_tools_brush_display"
1186     bl_category = "Tool"
1187     bl_options = {'DEFAULT_CLOSED'}
1188
1189     def draw_header(self, context):
1190         settings = context.tool_settings.image_paint
1191         brush = settings.brush
1192
1193         self.layout.prop(brush, "use_custom_icon", text="")
1194
1195     def draw(self, context):
1196         layout = self.layout
1197
1198         layout.use_property_split = True
1199         layout.use_property_decorate = False
1200
1201         settings = context.tool_settings.image_paint
1202         brush = settings.brush
1203
1204         col = layout.column()
1205         col.active = brush.use_custom_icon
1206         col.prop(brush, "icon_filepath", text="")
1207
1208
1209 class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel):
1210     bl_label = "Texture"
1211     bl_context = ".paint_common_2d"
1212     bl_category = "Tool"
1213     bl_options = {'DEFAULT_CLOSED'}
1214
1215     def draw(self, context):
1216         layout = self.layout
1217
1218         tool_settings = context.tool_settings.image_paint
1219         brush = tool_settings.brush
1220
1221         col = layout.column()
1222         col.template_ID_preview(brush, "texture", new="texture.new", rows=3, cols=8)
1223
1224         brush_texture_settings(col, brush, 0)
1225
1226
1227 class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel):
1228     bl_label = "Texture Mask"
1229     bl_context = ".paint_common_2d"
1230     bl_category = "Tool"
1231     bl_options = {'DEFAULT_CLOSED'}
1232
1233     def draw(self, context):
1234         layout = self.layout
1235
1236         brush = context.tool_settings.image_paint.brush
1237
1238         col = layout.column()
1239
1240         col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8)
1241
1242         brush_mask_texture_settings(col, brush)
1243
1244
1245 class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel):
1246     bl_label = "Stroke"
1247     bl_context = ".paint_common_2d"
1248     bl_category = "Tool"
1249     bl_options = {'DEFAULT_CLOSED'}
1250
1251     def draw(self, context):
1252         layout = self.layout
1253
1254         tool_settings = context.tool_settings.image_paint
1255         brush = tool_settings.brush
1256
1257         layout.use_property_split = True
1258         layout.use_property_decorate = False
1259
1260         col = layout.column()
1261
1262         col.prop(brush, "stroke_method")
1263
1264         if brush.use_anchor:
1265             col.prop(brush, "use_edge_to_edge", text="Edge To Edge")
1266
1267         if brush.use_airbrush:
1268             col.prop(brush, "rate", text="Rate", slider=True)
1269
1270         if brush.use_space:
1271             row = col.row(align=True)
1272             row.prop(brush, "spacing", text="Spacing")
1273             row.prop(brush, "use_pressure_spacing", toggle=True, text="")
1274
1275         if brush.use_line or brush.use_curve:
1276             row = col.row(align=True)
1277             row.prop(brush, "spacing", text="Spacing")
1278
1279         if brush.use_curve:
1280             col.template_ID(brush, "paint_curve", new="paintcurve.new")
1281             col.operator("paintcurve.draw")
1282
1283         row = col.row(align=True)
1284         if brush.use_relative_jitter:
1285             row.prop(brush, "jitter", slider=True)
1286         else:
1287             row.prop(brush, "jitter_absolute")
1288         row.prop(brush, "use_relative_jitter", icon_only=True)
1289         row.prop(brush, "use_pressure_jitter", toggle=True, text="")
1290
1291         col.prop(tool_settings, "input_samples")
1292
1293
1294 class IMAGE_PT_paint_stroke_smooth_stroke(BrushButtonsPanel, Panel):
1295     bl_context = ".paint_common_2d"  # dot on purpose (access from topbar)
1296     bl_label = "Smooth Stroke"
1297     bl_parent_id = "IMAGE_PT_paint_stroke"
1298     bl_category = "Tool"
1299     bl_options = {'DEFAULT_CLOSED'}
1300
1301     @classmethod
1302     def poll(cls, context):
1303         settings = context.tool_settings.image_paint
1304         brush = settings.brush
1305         if brush.brush_capabilities.has_smooth_stroke:
1306             return True
1307
1308     def draw_header(self, context):
1309         settings = context.tool_settings.image_paint
1310         brush = settings.brush
1311
1312         self.layout.prop(brush, "use_smooth_stroke", text="")
1313
1314     def draw(self, context):
1315         layout = self.layout
1316         layout.use_property_split = True
1317         layout.use_property_decorate = False
1318
1319         settings = context.tool_settings.image_paint
1320         brush = settings.brush
1321
1322         col = layout.column()
1323         col.active = brush.use_smooth_stroke
1324         col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
1325         col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
1326
1327
1328 class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel):
1329     bl_label = "Falloff"
1330     bl_context = ".paint_common_2d"
1331     bl_category = "Tool"
1332     bl_options = {'DEFAULT_CLOSED'}
1333
1334     def draw(self, context):
1335         layout = self.layout
1336
1337         tool_settings = context.tool_settings.image_paint
1338         brush = tool_settings.brush
1339
1340         layout.template_curve_mapping(brush, "curve")
1341
1342         col = layout.column(align=True)
1343         row = col.row(align=True)
1344         row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
1345         row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
1346         row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
1347         row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
1348         row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
1349         row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
1350
1351
1352 class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel):
1353     bl_context = ".imagepaint_2d"
1354     bl_label = "Tiling"
1355     bl_category = "Tool"
1356     bl_options = {'DEFAULT_CLOSED'}
1357
1358     def draw(self, context):
1359         layout = self.layout
1360
1361         tool_settings = context.tool_settings
1362         ipaint = tool_settings.image_paint
1363
1364         col = layout.column(align=True)
1365         row = col.row(align=True)
1366         row.prop(ipaint, "tile_x", text="X", toggle=True)
1367         row.prop(ipaint, "tile_y", text="Y", toggle=True)
1368
1369
1370 class IMAGE_PT_uv_sculpt_brush(Panel):
1371     bl_space_type = 'IMAGE_EDITOR'
1372     bl_region_type = 'UI'
1373     bl_context = ".uv_sculpt"  # dot on purpose (access from topbar)
1374     bl_category = "Tool"
1375     bl_label = "Brush"
1376
1377     @classmethod
1378     def poll(cls, context):
1379         sima = context.space_data
1380         # TODO(campbell): nicer way to check if we're in uv sculpt mode.
1381         if sima and sima.show_uvedit:
1382             from .space_toolsystem_common import ToolSelectPanelHelper
1383             tool = ToolSelectPanelHelper.tool_active_from_context(context)
1384             if tool.has_datablock:
1385                 return True
1386         return False
1387
1388     def draw(self, context):
1389         from .properties_paint_common import UnifiedPaintPanel
1390         layout = self.layout
1391
1392         tool_settings = context.tool_settings
1393         uvsculpt = tool_settings.uv_sculpt
1394
1395         layout.template_ID(uvsculpt, "brush")
1396
1397         brush = uvsculpt.brush
1398
1399         if not self.is_popover:
1400             if brush:
1401                 col = layout.column()
1402
1403                 row = col.row(align=True)
1404                 UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True)
1405                 UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size", text="")
1406
1407                 row = col.row(align=True)
1408                 UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True)
1409                 UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength", text="")
1410
1411         col = layout.column()
1412         col.prop(tool_settings, "uv_sculpt_lock_borders")
1413         col.prop(tool_settings, "uv_sculpt_all_islands")
1414
1415         if brush:
1416             if brush.uv_sculpt_tool == 'RELAX':
1417                 col.prop(tool_settings, "uv_relax_method")
1418
1419         col.prop(uvsculpt, "show_brush")
1420
1421
1422 class IMAGE_PT_uv_sculpt_curve(Panel):
1423     bl_space_type = 'IMAGE_EDITOR'
1424     bl_region_type = 'UI'
1425     bl_context = ".uv_sculpt"  # dot on purpose (access from topbar)
1426     bl_category = "Tool"
1427     bl_label = "Falloff"
1428     bl_options = {'DEFAULT_CLOSED'}
1429
1430     poll = IMAGE_PT_uv_sculpt_brush.poll
1431
1432     def draw(self, context):
1433         layout = self.layout
1434
1435         tool_settings = context.tool_settings
1436         uvsculpt = tool_settings.uv_sculpt
1437         brush = uvsculpt.brush
1438
1439         if brush is not None:
1440             layout.template_curve_mapping(brush, "curve")
1441
1442             row = layout.row(align=True)
1443             row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
1444             row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
1445             row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
1446             row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
1447             row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
1448             row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
1449
1450
1451
1452 class ImageScopesPanel:
1453     @classmethod
1454     def poll(cls, context):
1455         sima = context.space_data
1456
1457         if not (sima and sima.image):
1458             return False
1459
1460         # scopes are not updated in paint modes, hide.
1461         if sima.mode == 'PAINT':
1462             return False
1463
1464         ob = context.active_object
1465         if ob and ob.mode in {'TEXTURE_PAINT', 'EDIT'}:
1466             return False
1467
1468         return True
1469
1470
1471 class IMAGE_PT_view_histogram(ImageScopesPanel, Panel):
1472     bl_space_type = 'IMAGE_EDITOR'
1473     bl_region_type = 'UI'
1474     bl_category = "Scopes"
1475     bl_label = "Histogram"
1476
1477     def draw(self, context):
1478         layout = self.layout
1479
1480         sima = context.space_data
1481         hist = sima.scopes.histogram
1482
1483         layout.template_histogram(sima.scopes, "histogram")
1484
1485         row = layout.row(align=True)
1486         row.prop(hist, "mode", expand=True)
1487         row.prop(hist, "show_line", text="")
1488
1489
1490 class IMAGE_PT_view_waveform(ImageScopesPanel, Panel):
1491     bl_space_type = 'IMAGE_EDITOR'
1492     bl_region_type = 'UI'
1493     bl_category = "Scopes"
1494     bl_label = "Waveform"
1495     bl_options = {'DEFAULT_CLOSED'}
1496
1497     def draw(self, context):
1498         layout = self.layout
1499
1500         sima = context.space_data
1501
1502         layout.template_waveform(sima, "scopes")
1503         row = layout.split(factor=0.75)
1504         row.prop(sima.scopes, "waveform_alpha")
1505         row.prop(sima.scopes, "waveform_mode", text="")
1506
1507
1508 class IMAGE_PT_view_vectorscope(ImageScopesPanel, Panel):
1509     bl_space_type = 'IMAGE_EDITOR'
1510     bl_region_type = 'UI'
1511     bl_category = "Scopes"
1512     bl_label = "Vectorscope"
1513     bl_options = {'DEFAULT_CLOSED'}
1514
1515     def draw(self, context):
1516         layout = self.layout
1517
1518         sima = context.space_data
1519         layout.template_vectorscope(sima, "scopes")
1520         layout.prop(sima.scopes, "vectorscope_alpha")
1521
1522
1523 class IMAGE_PT_sample_line(ImageScopesPanel, Panel):
1524     bl_space_type = 'IMAGE_EDITOR'
1525     bl_region_type = 'UI'
1526     bl_category = "Scopes"
1527     bl_label = "Sample Line"
1528     bl_options = {'DEFAULT_CLOSED'}
1529
1530     def draw(self, context):
1531         layout = self.layout
1532
1533         sima = context.space_data
1534         hist = sima.sample_histogram
1535
1536         layout.operator("image.sample_line")
1537         layout.template_histogram(sima, "sample_histogram")
1538
1539         row = layout.row(align=True)
1540         row.prop(hist, "mode", expand=True)
1541         row.prop(hist, "show_line", text="")
1542
1543
1544 class IMAGE_PT_scope_sample(ImageScopesPanel, Panel):
1545     bl_space_type = 'IMAGE_EDITOR'
1546     bl_region_type = 'UI'
1547     bl_category = "Scopes"
1548     bl_label = "Samples"
1549     bl_options = {'DEFAULT_CLOSED'}
1550
1551     def draw(self, context):
1552         layout = self.layout
1553         layout.use_property_split = True
1554         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
1555
1556         sima = context.space_data
1557
1558         col = flow.column()
1559         col.prop(sima.scopes, "use_full_resolution")
1560
1561         col = flow.column()
1562         col.active = not sima.scopes.use_full_resolution
1563         col.prop(sima.scopes, "accuracy")
1564
1565
1566 class IMAGE_PT_uv_cursor(Panel):
1567     bl_space_type = 'IMAGE_EDITOR'
1568     bl_region_type = 'UI'
1569     bl_category = "View"
1570     bl_label = "2D Cursor"
1571
1572     @classmethod
1573     def poll(cls, context):
1574         sima = context.space_data
1575
1576         return (sima and (sima.show_uvedit or sima.show_maskedit))
1577
1578     def draw(self, context):
1579         layout = self.layout
1580
1581         sima = context.space_data
1582
1583         col = layout.column()
1584
1585         col = layout.column()
1586         col.prop(sima, "cursor_location", text="Cursor Location")
1587
1588
1589 # Grease Pencil properties
1590 class IMAGE_PT_grease_pencil(AnnotationDataPanel, Panel):
1591     bl_space_type = 'IMAGE_EDITOR'
1592     bl_region_type = 'UI'
1593     bl_category = "View"
1594
1595     # NOTE: this is just a wrapper around the generic GP Panel.
1596
1597 # Grease Pencil drawing tools.
1598
1599
1600 classes = (
1601     IMAGE_MT_view,
1602     IMAGE_MT_view_zoom,
1603     IMAGE_MT_select,
1604     IMAGE_MT_brush,
1605     IMAGE_MT_image,
1606     IMAGE_MT_image_invert,
1607     IMAGE_MT_uvs,
1608     IMAGE_MT_uvs_showhide,
1609     IMAGE_MT_uvs_transform,
1610     IMAGE_MT_uvs_snap,
1611     IMAGE_MT_uvs_mirror,
1612     IMAGE_MT_uvs_weldalign,
1613     IMAGE_MT_uvs_select_mode,
1614     IMAGE_MT_uvs_context_menu,
1615     IMAGE_MT_pivot_pie,
1616     IMAGE_MT_uvs_snap_pie,
1617     IMAGE_HT_tool_header,
1618     IMAGE_HT_header,
1619     MASK_MT_editor_menus,
1620     IMAGE_PT_active_tool,
1621     IMAGE_PT_mask,
1622     IMAGE_PT_mask_layers,
1623     IMAGE_PT_mask_display,
1624     IMAGE_PT_active_mask_spline,
1625     IMAGE_PT_active_mask_point,
1626     IMAGE_PT_image_properties,
1627     IMAGE_UL_render_slots,
1628     IMAGE_PT_render_slots,
1629     IMAGE_PT_view_display,
1630     IMAGE_PT_view_display_uv_edit_overlays,
1631     IMAGE_PT_view_display_uv_edit_overlays_stretch,
1632     IMAGE_PT_paint,
1633     IMAGE_PT_paint_color,
1634     IMAGE_PT_paint_swatches,
1635     IMAGE_PT_paint_gradient,
1636     IMAGE_PT_paint_clone,
1637     IMAGE_PT_paint_options,
1638     IMAGE_PT_tools_brush_texture,
1639     IMAGE_PT_tools_mask_texture,
1640     IMAGE_PT_paint_stroke,
1641     IMAGE_PT_paint_stroke_smooth_stroke,
1642     IMAGE_PT_paint_curve,
1643     IMAGE_PT_tools_brush_display,
1644     IMAGE_PT_tools_brush_display_show_brush,
1645     IMAGE_PT_tools_brush_display_custom_icon,
1646     IMAGE_PT_tools_imagepaint_symmetry,
1647     IMAGE_PT_uv_sculpt_brush,
1648     IMAGE_PT_uv_sculpt_curve,
1649     IMAGE_PT_view_histogram,
1650     IMAGE_PT_view_waveform,
1651     IMAGE_PT_view_vectorscope,
1652     IMAGE_PT_sample_line,
1653     IMAGE_PT_scope_sample,
1654     IMAGE_PT_uv_cursor,
1655     IMAGE_PT_grease_pencil,
1656 )
1657
1658
1659 if __name__ == "__main__":  # only for live edit.
1660     from bpy.utils import register_class
1661     for cls in classes:
1662         register_class(cls)