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