Merge branch 'master' into blender2.8
[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 import bpy
22 import math
23 from bpy.types import (
24     Header,
25     Menu,
26     Panel,
27     UIList,
28 )
29 from .properties_paint_common import (
30     UnifiedPaintPanel,
31     brush_texture_settings,
32     brush_texpaint_common,
33     brush_mask_texture_settings,
34 )
35 from .properties_grease_pencil_common import (
36     AnnotationDataPanel,
37 )
38 from bpy.app.translations import pgettext_iface as iface_
39
40
41 class ImagePaintPanel(UnifiedPaintPanel):
42     bl_space_type = 'PROPERTIES'
43     bl_region_type = 'WINDOW'
44
45
46 class BrushButtonsPanel(UnifiedPaintPanel):
47     bl_space_type = 'PROPERTIES'
48     bl_region_type = 'WINDOW'
49
50     @classmethod
51     def poll(cls, context):
52         tool_settings = context.tool_settings.image_paint
53         return tool_settings.brush
54
55
56 class IMAGE_MT_view(Menu):
57     bl_label = "View"
58
59     def draw(self, context):
60         layout = self.layout
61
62         sima = context.space_data
63         uv = sima.uv_editor
64         tool_settings = context.tool_settings
65         paint = tool_settings.image_paint
66
67         show_uvedit = sima.show_uvedit
68         show_render = sima.show_render
69
70         layout.operator("image.properties", icon='MENU_PANEL')
71         layout.operator("image.toolshelf", icon='MENU_PANEL')
72
73         layout.separator()
74
75         layout.prop(sima, "use_realtime_update")
76         if show_uvedit:
77             layout.prop(tool_settings, "show_uv_local_view")
78
79         layout.prop(uv, "show_metadata")
80
81         if paint.brush and (context.image_paint_object or sima.mode == 'PAINT'):
82             layout.prop(uv, "show_texpaint")
83             layout.prop(tool_settings, "show_uv_local_view", text="Show Same Material")
84
85         layout.separator()
86
87         layout.operator("image.view_zoom_in")
88         layout.operator("image.view_zoom_out")
89
90         layout.separator()
91
92         layout.menu("IMAGE_MT_view_zoom")
93
94         layout.separator()
95
96         if show_uvedit:
97             layout.operator("image.view_selected")
98
99         layout.operator("image.view_all")
100         layout.operator("image.view_all", text="View Fit").fit_view = True
101
102         layout.separator()
103
104         if show_render:
105             layout.operator("image.render_border")
106             layout.operator("image.clear_render_border")
107
108             layout.separator()
109
110             layout.operator("image.cycle_render_slot", text="Render Slot Cycle Next")
111             layout.operator("image.cycle_render_slot", text="Render Slot Cycle Previous").reverse = True
112             layout.separator()
113
114         layout.menu("INFO_MT_area")
115
116
117 class IMAGE_MT_view_zoom(Menu):
118     bl_label = "Fractional Zoom"
119
120     def draw(self, context):
121         layout = self.layout
122
123         ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
124
125         for i, (a, b) in enumerate(ratios):
126             if i in {3, 4}:  # Draw separators around Zoom 1:1.
127                 layout.separator()
128
129             layout.operator(
130                 "image.view_zoom_ratio",
131                 text=iface_(f"Zoom {a:d}:{b:d}"),
132                 translate=False,
133             ).ratio = a / b
134
135
136 class IMAGE_MT_select(Menu):
137     bl_label = "Select"
138
139     def draw(self, context):
140         layout = self.layout
141
142         layout.operator("uv.select_all", text="All").action = 'SELECT'
143         layout.operator("uv.select_all", text="None").action = 'DESELECT'
144         layout.operator("uv.select_all", text="Invert").action = 'INVERT'
145
146         layout.separator()
147
148         layout.operator("uv.select_box").pinned = False
149         layout.operator("uv.select_box", text="Box Select Pinned").pinned = True
150         layout.operator("uv.select_circle")
151
152         layout.separator()
153
154         layout.operator("uv.select_less", text="Less")
155         layout.operator("uv.select_more", text="More")
156
157         layout.separator()
158
159         layout.operator("uv.select_pinned")
160         layout.operator("uv.select_linked").extend = False
161
162         layout.separator()
163
164         layout.operator("uv.select_split")
165
166
167 class IMAGE_MT_brush(Menu):
168     bl_label = "Brush"
169
170     def draw(self, context):
171         layout = self.layout
172         tool_settings = context.tool_settings
173         settings = tool_settings.image_paint
174         brush = settings.brush
175
176         ups = context.tool_settings.unified_paint_settings
177         layout.prop(ups, "use_unified_size", text="Unified Size")
178         layout.prop(ups, "use_unified_strength", text="Unified Strength")
179         layout.prop(ups, "use_unified_color", text="Unified Color")
180         layout.separator()
181
182         # Brush tool.
183         layout.prop_menu_enum(brush, "image_tool")
184
185
186 class IMAGE_MT_image(Menu):
187     bl_label = "Image"
188
189     def draw(self, context):
190         layout = self.layout
191
192         sima = context.space_data
193         ima = sima.image
194         show_render = sima.show_render
195
196         layout.operator("image.new", text="New")
197         layout.operator("image.open", text="Open...", icon='FILE_FOLDER')
198
199         layout.operator("image.read_viewlayers")
200
201         if ima:
202             layout.separator()
203
204             if not show_render:
205                 layout.operator("image.replace", text="Replace...")
206                 layout.operator("image.reload", text="Reload")
207
208             layout.operator("image.external_edit", text="Edit Externally")
209
210         layout.separator()
211
212         if ima:
213             layout.operator("image.save", text="Save", icon='FILE_TICK')
214             layout.operator("image.save_as", text="Save As...")
215             layout.operator("image.save_as", text="Save a Copy...").copy = True
216
217         if ima and ima.source == 'SEQUENCE':
218             layout.operator("image.save_sequence")
219
220         layout.operator("image.save_dirty", text="Save All Images")
221
222         if ima:
223             layout.separator()
224
225             layout.menu("IMAGE_MT_image_invert")
226
227             if not show_render:
228                 if not ima.packed_file:
229                     layout.separator()
230                     layout.operator("image.pack", text="Pack")
231
232                 # Only for dirty && specific image types, perhaps
233                 # this could be done in operator poll too.
234                 if ima.is_dirty:
235                     if ima.source in {'FILE', 'GENERATED'} and ima.type != 'OPEN_EXR_MULTILAYER':
236                         if ima.packed_file:
237                             layout.separator()
238                         layout.operator("image.pack", text="Pack As PNG").as_png = True
239
240
241 class IMAGE_MT_image_invert(Menu):
242     bl_label = "Invert"
243
244     def draw(self, context):
245         layout = self.layout
246
247         props = layout.operator("image.invert", text="Invert Image Colors", icon='IMAGE_RGB')
248         props.invert_r = True
249         props.invert_g = True
250         props.invert_b = True
251
252         layout.separator()
253
254         layout.operator("image.invert", text="Invert Red Channel", icon='COLOR_RED').invert_r = True
255         layout.operator("image.invert", text="Invert Green Channel", icon='COLOR_GREEN').invert_g = True
256         layout.operator("image.invert", text="Invert Blue Channel", icon='COLOR_BLUE').invert_b = True
257         layout.operator("image.invert", text="Invert Alpha Channel", icon='IMAGE_ALPHA').invert_a = True
258
259
260 class IMAGE_MT_uvs_showhide(Menu):
261     bl_label = "Show/Hide Faces"
262
263     def draw(self, context):
264         layout = self.layout
265
266         layout.operator("uv.reveal")
267         layout.operator("uv.hide", text="Hide Selected").unselected = False
268         layout.operator("uv.hide", text="Hide Unselected").unselected = True
269
270
271 class IMAGE_MT_uvs_proportional(Menu):
272     bl_label = "Proportional Editing"
273
274     def draw(self, context):
275         layout = self.layout
276
277         layout.props_enum(context.tool_settings, "proportional_edit")
278
279         layout.separator()
280
281         layout.label(text="Falloff:")
282         layout.props_enum(context.tool_settings, "proportional_edit_falloff")
283
284
285 class IMAGE_MT_uvs_transform(Menu):
286     bl_label = "Transform"
287
288     def draw(self, context):
289         layout = self.layout
290
291         layout.operator("transform.translate")
292         layout.operator("transform.rotate")
293         layout.operator("transform.resize")
294
295         layout.separator()
296
297         layout.operator("transform.shear")
298
299
300 class IMAGE_MT_uvs_snap(Menu):
301     bl_label = "Snap"
302
303     def draw(self, context):
304         layout = self.layout
305
306         layout.operator_context = 'EXEC_REGION_WIN'
307
308         layout.operator("uv.snap_selected", text="Selected to Pixels").target = 'PIXELS'
309         layout.operator("uv.snap_selected", text="Selected to Cursor").target = 'CURSOR'
310         layout.operator("uv.snap_selected", text="Selected to Cursor (Offset)").target = 'CURSOR_OFFSET'
311         layout.operator("uv.snap_selected", text="Selected to Adjacent Unselected").target = 'ADJACENT_UNSELECTED'
312
313         layout.separator()
314
315         layout.operator("uv.snap_cursor", text="Cursor to Pixels").target = 'PIXELS'
316         layout.operator("uv.snap_cursor", text="Cursor to Selected").target = 'SELECTED'
317
318
319 class IMAGE_MT_uvs_mirror(Menu):
320     bl_label = "Mirror"
321
322     def draw(self, context):
323         layout = self.layout
324
325         layout.operator("mesh.faces_mirror_uv")
326
327         layout.separator()
328
329         layout.operator_context = 'EXEC_REGION_WIN'
330
331         layout.operator("transform.mirror", text="X Axis").constraint_axis[0] = True
332         layout.operator("transform.mirror", text="Y Axis").constraint_axis[1] = True
333
334
335 class IMAGE_MT_uvs_weldalign(Menu):
336     bl_label = "Weld/Align"
337
338     def draw(self, context):
339         layout = self.layout
340
341         layout.operator("uv.weld")  # W, 1.
342         layout.operator("uv.remove_doubles")
343         layout.operator_enum("uv.align", "axis")  # W, 2/3/4.
344
345
346 class IMAGE_MT_uvs(Menu):
347     bl_label = "UV"
348
349     def draw(self, context):
350         layout = self.layout
351
352         sima = context.space_data
353         uv = sima.uv_editor
354         tool_settings = context.tool_settings
355
356         layout.prop(uv, "use_snap_to_pixels")
357         layout.prop(uv, "lock_bounds")
358
359         layout.separator()
360
361         layout.prop(tool_settings, "use_uv_sculpt")
362
363         layout.separator()
364
365         layout.prop(uv, "use_live_unwrap")
366         layout.operator("uv.unwrap")
367         layout.operator("uv.pin", text="Unpin").clear = True
368         layout.operator("uv.pin").clear = False
369
370         layout.separator()
371
372         layout.operator("uv.pack_islands")
373         layout.operator("uv.average_islands_scale")
374         layout.operator("uv.minimize_stretch")
375         layout.operator("uv.stitch")
376
377         layout.separator()
378
379         layout.operator("uv.mark_seam").clear = False
380         layout.operator("uv.mark_seam", text="Clear Seam").clear = True
381         layout.operator("uv.seams_from_islands")
382
383         layout.separator()
384
385         layout.menu("IMAGE_MT_uvs_transform")
386         layout.menu("IMAGE_MT_uvs_mirror")
387         layout.menu("IMAGE_MT_uvs_snap")
388         layout.menu("IMAGE_MT_uvs_weldalign")
389
390         layout.separator()
391
392         layout.menu("IMAGE_MT_uvs_proportional")
393
394         layout.separator()
395
396         layout.menu("IMAGE_MT_uvs_showhide")
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_specials(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             layout.operator("uv.unwrap")
452             layout.operator("uv.follow_active_quads")
453
454             layout.separator()
455
456             layout.operator("uv.pin").clear = False
457             layout.operator("uv.pin", text="Unpin").clear = True
458
459             layout.separator()
460
461             layout.operator("uv.weld")
462             layout.operator("uv.stitch")
463
464             layout.separator()
465
466             layout.operator_enum("uv.align", "axis")  # W, 2/3/4.
467
468             layout.separator()
469
470             layout.operator("transform.mirror", text="Mirror X").constraint_axis[0] = True
471             layout.operator("transform.mirror", text="Mirror Y").constraint_axis[1] = True
472
473             layout.separator()
474
475             layout.menu("IMAGE_MT_uvs_snap")
476
477
478 class IMAGE_MT_pivot_pie(Menu):
479     bl_label = "Pivot Point"
480
481     def draw(self, context):
482         layout = self.layout
483         pie = layout.menu_pie()
484
485         pie.prop_enum(context.space_data, "pivot_point", value='CENTER')
486         pie.prop_enum(context.space_data, "pivot_point", value='CURSOR')
487         pie.prop_enum(context.space_data, "pivot_point", value='INDIVIDUAL_ORIGINS')
488         pie.prop_enum(context.space_data, "pivot_point", value='MEDIAN')
489
490
491 class IMAGE_MT_uvs_snap_pie(Menu):
492     bl_label = "Snap"
493
494     def draw(self, context):
495         layout = self.layout
496         pie = layout.menu_pie()
497
498         layout.operator_context = 'EXEC_REGION_WIN'
499
500         pie.operator("uv.snap_selected", text="Selected to Pixels", icon='RESTRICT_SELECT_OFF').target = 'PIXELS'
501         pie.operator("uv.snap_cursor", text="Cursor to Pixels", icon='CURSOR').target = 'PIXELS'
502         pie.operator("uv.snap_cursor", text="Cursor to Selected", icon='CURSOR').target = 'SELECTED'
503         pie.operator("uv.snap_selected", text="Selected to Cursor", icon='RESTRICT_SELECT_OFF').target = 'CURSOR'
504         pie.operator("uv.snap_selected", text="Selected to Cursor (Offset)", icon='RESTRICT_SELECT_OFF').target = 'CURSOR_OFFSET'
505         pie.operator("uv.snap_selected", text="Selected to Adjacent Unselected", icon='RESTRICT_SELECT_OFF').target = 'ADJACENT_UNSELECTED'
506
507
508 class IMAGE_HT_header(Header):
509     bl_space_type = 'IMAGE_EDITOR'
510
511     def draw(self, context):
512         layout = self.layout
513
514         sima = context.space_data
515         ima = sima.image
516         iuser = sima.image_user
517         tool_settings = context.tool_settings
518
519         show_render = sima.show_render
520         show_uvedit = sima.show_uvedit
521         show_maskedit = sima.show_maskedit
522
523         row = layout.row(align=True)
524         row.template_header()
525
526         if sima.mode != 'UV':
527             layout.prop(sima, "ui_mode", text="")
528
529         # UV editing.
530         if show_uvedit:
531             uvedit = sima.uv_editor
532
533             layout.prop(tool_settings, "use_uv_select_sync", text="")
534
535             if tool_settings.use_uv_select_sync:
536                 layout.template_edit_mode_selection()
537             else:
538                 layout.prop(tool_settings, "uv_select_mode", text="", expand=True)
539                 layout.prop(uvedit, "sticky_select_mode", icon_only=True)
540
541         MASK_MT_editor_menus.draw_collapsible(context, layout)
542
543         layout.separator_spacer()
544
545         layout.template_ID(sima, "image", new="image.new", open="image.open")
546
547         if show_maskedit:
548             row = layout.row()
549             row.template_ID(sima, "mask", new="mask.new")
550
551         if not show_render:
552             layout.prop(sima, "use_image_pin", text="")
553
554         layout.separator_spacer()
555
556         if show_uvedit:
557             uvedit = sima.uv_editor
558
559             mesh = context.edit_object.data
560             layout.prop_search(mesh.uv_layers, "active", mesh, "uv_layers", text="")
561
562             # Snap.
563             row = layout.row(align=True)
564             row.prop(tool_settings, "use_snap", text="")
565             row.prop(tool_settings, "snap_uv_element", icon_only=True)
566             if tool_settings.snap_uv_element != 'INCREMENT':
567                 row.prop(tool_settings, "snap_target", text="")
568
569             row = layout.row(align=True)
570             row.prop(tool_settings, "proportional_edit", icon_only=True)
571             # if tool_settings.proportional_edit != 'DISABLED':
572             sub = row.row(align=True)
573             sub.active = tool_settings.proportional_edit != 'DISABLED'
574             sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
575
576         if show_uvedit or show_maskedit:
577             layout.prop(sima, "pivot_point", icon_only=True)
578
579         row = layout.row()
580         row.popover(
581             panel="IMAGE_PT_view_display",
582             text="Display"
583         )
584
585         if ima:
586             if ima.is_stereo_3d:
587                 row = layout.row()
588                 row.prop(sima, "show_stereo_3d", text="")
589
590             # layers.
591             layout.template_image_layers(ima, iuser)
592
593             # draw options.
594             row = layout.row()
595             row.prop(sima, "display_channels", icon_only=True)
596
597             row = layout.row(align=True)
598             if ima.type == 'COMPOSITE':
599                 row.operator("image.record_composite", icon='REC')
600             if ima.type == 'COMPOSITE' and ima.source in {'MOVIE', 'SEQUENCE'}:
601                 row.operator("image.play_composite", icon='PLAY')
602
603
604 class MASK_MT_editor_menus(Menu):
605     bl_idname = "MASK_MT_editor_menus"
606     bl_label = ""
607
608     def draw(self, context):
609         self.draw_menus(self.layout, context)
610
611     @staticmethod
612     def draw_menus(layout, context):
613         sima = context.space_data
614         ima = sima.image
615
616         show_uvedit = sima.show_uvedit
617         show_maskedit = sima.show_maskedit
618         show_paint = sima.show_paint
619
620         layout.menu("IMAGE_MT_view")
621
622         if show_uvedit:
623             layout.menu("IMAGE_MT_select")
624         if show_maskedit:
625             layout.menu("MASK_MT_select")
626         if show_paint:
627             layout.menu("IMAGE_MT_brush")
628
629         if ima and ima.is_dirty:
630             layout.menu("IMAGE_MT_image", text="Image*")
631         else:
632             layout.menu("IMAGE_MT_image", text="Image")
633
634         if show_uvedit:
635             layout.menu("IMAGE_MT_uvs")
636         if show_maskedit:
637             layout.menu("MASK_MT_add")
638             layout.menu("MASK_MT_mask")
639
640
641 # -----------------------------------------------------------------------------
642 # Mask (similar code in space_clip.py, keep in sync)
643 # note! - panel placement does _not_ fit well with image panels... need to fix.
644
645 from .properties_mask_common import (
646     MASK_PT_mask,
647     MASK_PT_layers,
648     MASK_PT_spline,
649     MASK_PT_point,
650     MASK_PT_display,
651 )
652
653
654 class IMAGE_PT_mask(MASK_PT_mask, Panel):
655     bl_space_type = 'IMAGE_EDITOR'
656     bl_region_type = 'UI'
657     bl_category = "Image"
658
659
660 class IMAGE_PT_mask_layers(MASK_PT_layers, Panel):
661     bl_space_type = 'IMAGE_EDITOR'
662     bl_region_type = 'UI'
663     bl_category = "Image"
664
665
666 class IMAGE_PT_mask_display(MASK_PT_display, Panel):
667     bl_space_type = 'IMAGE_EDITOR'
668     bl_region_type = 'UI'
669     bl_category = "Image"
670
671
672 class IMAGE_PT_active_mask_spline(MASK_PT_spline, Panel):
673     bl_space_type = 'IMAGE_EDITOR'
674     bl_region_type = 'UI'
675     bl_category = "Image"
676
677
678 class IMAGE_PT_active_mask_point(MASK_PT_point, Panel):
679     bl_space_type = 'IMAGE_EDITOR'
680     bl_region_type = 'UI'
681     bl_category = "Image"
682
683
684 # --- end mask ---
685
686
687 class IMAGE_PT_image_properties(Panel):
688     bl_space_type = 'IMAGE_EDITOR'
689     bl_region_type = 'UI'
690     bl_category = "Image"
691     bl_label = "Image"
692
693     @classmethod
694     def poll(cls, context):
695         sima = context.space_data
696         return (sima.image)
697
698     def draw(self, context):
699         layout = self.layout
700
701         sima = context.space_data
702         iuser = sima.image_user
703
704         layout.template_image(sima, "image", iuser, multiview=True)
705
706
707 class IMAGE_PT_view_display(Panel):
708     bl_space_type = 'IMAGE_EDITOR'
709     bl_region_type = 'HEADER'
710     bl_label = "Display"
711
712     @classmethod
713     def poll(cls, context):
714         sima = context.space_data
715         return (sima and (sima.image or sima.show_uvedit))
716
717     def draw(self, context):
718         layout = self.layout
719         layout.use_property_split = True
720
721         sima = context.space_data
722         ima = sima.image
723
724         show_uvedit = sima.show_uvedit
725         show_maskedit = sima.show_maskedit
726         uvedit = sima.uv_editor
727
728         col = layout.column()
729
730         if ima:
731             col.prop(ima, "display_aspect", text="Aspect Ratio")
732             col.prop(sima, "show_repeat", text="Repeat Image")
733
734         if show_uvedit or show_maskedit:
735             col.separator()
736
737             col = layout.column()
738             col.prop(sima, "cursor_location", text="Cursor Location")
739
740         if show_uvedit:
741             col.prop(uvedit, "show_pixel_coords", text="Pixel Coordinates")
742
743
744 class IMAGE_PT_view_display_uv_edit_overlays(Panel):
745     bl_space_type = 'IMAGE_EDITOR'
746     bl_region_type = 'HEADER'
747     bl_label = "Overlays"
748     bl_parent_id = 'IMAGE_PT_view_display'
749     bl_options = {'DEFAULT_CLOSED'}
750
751     @classmethod
752     def poll(cls, context):
753         sima = context.space_data
754         return (sima and (sima.show_uvedit))
755
756     def draw(self, context):
757         layout = self.layout
758         layout.use_property_split = True
759
760         sima = context.space_data
761         uvedit = sima.uv_editor
762
763         col = layout.column()
764
765         split = col.split(factor=0.6)
766         split.prop(uvedit, "show_edges", text="Edges")
767         split.prop(uvedit, "edge_display_type", text="")
768
769         col.prop(uvedit, "show_faces", text="Faces")
770
771         col = layout.column()
772         col.prop(uvedit, "show_smooth_edges", text="Smooth")
773         col.prop(uvedit, "show_modified_edges", text="Modified")
774
775
776 class IMAGE_PT_view_display_uv_edit_overlays_advanced(Panel):
777     bl_space_type = 'IMAGE_EDITOR'
778     bl_region_type = 'HEADER'
779     bl_label = "Advanced"
780     bl_parent_id = 'IMAGE_PT_view_display_uv_edit_overlays'
781     bl_options = {'DEFAULT_CLOSED'}
782
783     @classmethod
784     def poll(cls, context):
785         sima = context.space_data
786         return (sima and (sima.show_uvedit))
787
788     def draw(self, context):
789         layout = self.layout
790         layout.use_property_split = True
791
792         sima = context.space_data
793         uvedit = sima.uv_editor
794
795         col = layout.column()
796         col.prop(uvedit, "show_stretch", text="Stretch")
797
798         sub = col.column()
799         sub.active = uvedit.show_stretch
800         sub.prop(uvedit, "display_stretch_type", text="Type")
801
802
803 class IMAGE_UL_render_slots(UIList):
804     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
805         slot = item
806         layout.prop(slot, "name", text="", emboss=False)
807
808
809 class IMAGE_PT_render_slots(Panel):
810     bl_space_type = 'IMAGE_EDITOR'
811     bl_region_type = 'UI'
812     bl_category = "Image"
813     bl_label = "Render Slots"
814
815     @classmethod
816     def poll(cls, context):
817         sima = context.space_data
818         return (sima and sima.image and sima.show_render)
819
820     def draw(self, context):
821         layout = self.layout
822
823         sima = context.space_data
824         ima = sima.image
825
826         row = layout.row()
827
828         col = row.column()
829         col.template_list(
830             "IMAGE_UL_render_slots", "render_slots", ima,
831             "render_slots", ima.render_slots, "active_index", rows=3
832         )
833
834         col = row.column(align=True)
835         col.operator("image.add_render_slot", icon='ADD', text="")
836         col.operator("image.remove_render_slot", icon='REMOVE', text="")
837
838         col.separator()
839
840         col.operator("image.clear_render_slot", icon='X', text="")
841
842
843 class IMAGE_PT_paint(Panel, ImagePaintPanel):
844     bl_label = "Brush"
845     bl_context = ".paint_common_2d"
846     bl_category = "Tools"
847
848     def draw(self, context):
849         layout = self.layout
850
851         settings = context.tool_settings.image_paint
852         brush = settings.brush
853
854         col = layout.column()
855         col.template_ID_preview(settings, "brush", new="brush.add", rows=2, cols=6)
856
857         if brush:
858             brush_texpaint_common(self, context, layout, brush, settings)
859
860
861 class IMAGE_PT_tools_brush_overlay(BrushButtonsPanel, Panel):
862     bl_label = "Overlay"
863     bl_context = ".paint_common_2d"
864     bl_options = {'DEFAULT_CLOSED'}
865     bl_category = "Options"
866
867     def draw(self, context):
868         layout = self.layout
869
870         tool_settings = context.tool_settings.image_paint
871         brush = tool_settings.brush
872         tex_slot = brush.texture_slot
873         tex_slot_mask = brush.mask_texture_slot
874
875         col = layout.column()
876
877         col.label(text="Curve:")
878
879         row = col.row(align=True)
880         row.prop(
881             brush,
882             "use_cursor_overlay",
883             text="",
884             toggle=True,
885             icon='RESTRICT_VIEW_ON' if brush.use_cursor_overlay else 'RESTRICT_VIEW_OFF',
886         )
887
888         sub = row.row(align=True)
889         sub.prop(brush, "cursor_overlay_alpha", text="Alpha")
890         sub.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA')
891
892         col.active = brush.brush_capabilities.has_overlay
893         col.label(text="Texture:")
894         row = col.row(align=True)
895         if tex_slot.map_mode != 'STENCIL':
896             row.prop(
897                 brush,
898                 "use_primary_overlay",
899                 text="",
900                 toggle=True,
901                 icon='RESTRICT_VIEW_ON' if brush.use_primary_overlay else 'RESTRICT_VIEW_OFF',
902             )
903
904         sub = row.row(align=True)
905         sub.prop(brush, "texture_overlay_alpha", text="Alpha")
906         sub.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA')
907
908         col.label(text="Mask Texture:")
909
910         row = col.row(align=True)
911         if tex_slot_mask.map_mode != 'STENCIL':
912             row.prop(
913                 brush,
914                 "use_secondary_overlay",
915                 text="",
916                 toggle=True,
917                 icon='RESTRICT_VIEW_ON' if brush.use_secondary_overlay else 'RESTRICT_VIEW_OFF',
918             )
919
920         sub = row.row(align=True)
921         sub.prop(brush, "mask_overlay_alpha", text="Alpha")
922         sub.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA')
923
924
925 class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel):
926     bl_label = "Texture"
927     bl_context = ".paint_common_2d"
928     bl_options = {'DEFAULT_CLOSED'}
929     bl_category = "Tools"
930
931     def draw(self, context):
932         layout = self.layout
933
934         tool_settings = context.tool_settings.image_paint
935         brush = tool_settings.brush
936
937         col = layout.column()
938         col.template_ID_preview(brush, "texture", new="texture.new", rows=3, cols=8)
939
940         brush_texture_settings(col, brush, 0)
941
942
943 class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel):
944     bl_label = "Texture Mask"
945     bl_context = ".paint_common_2d"
946     bl_options = {'DEFAULT_CLOSED'}
947     bl_category = "Tools"
948
949     def draw(self, context):
950         layout = self.layout
951
952         brush = context.tool_settings.image_paint.brush
953
954         col = layout.column()
955
956         col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8)
957
958         brush_mask_texture_settings(col, brush)
959
960
961 class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel):
962     bl_label = "Stroke"
963     bl_context = ".paint_common_2d"
964     bl_options = {'DEFAULT_CLOSED'}
965     bl_category = "Tools"
966
967     def draw(self, context):
968         layout = self.layout
969
970         tool_settings = context.tool_settings.image_paint
971         brush = tool_settings.brush
972
973         col = layout.column()
974
975         col.label(text="Stroke Method:")
976
977         col.prop(brush, "stroke_method", text="")
978
979         if brush.use_anchor:
980             col.separator()
981             col.prop(brush, "use_edge_to_edge", text="Edge To Edge")
982
983         if brush.use_airbrush:
984             col.separator()
985             col.prop(brush, "rate", text="Rate", slider=True)
986
987         if brush.use_space:
988             col.separator()
989             row = col.row(align=True)
990             row.prop(brush, "spacing", text="Spacing")
991             row.prop(brush, "use_pressure_spacing", toggle=True, text="")
992
993         if brush.use_line or brush.use_curve:
994             col.separator()
995             row = col.row(align=True)
996             row.prop(brush, "spacing", text="Spacing")
997
998         if brush.use_curve:
999             col.separator()
1000             col.template_ID(brush, "paint_curve", new="paintcurve.new")
1001             col.operator("paintcurve.draw")
1002
1003         col = layout.column()
1004         col.separator()
1005
1006         row = col.row(align=True)
1007         row.prop(brush, "use_relative_jitter", icon_only=True)
1008         if brush.use_relative_jitter:
1009             row.prop(brush, "jitter", slider=True)
1010         else:
1011             row.prop(brush, "jitter_absolute")
1012         row.prop(brush, "use_pressure_jitter", toggle=True, text="")
1013
1014         col = layout.column()
1015         col.separator()
1016
1017         if brush.brush_capabilities.has_smooth_stroke:
1018             col.prop(brush, "use_smooth_stroke")
1019
1020             sub = col.column()
1021             sub.active = brush.use_smooth_stroke
1022             sub.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
1023             sub.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
1024
1025             col.separator()
1026
1027         col.prop(tool_settings, "input_samples")
1028
1029
1030 class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel):
1031     bl_label = "Curve"
1032     bl_context = ".paint_common_2d"
1033     bl_options = {'DEFAULT_CLOSED'}
1034     bl_category = "Tools"
1035
1036     def draw(self, context):
1037         layout = self.layout
1038
1039         tool_settings = context.tool_settings.image_paint
1040         brush = tool_settings.brush
1041
1042         layout.template_curve_mapping(brush, "curve")
1043
1044         col = layout.column(align=True)
1045         row = col.row(align=True)
1046         row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
1047         row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
1048         row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
1049         row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
1050         row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
1051         row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
1052
1053
1054 class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel):
1055     bl_category = "Tools"
1056     bl_context = ".imagepaint_2d"
1057     bl_label = "Tiling"
1058     bl_options = {'DEFAULT_CLOSED'}
1059
1060     def draw(self, context):
1061         layout = self.layout
1062
1063         tool_settings = context.tool_settings
1064         ipaint = tool_settings.image_paint
1065
1066         col = layout.column(align=True)
1067         row = col.row(align=True)
1068         row.prop(ipaint, "tile_x", text="X", toggle=True)
1069         row.prop(ipaint, "tile_y", text="Y", toggle=True)
1070
1071
1072 class IMAGE_PT_tools_brush_appearance(BrushButtonsPanel, Panel):
1073     bl_label = "Appearance"
1074     bl_context = ".paint_common_2d"
1075     bl_options = {'DEFAULT_CLOSED'}
1076     bl_category = "Options"
1077     bl_parent_id = "IMAGE_PT_tools_brush_overlay"
1078
1079     def draw(self, context):
1080         layout = self.layout
1081
1082         tool_settings = context.tool_settings.image_paint
1083         brush = tool_settings.brush
1084
1085         if brush is None:  # unlikely but can happen.
1086             layout.label(text="Brush Unset")
1087             return
1088
1089         col = layout.column(align=True)
1090
1091         col.prop(tool_settings, "show_brush")
1092         sub = col.column()
1093         sub.active = tool_settings.show_brush
1094         sub.prop(brush, "cursor_color_add", text="")
1095
1096         col.separator()
1097
1098         col.prop(brush, "use_custom_icon")
1099         sub = col.column()
1100         sub.active = brush.use_custom_icon
1101         sub.prop(brush, "icon_filepath", text="")
1102
1103
1104 class IMAGE_PT_uv_sculpt_curve(Panel):
1105     bl_space_type = 'PROPERTIES'
1106     bl_region_type = 'WINDOW'
1107     bl_context = ".uv_sculpt"  # dot on purpose (access from topbar)
1108     bl_category = "Options"
1109     bl_label = "UV Sculpt Curve"
1110     bl_options = {'DEFAULT_CLOSED'}
1111
1112     @classmethod
1113     def poll(cls, context):
1114         return (context.uv_sculpt_object is not None)
1115
1116     def draw(self, context):
1117         layout = self.layout
1118
1119         tool_settings = context.tool_settings
1120         uvsculpt = tool_settings.uv_sculpt
1121         brush = uvsculpt.brush
1122
1123         layout.template_curve_mapping(brush, "curve")
1124
1125         row = layout.row(align=True)
1126         row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
1127         row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
1128         row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
1129         row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
1130         row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
1131         row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
1132
1133
1134 class IMAGE_PT_uv_sculpt(Panel):
1135     bl_space_type = 'PROPERTIES'
1136     bl_region_type = 'WINDOW'
1137     bl_context = ".uv_sculpt"  # dot on purpose (access from topbar)
1138     bl_category = "Options"
1139     bl_label = "UV Sculpt"
1140
1141     @classmethod
1142     def poll(cls, context):
1143         return (context.uv_sculpt_object is not None)
1144
1145     def draw(self, context):
1146         from .properties_paint_common import UnifiedPaintPanel
1147         layout = self.layout
1148
1149         tool_settings = context.tool_settings
1150         uvsculpt = tool_settings.uv_sculpt
1151         brush = uvsculpt.brush
1152
1153         if not self.is_popover:
1154             if brush:
1155                 col = layout.column()
1156
1157                 row = col.row(align=True)
1158                 UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
1159                 UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size")
1160
1161                 row = col.row(align=True)
1162                 UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True, text="Strength")
1163                 UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength")
1164
1165         col = layout.column()
1166         col.prop(tool_settings, "uv_sculpt_lock_borders")
1167         col.prop(tool_settings, "uv_sculpt_all_islands")
1168
1169         col.prop(tool_settings, "uv_sculpt_tool")
1170         if tool_settings.uv_sculpt_tool == 'RELAX':
1171             col.prop(tool_settings, "uv_relax_method")
1172
1173         col.prop(uvsculpt, "show_brush")
1174
1175
1176 class ImageScopesPanel:
1177     @classmethod
1178     def poll(cls, context):
1179         sima = context.space_data
1180
1181         if not (sima and sima.image):
1182             return False
1183
1184         # scopes are not updated in paint modes, hide.
1185         if sima.mode == 'PAINT':
1186             return False
1187
1188         ob = context.active_object
1189         if ob and ob.mode in {'TEXTURE_PAINT', 'EDIT'}:
1190             return False
1191
1192         return True
1193
1194
1195 class IMAGE_PT_view_scopes(ImageScopesPanel, Panel):
1196     bl_space_type = 'IMAGE_EDITOR'
1197     bl_region_type = 'UI'
1198     bl_category = "Image"
1199     bl_label = "Scopes"
1200
1201     def draw(self, layout):
1202         return  # nothing to draw.
1203
1204
1205 class IMAGE_PT_view_histogram(ImageScopesPanel, Panel):
1206     bl_space_type = 'IMAGE_EDITOR'
1207     bl_region_type = 'UI'
1208     bl_category = "Image"
1209     bl_label = "Histogram"
1210     bl_parent_id = 'IMAGE_PT_view_scopes'
1211
1212     def draw(self, context):
1213         layout = self.layout
1214
1215         sima = context.space_data
1216         hist = sima.scopes.histogram
1217
1218         layout.template_histogram(sima.scopes, "histogram")
1219
1220         row = layout.row(align=True)
1221         row.prop(hist, "mode", expand=True)
1222         row.prop(hist, "show_line", text="")
1223
1224
1225 class IMAGE_PT_view_waveform(ImageScopesPanel, Panel):
1226     bl_space_type = 'IMAGE_EDITOR'
1227     bl_region_type = 'UI'
1228     bl_category = "Image"
1229     bl_label = "Waveform"
1230     bl_parent_id = 'IMAGE_PT_view_scopes'
1231     bl_options = {'DEFAULT_CLOSED'}
1232
1233     def draw(self, context):
1234         layout = self.layout
1235
1236         sima = context.space_data
1237
1238         layout.template_waveform(sima, "scopes")
1239         row = layout.split(factor=0.75)
1240         row.prop(sima.scopes, "waveform_alpha")
1241         row.prop(sima.scopes, "waveform_mode", text="")
1242
1243
1244 class IMAGE_PT_view_vectorscope(ImageScopesPanel, Panel):
1245     bl_space_type = 'IMAGE_EDITOR'
1246     bl_region_type = 'UI'
1247     bl_category = "Image"
1248     bl_label = "Vectorscope"
1249     bl_parent_id = 'IMAGE_PT_view_scopes'
1250     bl_options = {'DEFAULT_CLOSED'}
1251
1252     def draw(self, context):
1253         layout = self.layout
1254
1255         sima = context.space_data
1256         layout.template_vectorscope(sima, "scopes")
1257         layout.prop(sima.scopes, "vectorscope_alpha")
1258
1259
1260 class IMAGE_PT_sample_line(ImageScopesPanel, Panel):
1261     bl_space_type = 'IMAGE_EDITOR'
1262     bl_region_type = 'UI'
1263     bl_category = "Image"
1264     bl_label = "Sample Line"
1265     bl_parent_id = 'IMAGE_PT_view_scopes'
1266     bl_options = {'DEFAULT_CLOSED'}
1267
1268     def draw(self, context):
1269         layout = self.layout
1270
1271         sima = context.space_data
1272         hist = sima.sample_histogram
1273
1274         layout.operator("image.sample_line")
1275         layout.template_histogram(sima, "sample_histogram")
1276
1277         row = layout.row(align=True)
1278         row.prop(hist, "mode", expand=True)
1279         row.prop(hist, "show_line", text="")
1280
1281
1282 class IMAGE_PT_scope_sample(ImageScopesPanel, Panel):
1283     bl_space_type = 'IMAGE_EDITOR'
1284     bl_region_type = 'UI'
1285     bl_category = "Image"
1286     bl_label = "Samples"
1287     bl_parent_id = 'IMAGE_PT_view_scopes'
1288     bl_options = {'DEFAULT_CLOSED'}
1289
1290     def draw(self, context):
1291         layout = self.layout
1292         layout.use_property_split = True
1293         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
1294
1295         sima = context.space_data
1296
1297         col = flow.column()
1298         col.prop(sima.scopes, "use_full_resolution")
1299
1300         col = flow.column()
1301         col.active = not sima.scopes.use_full_resolution
1302         col.prop(sima.scopes, "accuracy")
1303
1304
1305 # Grease Pencil properties
1306 class IMAGE_PT_grease_pencil(AnnotationDataPanel, Panel):
1307     bl_space_type = 'IMAGE_EDITOR'
1308     bl_region_type = 'UI'
1309     bl_category = "Image"
1310
1311     # NOTE: this is just a wrapper around the generic GP Panel.
1312
1313 # Grease Pencil drawing tools.
1314
1315
1316 classes = (
1317     IMAGE_MT_view,
1318     IMAGE_MT_view_zoom,
1319     IMAGE_MT_select,
1320     IMAGE_MT_brush,
1321     IMAGE_MT_image,
1322     IMAGE_MT_image_invert,
1323     IMAGE_MT_uvs,
1324     IMAGE_MT_uvs_showhide,
1325     IMAGE_MT_uvs_proportional,
1326     IMAGE_MT_uvs_transform,
1327     IMAGE_MT_uvs_snap,
1328     IMAGE_MT_uvs_mirror,
1329     IMAGE_MT_uvs_weldalign,
1330     IMAGE_MT_uvs_select_mode,
1331     IMAGE_MT_specials,
1332     IMAGE_MT_pivot_pie,
1333     IMAGE_MT_uvs_snap_pie,
1334     IMAGE_HT_header,
1335     MASK_MT_editor_menus,
1336     IMAGE_PT_mask,
1337     IMAGE_PT_mask_layers,
1338     IMAGE_PT_mask_display,
1339     IMAGE_PT_active_mask_spline,
1340     IMAGE_PT_active_mask_point,
1341     IMAGE_PT_image_properties,
1342     IMAGE_UL_render_slots,
1343     IMAGE_PT_render_slots,
1344     IMAGE_PT_view_display,
1345     IMAGE_PT_view_display_uv_edit_overlays,
1346     IMAGE_PT_view_display_uv_edit_overlays_advanced,
1347     IMAGE_PT_paint,
1348     IMAGE_PT_tools_brush_overlay,
1349     IMAGE_PT_tools_brush_texture,
1350     IMAGE_PT_tools_mask_texture,
1351     IMAGE_PT_paint_stroke,
1352     IMAGE_PT_paint_curve,
1353     IMAGE_PT_tools_imagepaint_symmetry,
1354     IMAGE_PT_tools_brush_appearance,
1355     IMAGE_PT_uv_sculpt,
1356     IMAGE_PT_uv_sculpt_curve,
1357     IMAGE_PT_view_scopes,
1358     IMAGE_PT_view_histogram,
1359     IMAGE_PT_view_waveform,
1360     IMAGE_PT_view_vectorscope,
1361     IMAGE_PT_sample_line,
1362     IMAGE_PT_scope_sample,
1363     IMAGE_PT_grease_pencil,
1364 )
1365
1366
1367 if __name__ == "__main__":  # only for live edit.
1368     from bpy.utils import register_class
1369     for cls in classes:
1370         register_class(cls)