3D View: add opacity for sculpt mask display
[blender.git] / release / scripts / startup / bl_ui / space_view3d.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 import bpy
21 from bpy.types import (
22     Header,
23     Menu,
24     Panel,
25 )
26 from .properties_paint_common import (
27     UnifiedPaintPanel,
28 )
29 from .properties_grease_pencil_common import (
30     AnnotationDataPanel,
31     AnnotationOnionSkin,
32     GreasePencilMaterialsPanel,
33 )
34 from bpy.app.translations import contexts as i18n_contexts
35
36
37 class VIEW3D_HT_tool_header(Header):
38     bl_space_type = 'VIEW_3D'
39     bl_region_type = "TOOL_HEADER"
40
41     def draw(self, context):
42         layout = self.layout
43
44         layout.row(align=True).template_header()
45
46         # mode_string = context.mode
47         obj = context.active_object
48         tool_settings = context.tool_settings
49
50         object_mode = 'OBJECT' if obj is None else obj.mode
51         has_pose_mode = (
52             (object_mode == 'POSE') or
53             (object_mode == 'WEIGHT_PAINT' and context.pose_object is not None)
54         )
55
56         self.draw_tool_settings(context)
57
58         layout.separator_spacer()
59
60         # Mode & Transform Settings
61         scene = context.scene
62
63         # Orientation
64         if object_mode in {'OBJECT', 'EDIT', 'EDIT_GPENCIL'} or has_pose_mode:
65             orient_slot = scene.transform_orientation_slots[0]
66             row = layout.row(align=True)
67
68             sub = row.row()
69             sub.ui_units_x = 4
70             sub.prop_with_popover(
71                 orient_slot,
72                 "type",
73                 text="",
74                 panel="VIEW3D_PT_transform_orientations",
75             )
76
77         # Pivot
78         if object_mode in {'OBJECT', 'EDIT', 'EDIT_GPENCIL', 'SCULPT_GPENCIL'} or has_pose_mode:
79             layout.prop_with_popover(
80                 tool_settings,
81                 "transform_pivot_point",
82                 text="",
83                 icon_only=True,
84                 panel="VIEW3D_PT_pivot_point",
85             )
86
87         # Snap
88         show_snap = False
89         if obj is None:
90             show_snap = True
91         else:
92             if (object_mode not in {
93                     'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT',
94                     'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'
95             }) or has_pose_mode:
96                 show_snap = True
97             else:
98
99                 from .properties_paint_common import UnifiedPaintPanel
100                 paint_settings = UnifiedPaintPanel.paint_settings(context)
101
102                 if paint_settings:
103                     brush = paint_settings.brush
104                     if brush and brush.stroke_method == 'CURVE':
105                         show_snap = True
106
107         if show_snap:
108             snap_items = bpy.types.ToolSettings.bl_rna.properties["snap_elements"].enum_items
109             snap_elements = tool_settings.snap_elements
110             if len(snap_elements) == 1:
111                 text = ""
112                 for elem in snap_elements:
113                     icon = snap_items[elem].icon
114                     break
115             else:
116                 text = "Mix"
117                 icon = 'NONE'
118             del snap_items, snap_elements
119
120             row = layout.row(align=True)
121             row.prop(tool_settings, "use_snap", text="")
122
123             sub = row.row(align=True)
124             sub.popover(
125                 panel="VIEW3D_PT_snapping",
126                 icon=icon,
127                 text=text,
128             )
129
130         # Proportional editing
131         gpd = context.gpencil_data
132         if object_mode in {'EDIT', 'PARTICLE_EDIT'}:
133             row = layout.row(align=True)
134             row.prop(tool_settings, "proportional_edit", icon_only=True)
135             sub = row.row(align=True)
136             sub.active = tool_settings.proportional_edit != 'DISABLED'
137             sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
138
139         elif object_mode == 'OBJECT':
140             row = layout.row(align=True)
141             row.prop(tool_settings, "use_proportional_edit_objects", icon_only=True)
142             sub = row.row(align=True)
143             sub.active = tool_settings.use_proportional_edit_objects
144             sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
145
146         elif gpd is not None and obj.type == 'GPENCIL':
147             if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode:
148                 row = layout.row(align=True)
149                 row.prop(tool_settings, "proportional_edit", icon_only=True)
150
151                 sub = row.row(align=True)
152                 sub.active = tool_settings.proportional_edit != 'DISABLED'
153                 sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
154
155         # grease pencil
156         if object_mode == 'PAINT_GPENCIL':
157             layout.prop_with_popover(
158                 tool_settings,
159                 "gpencil_stroke_placement_view3d",
160                 text="",
161                 panel="VIEW3D_PT_gpencil_origin",
162             )
163
164         if object_mode in {'PAINT_GPENCIL', 'SCULPT_GPENCIL'}:
165             layout.prop_with_popover(
166                 tool_settings.gpencil_sculpt,
167                 "lock_axis",
168                 text="",
169                 panel="VIEW3D_PT_gpencil_lock",
170             )
171
172         if object_mode == 'PAINT_GPENCIL':
173             # FIXME: this is bad practice!
174             # Tool options are to be displayed in the topbar.
175             if context.workspace.tools.from_space_view3d_mode(object_mode).idname == "builtin_brush.Draw":
176                 settings = tool_settings.gpencil_sculpt.guide
177                 row = layout.row(align=True)
178                 row.prop(settings, "use_guide", text="", icon='GRID')
179                 sub = row.row(align=True)
180                 sub.active = settings.use_guide
181                 sub.popover(
182                     panel="VIEW3D_PT_gpencil_guide",
183                     text="Guides",
184                 )
185
186         layout.separator_spacer()
187
188         self.draw_mode_settings(context)
189
190     def draw_tool_settings(self, context):
191         layout = self.layout
192
193         # Active Tool
194         # -----------
195         from .space_toolsystem_common import ToolSelectPanelHelper
196         tool = ToolSelectPanelHelper.draw_active_tool_header(context, layout)
197         tool_mode = context.mode if tool is None else tool.mode
198
199         # Object Mode Options
200         # -------------------
201
202         # Example of how tool_settings can be accessed as pop-overs.
203
204         # TODO(campbell): editing options should be after active tool options
205         # (obviously separated for from the users POV)
206         draw_fn = getattr(_draw_tool_settings_context_mode, tool_mode, None)
207         if draw_fn is not None:
208             draw_fn(context, layout, tool)
209
210         # Note: general mode options should be added to 'draw_mode_settings'.
211         if tool_mode == 'SCULPT':
212             if (tool is not None) and tool.has_datablock:
213                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
214         elif tool_mode == 'PAINT_VERTEX':
215             if (tool is not None) and tool.has_datablock:
216                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
217         elif tool_mode == 'PAINT_WEIGHT':
218             if (tool is not None) and tool.has_datablock:
219                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
220         elif tool_mode == 'PAINT_TEXTURE':
221             if (tool is not None) and tool.has_datablock:
222                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
223         elif tool_mode == 'EDIT_ARMATURE':
224             pass
225         elif tool_mode == 'EDIT_CURVE':
226             pass
227         elif tool_mode == 'EDIT_MESH':
228             pass
229         elif tool_mode == 'POSE':
230             pass
231         elif tool_mode == 'PARTICLE':
232             # Disable, only shows "Brush" panel, which is already in the top-bar.
233             # if tool.has_datablock:
234             #     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
235             pass
236         elif tool_mode == 'PAINT_GPENCIL':
237             if (tool is not None) and tool.has_datablock:
238                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_paint", category="")
239         elif tool_mode == 'SCULPT_GPENCIL':
240             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_sculpt", category="")
241         elif tool_mode == 'WEIGHT_GPENCIL':
242             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_weight", category="")
243
244     def draw_mode_settings(self, context):
245         layout = self.layout
246
247         # Active Tool
248         # -----------
249         from .space_toolsystem_common import ToolSelectPanelHelper
250         tool = ToolSelectPanelHelper.tool_active_from_context(context)
251         tool_mode = context.mode if tool is None else tool.mode
252
253         if tool_mode == 'SCULPT':
254             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".sculpt_mode", category="")
255         elif tool_mode == 'PAINT_VERTEX':
256             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".vertexpaint", category="")
257         elif tool_mode == 'PAINT_WEIGHT':
258             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".weightpaint", category="")
259         elif tool_mode == 'PAINT_TEXTURE':
260             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".imagepaint", category="")
261         elif tool_mode == 'EDIT_TEXT':
262             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".text_edit", category="")
263         elif tool_mode == 'EDIT_ARMATURE':
264             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".armature_edit", category="")
265         elif tool_mode == 'EDIT_METABALL':
266             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".mball_edit", category="")
267         elif tool_mode == 'EDIT_LATTICE':
268             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".lattice_edit", category="")
269         elif tool_mode == 'EDIT_CURVE':
270             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".curve_edit", category="")
271         elif tool_mode == 'EDIT_MESH':
272             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".mesh_edit", category="")
273         elif tool_mode == 'POSE':
274             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".posemode", category="")
275         elif tool_mode == 'PARTICLE':
276             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".particlemode", category="")
277         elif tool_mode == 'OBJECT':
278             layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".objectmode", category="")
279         elif tool_mode in {'PAINT_GPENCIL', 'EDIT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
280             # Grease pencil layer.
281             gpl = context.active_gpencil_layer
282             if gpl and gpl.info is not None:
283                 text = gpl.info
284                 maxw = 25
285                 if len(text) > maxw:
286                     text = text[:maxw - 5] + '..' + text[-3:]
287             else:
288                 text = ""
289
290             layout.label(text="Layer:")
291             sub = layout.row()
292             sub.ui_units_x = 8
293             sub.popover(
294                 panel="TOPBAR_PT_gpencil_layers",
295                 text=text,
296             )
297
298
299 class _draw_tool_settings_context_mode:
300     @staticmethod
301     def SCULPT(context, layout, tool):
302         if (tool is None) or (not tool.has_datablock):
303             return
304
305         paint = context.tool_settings.sculpt
306         layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
307
308         brush = paint.brush
309         if brush is None:
310             return
311
312         from .properties_paint_common import (
313             brush_basic_sculpt_settings,
314         )
315         brush_basic_sculpt_settings(layout, context, brush, compact=True)
316
317     @staticmethod
318     def PAINT_TEXTURE(context, layout, tool):
319         if (tool is None) or (not tool.has_datablock):
320             return
321
322         paint = context.tool_settings.image_paint
323         layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
324
325         brush = paint.brush
326         if brush is None:
327             return
328
329         from .properties_paint_common import (
330             UnifiedPaintPanel,
331             brush_basic_texpaint_settings,
332         )
333         capabilities = brush.image_paint_capabilities
334         if capabilities.has_color:
335             UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
336         brush_basic_texpaint_settings(layout, context, brush, compact=True)
337
338     @staticmethod
339     def PAINT_VERTEX(context, layout, tool):
340         if (tool is None) or (not tool.has_datablock):
341             return
342
343         paint = context.tool_settings.vertex_paint
344         layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
345
346         brush = paint.brush
347         if brush is None:
348             return
349
350         from .properties_paint_common import (
351             UnifiedPaintPanel,
352             brush_basic_vpaint_settings,
353         )
354         capabilities = brush.vertex_paint_capabilities
355         if capabilities.has_color:
356             UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
357         brush_basic_vpaint_settings(layout, context, brush, compact=True)
358
359     @staticmethod
360     def PAINT_WEIGHT(context, layout, tool):
361         if (tool is None) or (not tool.has_datablock):
362             return
363
364         paint = context.tool_settings.weight_paint
365         layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
366         brush = paint.brush
367         if brush is None:
368             return
369
370         from .properties_paint_common import brush_basic_wpaint_settings
371         brush_basic_wpaint_settings(layout, context, brush, compact=True)
372
373     @staticmethod
374     def PAINT_GPENCIL(context, layout, tool):
375         if tool is None:
376             return
377
378         # is_paint = True
379         # FIXME: tools must use their own UI drawing!
380         if tool.idname in {"builtin.line", "builtin.box", "builtin.circle", "builtin.arc", "builtin.curve"}:
381             # is_paint = False
382             pass
383         elif tool.idname == "Cutter":
384             row = layout.row(align=True)
385             row.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
386             return
387         elif not tool.has_datablock:
388             return
389
390         paint = context.tool_settings.gpencil_paint
391         brush = paint.brush
392         if brush is None:
393             return
394
395         gp_settings = brush.gpencil_settings
396
397         def draw_color_selector():
398             ma = gp_settings.material
399             row = layout.row(align=True)
400             if not gp_settings.use_material_pin:
401                 ma = context.object.active_material
402             icon_id = 0
403             if ma:
404                 icon_id = ma.id_data.preview.icon_id
405                 txt_ma = ma.name
406                 maxw = 25
407                 if len(txt_ma) > maxw:
408                     txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:]
409             else:
410                 txt_ma = ""
411
412             sub = row.row()
413             sub.ui_units_x = 8
414             sub.popover(
415                 panel="TOPBAR_PT_gpencil_materials",
416                 text=txt_ma,
417                 icon_value=icon_id,
418             )
419
420             row.prop(gp_settings, "use_material_pin", text="")
421
422         row = layout.row(align=True)
423         tool_settings = context.scene.tool_settings
424         settings = tool_settings.gpencil_paint
425         row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
426
427         if context.object and brush.gpencil_tool in {'FILL', 'DRAW'}:
428             draw_color_selector()
429
430         from .properties_paint_common import (
431             brush_basic_gpencil_paint_settings,
432         )
433         brush_basic_gpencil_paint_settings(layout, context, brush, compact=True)
434
435         # FIXME: tools must use their own UI drawing!
436         if tool.idname in {"builtin.arc", "builtin.curve", "builtin.line", "builtin.box", "builtin.circle"}:
437             settings = context.tool_settings.gpencil_sculpt
438             row = layout.row(align=True)
439             row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA')
440             sub = row.row(align=True)
441             sub.active = settings.use_thickness_curve
442             sub.popover(
443                 panel="TOPBAR_PT_gpencil_primitive",
444                 text="Thickness Profile",
445             )
446
447         if brush.gpencil_tool == 'FILL':
448             settings = context.tool_settings.gpencil_sculpt
449             row = layout.row(align=True)
450             sub = row.row(align=True)
451             sub.popover(
452                 panel="TOPBAR_PT_gpencil_fill",
453                 text="Fill Options",
454             )
455
456     @staticmethod
457     def SCULPT_GPENCIL(context, layout, tool):
458         if (tool is None) or (not tool.has_datablock):
459             return
460         tool_settings = context.tool_settings
461         settings = tool_settings.gpencil_sculpt
462         brush = settings.brush
463
464         from .properties_paint_common import (
465             brush_basic_gpencil_sculpt_settings,
466         )
467         brush_basic_gpencil_sculpt_settings(layout, context, brush, compact=True)
468
469     @staticmethod
470     def WEIGHT_GPENCIL(context, layout, tool):
471         if (tool is None) or (not tool.has_datablock):
472             return
473         tool_settings = context.tool_settings
474         settings = tool_settings.gpencil_sculpt
475         brush = settings.brush
476
477         from .properties_paint_common import (
478             brush_basic_gpencil_weight_settings,
479         )
480         brush_basic_gpencil_weight_settings(layout, context, brush, compact=True)
481
482     @staticmethod
483     def PARTICLE(context, layout, tool):
484         if (tool is None) or (not tool.has_datablock):
485             return
486
487         # See: 'VIEW3D_PT_tools_brush', basically a duplicate
488         settings = context.tool_settings.particle_edit
489         brush = settings.brush
490         tool = settings.tool
491         if tool != 'NONE':
492             layout.prop(brush, "size", slider=True)
493             if tool == 'ADD':
494                 layout.prop(brush, "count")
495
496                 layout.prop(settings, "use_default_interpolate")
497                 layout.prop(brush, "steps", slider=True)
498                 layout.prop(settings, "default_key_count", slider=True)
499             else:
500                 layout.prop(brush, "strength", slider=True)
501
502                 if tool == 'LENGTH':
503                     layout.row().prop(brush, "length_mode", expand=True)
504                 elif tool == 'PUFF':
505                     layout.row().prop(brush, "puff_mode", expand=True)
506                     layout.prop(brush, "use_puff_volume")
507                 elif tool == 'COMB':
508                     row = layout.row()
509                     row.active = settings.is_editable
510                     row.prop(settings, "use_emitter_deflect", text="Deflect Emitter")
511                     sub = row.row(align=True)
512                     sub.active = settings.use_emitter_deflect
513                     sub.prop(settings, "emitter_distance", text="Distance")
514
515
516 class VIEW3D_HT_header(Header):
517     bl_space_type = 'VIEW_3D'
518
519     def draw(self, context):
520         layout = self.layout
521
522         tool_settings = context.tool_settings
523         view = context.space_data
524         shading = view.shading
525         # mode_string = context.mode
526         obj = context.active_object
527
528         if not view.show_region_tool_header:
529             layout.row(align=True).template_header()
530
531         row = layout.row(align=True)
532         object_mode = 'OBJECT' if obj is None else obj.mode
533
534         # Note: This is actually deadly in case enum_items have to be dynamically generated
535         #       (because internal RNA array iterator will free everything immediately...).
536         # XXX This is an RNA internal issue, not sure how to fix it.
537         # Note: Tried to add an accessor to get translated UI strings instead of manual call
538         #       to pgettext_iface below, but this fails because translated enumitems
539         #       are always dynamically allocated.
540         act_mode_item = bpy.types.Object.bl_rna.properties["mode"].enum_items[object_mode]
541         act_mode_i18n_context = bpy.types.Object.bl_rna.properties["mode"].translation_context
542
543         sub = row.row(align=True)
544         sub.ui_units_x = 5.5
545         sub.operator_menu_enum(
546             "object.mode_set", "mode",
547             text=bpy.app.translations.pgettext_iface(act_mode_item.name, act_mode_i18n_context),
548             icon=act_mode_item.icon,
549         )
550         del act_mode_item
551
552         layout.template_header_3D_mode()
553
554         # Contains buttons like Mode, Pivot, Layer, Mesh Select Mode...
555         if obj:
556             # Particle edit
557             if object_mode == 'PARTICLE_EDIT':
558                 row = layout.row()
559                 row.prop(tool_settings.particle_edit, "select_mode", text="", expand=True)
560
561         # Grease Pencil
562         if obj and obj.type == 'GPENCIL' and context.gpencil_data:
563             gpd = context.gpencil_data
564
565             if gpd.is_stroke_paint_mode:
566                 row = layout.row()
567                 sub = row.row(align=True)
568                 sub.prop(tool_settings, "use_gpencil_draw_onback", text="", icon='MOD_OPACITY')
569                 sub.separator(factor=0.4)
570                 sub.prop(tool_settings, "use_gpencil_weight_data_add", text="", icon='WPAINT_HLT')
571                 sub.separator(factor=0.4)
572                 sub.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
573
574             if gpd.use_stroke_edit_mode:
575                 row = layout.row(align=True)
576                 row.prop(tool_settings, "gpencil_selectmode", text="", expand=True)
577
578             if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode or gpd.is_stroke_weight_mode:
579                 row = layout.row(align=True)
580
581                 if gpd.is_stroke_sculpt_mode:
582                     row.prop(tool_settings.gpencil_sculpt, "use_select_mask", text="")
583                     row.separator()
584
585                 row.prop(gpd, "use_multiedit", text="", icon='GP_MULTIFRAME_EDITING')
586
587                 sub = row.row(align=True)
588                 sub.active = gpd.use_multiedit
589                 sub.popover(
590                     panel="VIEW3D_PT_gpencil_multi_frame",
591                     text="Multiframe",
592                 )
593
594             if gpd.use_stroke_edit_mode:
595                 row = layout.row(align=True)
596                 row.prop(tool_settings.gpencil_sculpt, "use_select_mask", text="")
597
598                 row.popover(
599                     panel="VIEW3D_PT_tools_grease_pencil_interpolate",
600                     text="Interpolate",
601                 )
602
603         overlay = view.overlay
604
605         VIEW3D_MT_editor_menus.draw_collapsible(context, layout)
606
607         layout.separator_spacer()
608
609         # Viewport Settings
610         layout.popover(
611             panel="VIEW3D_PT_object_type_visibility",
612             icon_value=view.icon_from_show_object_viewport,
613             text="",
614         )
615
616         # Gizmo toggle & popover.
617         row = layout.row(align=True)
618         # FIXME: place-holder icon.
619         row.prop(view, "show_gizmo", text="", toggle=True, icon='EMPTY_DATA')
620         sub = row.row(align=True)
621         sub.active = view.show_gizmo
622         sub.popover(
623             panel="VIEW3D_PT_gizmo_display",
624             text="",
625         )
626
627         # Overlay toggle & popover.
628         row = layout.row(align=True)
629         row.prop(overlay, "show_overlays", icon='OVERLAY', text="")
630         sub = row.row(align=True)
631         sub.active = overlay.show_overlays
632         sub.popover(panel="VIEW3D_PT_overlay", text="")
633
634         row = layout.row()
635         row.active = (shading.type in {'WIREFRAME', 'SOLID'}) or object_mode in {'EDIT'}
636
637         if shading.type == 'WIREFRAME':
638             row.prop(shading, "show_xray_wireframe", text="", icon='XRAY')
639         else:
640             row.prop(shading, "show_xray", text="", icon='XRAY')
641
642         row = layout.row(align=True)
643         row.prop(shading, "type", text="", expand=True)
644         sub = row.row(align=True)
645         # TODO, currently render shading type ignores mesh two-side, until it's supported
646         # show the shading popover which shows double-sided option.
647
648         # sub.enabled = shading.type != 'RENDERED'
649         sub.popover(panel="VIEW3D_PT_shading", text="")
650
651
652 class VIEW3D_MT_editor_menus(Menu):
653     bl_label = ""
654
655     def draw(self, context):
656         layout = self.layout
657         obj = context.active_object
658         mode_string = context.mode
659         edit_object = context.edit_object
660         gp_edit = obj and obj.mode in {'EDIT_GPENCIL', 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}
661
662         layout.menu("VIEW3D_MT_view")
663
664         # Select Menu
665         if gp_edit:
666             if mode_string not in {'PAINT_GPENCIL', 'WEIGHT_GPENCIL'}:
667                 layout.menu("VIEW3D_MT_select_gpencil")
668         elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}:
669             mesh = obj.data
670             if mesh.use_paint_mask:
671                 layout.menu("VIEW3D_MT_select_paint_mask")
672             elif mesh.use_paint_mask_vertex and mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX'}:
673                 layout.menu("VIEW3D_MT_select_paint_mask_vertex")
674         elif mode_string != 'SCULPT':
675             layout.menu("VIEW3D_MT_select_%s" % mode_string.lower())
676
677         if gp_edit:
678             pass
679         elif mode_string == 'OBJECT':
680             layout.menu("VIEW3D_MT_add", text="Add")
681         elif mode_string == 'EDIT_MESH':
682             layout.menu("VIEW3D_MT_mesh_add", text="Add")
683         elif mode_string == 'EDIT_CURVE':
684             layout.menu("VIEW3D_MT_curve_add", text="Add")
685         elif mode_string == 'EDIT_SURFACE':
686             layout.menu("VIEW3D_MT_surface_add", text="Add")
687         elif mode_string == 'EDIT_METABALL':
688             layout.menu("VIEW3D_MT_metaball_add", text="Add")
689         elif mode_string == 'EDIT_ARMATURE':
690             layout.menu("TOPBAR_MT_edit_armature_add", text="Add")
691
692         if gp_edit:
693             if obj and obj.mode == 'PAINT_GPENCIL':
694                 layout.menu("VIEW3D_MT_paint_gpencil")
695             elif obj and obj.mode == 'EDIT_GPENCIL':
696                 layout.menu("VIEW3D_MT_edit_gpencil")
697             elif obj and obj.mode == 'WEIGHT_GPENCIL':
698                 layout.menu("VIEW3D_MT_weight_gpencil")
699
700         elif edit_object:
701             layout.menu("VIEW3D_MT_edit_%s" % edit_object.type.lower())
702
703             if mode_string == 'EDIT_MESH':
704                 layout.menu("VIEW3D_MT_edit_mesh_vertices")
705                 layout.menu("VIEW3D_MT_edit_mesh_edges")
706                 layout.menu("VIEW3D_MT_edit_mesh_faces")
707                 layout.menu("VIEW3D_MT_uv_map", text="UV")
708             elif mode_string in {'EDIT_CURVE', 'EDIT_SURFACE'}:
709                 layout.menu("VIEW3D_MT_edit_curve_ctrlpoints")
710                 layout.menu("VIEW3D_MT_edit_curve_segments")
711
712         elif obj:
713             if mode_string != 'PAINT_TEXTURE':
714                 layout.menu("VIEW3D_MT_%s" % mode_string.lower())
715             if mode_string in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'PAINT_TEXTURE'}:
716                 layout.menu("VIEW3D_MT_brush")
717             if mode_string == 'SCULPT':
718                 layout.menu("VIEW3D_MT_hide_mask")
719         else:
720             layout.menu("VIEW3D_MT_object")
721
722
723 # ********** Menu **********
724
725
726 # ********** Utilities **********
727
728
729 class ShowHideMenu:
730     bl_label = "Show/Hide"
731     _operator_name = ""
732
733     def draw(self, _context):
734         layout = self.layout
735
736         layout.operator("%s.reveal" % self._operator_name)
737         layout.operator("%s.hide" % self._operator_name, text="Hide Selected").unselected = False
738         layout.operator("%s.hide" % self._operator_name, text="Hide Unselected").unselected = True
739
740
741 # Standard transforms which apply to all cases
742 # NOTE: this doesn't seem to be able to be used directly
743 class VIEW3D_MT_transform_base(Menu):
744     bl_label = "Transform"
745     bl_category = "View"
746
747     # TODO: get rid of the custom text strings?
748     def draw(self, context):
749         layout = self.layout
750
751         layout.operator("transform.translate")
752         layout.operator("transform.rotate")
753         layout.operator("transform.resize", text="Scale")
754
755         layout.separator()
756
757         layout.operator("transform.tosphere", text="To Sphere")
758         layout.operator("transform.shear", text="Shear")
759         layout.operator("transform.bend", text="Bend")
760         layout.operator("transform.push_pull", text="Push/Pull")
761
762         if context.mode != 'OBJECT':
763             layout.operator("transform.vertex_warp", text="Warp")
764             layout.operator("transform.vertex_random", text="Randomize")
765
766
767 # Generic transform menu - geometry types
768 class VIEW3D_MT_transform(VIEW3D_MT_transform_base):
769     def draw(self, context):
770         # base menu
771         VIEW3D_MT_transform_base.draw(self, context)
772
773         # generic...
774         layout = self.layout
775         if context.mode == 'EDIT_MESH':
776             layout.operator("transform.shrink_fatten", text="Shrink Fatten")
777         elif context.mode == 'EDIT_CURVE':
778             layout.operator("transform.transform", text="Radius").mode = 'CURVE_SHRINKFATTEN'
779
780         layout.separator()
781
782         layout.operator("transform.translate", text="Move Texture Space").texture_space = True
783         layout.operator("transform.resize", text="Scale Texture Space").texture_space = True
784
785
786 # Object-specific extensions to Transform menu
787 class VIEW3D_MT_transform_object(VIEW3D_MT_transform_base):
788     def draw(self, context):
789         layout = self.layout
790
791         # base menu
792         VIEW3D_MT_transform_base.draw(self, context)
793
794         # object-specific option follow...
795         layout.separator()
796
797         layout.operator("transform.translate", text="Move Texture Space").texture_space = True
798         layout.operator("transform.resize", text="Scale Texture Space").texture_space = True
799
800         layout.separator()
801
802         layout.operator_context = 'EXEC_REGION_WIN'
803         # XXX see alignmenu() in edit.c of b2.4x to get this working
804         layout.operator("transform.transform", text="Align to Transform Orientation").mode = 'ALIGN'
805
806         layout.separator()
807
808         layout.operator("object.randomize_transform")
809         layout.operator("object.align")
810
811         # TODO: there is a strange context bug here.
812         """
813         layout.operator_context = 'INVOKE_REGION_WIN'
814         layout.operator("object.transform_axis_target")
815         """
816
817
818 # Armature EditMode extensions to Transform menu
819 class VIEW3D_MT_transform_armature(VIEW3D_MT_transform_base):
820     def draw(self, context):
821         layout = self.layout
822
823         # base menu
824         VIEW3D_MT_transform_base.draw(self, context)
825
826         # armature specific extensions follow...
827         obj = context.object
828         if obj.type == 'ARMATURE' and obj.mode in {'EDIT', 'POSE'}:
829             if obj.data.display_type == 'BBONE':
830                 layout.separator()
831
832                 layout.operator("transform.transform", text="Scale BBone").mode = 'BONE_SIZE'
833             elif obj.data.display_type == 'ENVELOPE':
834                 layout.separator()
835
836                 layout.operator("transform.transform", text="Scale Envelope Distance").mode = 'BONE_SIZE'
837                 layout.operator("transform.transform", text="Scale Radius").mode = 'BONE_ENVELOPE'
838
839         if context.edit_object and context.edit_object.type == 'ARMATURE':
840             layout.separator()
841
842             layout.operator("armature.align")
843
844
845 class VIEW3D_MT_mirror(Menu):
846     bl_label = "Mirror"
847
848     def draw(self, _context):
849         layout = self.layout
850
851         layout.operator("transform.mirror", text="Interactive Mirror")
852
853         layout.separator()
854
855         layout.operator_context = 'EXEC_REGION_WIN'
856
857         for (space_name, space_id) in (("Global", 'GLOBAL'), ("Local", 'LOCAL')):
858             for axis_index, axis_name in enumerate("XYZ"):
859                 props = layout.operator("transform.mirror", text=f"{axis_name!s} {space_name!s}")
860                 props.constraint_axis[axis_index] = True
861                 props.orient_type = 'GLOBAL'
862
863             if space_id == 'GLOBAL':
864                 layout.separator()
865
866
867 class VIEW3D_MT_snap(Menu):
868     bl_label = "Snap"
869
870     def draw(self, _context):
871         layout = self.layout
872
873         layout.operator("view3d.snap_selected_to_grid", text="Selection to Grid")
874         layout.operator("view3d.snap_selected_to_cursor", text="Selection to Cursor").use_offset = False
875         layout.operator("view3d.snap_selected_to_cursor", text="Selection to Cursor (Keep Offset)").use_offset = True
876         layout.operator("view3d.snap_selected_to_active", text="Selection to Active")
877
878         layout.separator()
879
880         layout.operator("view3d.snap_cursor_to_selected", text="Cursor to Selected")
881         layout.operator("view3d.snap_cursor_to_center", text="Cursor to World Origin")
882         layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
883         layout.operator("view3d.snap_cursor_to_active", text="Cursor to Active")
884
885
886 class VIEW3D_MT_uv_map(Menu):
887     bl_label = "UV Mapping"
888
889     def draw(self, _context):
890         layout = self.layout
891
892         layout.operator("uv.unwrap")
893
894         layout.operator_context = 'INVOKE_DEFAULT'
895         layout.operator("uv.smart_project")
896         layout.operator("uv.lightmap_pack")
897         layout.operator("uv.follow_active_quads")
898
899         layout.separator()
900
901         layout.operator_context = 'EXEC_REGION_WIN'
902         layout.operator("uv.cube_project")
903         layout.operator("uv.cylinder_project")
904         layout.operator("uv.sphere_project")
905
906         layout.separator()
907
908         layout.operator_context = 'INVOKE_REGION_WIN'
909         layout.operator("uv.project_from_view").scale_to_bounds = False
910         layout.operator("uv.project_from_view", text="Project from View (Bounds)").scale_to_bounds = True
911
912         layout.separator()
913
914         layout.operator("mesh.mark_seam").clear = False
915         layout.operator("mesh.mark_seam", text="Clear Seam").clear = True
916
917         layout.separator()
918
919         layout.operator("uv.reset")
920
921
922 # ********** View menus **********
923
924
925 class VIEW3D_MT_view(Menu):
926     bl_label = "View"
927
928     def draw(self, context):
929         layout = self.layout
930         view = context.space_data
931
932         layout.prop(view, "show_region_toolbar")
933         layout.prop(view, "show_region_ui")
934         layout.prop(view, "show_region_hud")
935
936         layout.separator()
937
938         layout.operator("view3d.view_selected", text="Frame Selected").use_all_regions = False
939         if view.region_quadviews:
940             layout.operator("view3d.view_selected", text="Frame Selected (Quad View)").use_all_regions = True
941
942         layout.operator("view3d.view_all", text="Frame All").center = False
943         layout.operator("view3d.view_persportho", text="Perspective/Orthographic")
944         layout.menu("VIEW3D_MT_view_local")
945
946         layout.separator()
947
948         layout.menu("VIEW3D_MT_view_cameras", text="Cameras")
949
950         layout.separator()
951         layout.menu("VIEW3D_MT_view_viewpoint")
952         layout.menu("VIEW3D_MT_view_navigation")
953         layout.menu("VIEW3D_MT_view_align")
954
955         layout.separator()
956
957         layout.operator_context = 'INVOKE_REGION_WIN'
958         layout.menu("VIEW3D_MT_view_regions", text="View Regions")
959
960         layout.separator()
961
962         layout.operator("screen.animation_play", text="Play Animation")
963
964         layout.separator()
965
966         layout.operator("render.opengl", text="Viewport Render Image", icon='RENDER_STILL')
967         layout.operator("render.opengl", text="Viewport Render Animation", icon='RENDER_ANIMATION').animation = True
968
969         layout.separator()
970
971         layout.menu("INFO_MT_area")
972
973
974 class VIEW3D_MT_view_local(Menu):
975     bl_label = "Local View"
976
977     def draw(self, _context):
978         layout = self.layout
979
980         layout.operator("view3d.localview", text="Toggle Local View")
981         layout.operator("view3d.localview_remove_from")
982
983
984 class VIEW3D_MT_view_cameras(Menu):
985     bl_label = "Cameras"
986
987     def draw(self, _context):
988         layout = self.layout
989
990         layout.operator("view3d.object_as_camera")
991         layout.operator("view3d.view_camera", text="Active Camera")
992
993
994 class VIEW3D_MT_view_viewpoint(Menu):
995     bl_label = "Viewpoint"
996
997     def draw(self, _context):
998         layout = self.layout
999
1000         layout.operator("view3d.view_camera", text="Camera")
1001
1002         layout.separator()
1003
1004         layout.operator("view3d.view_axis", text="Top").type = 'TOP'
1005         layout.operator("view3d.view_axis", text="Bottom").type = 'BOTTOM'
1006
1007         layout.separator()
1008
1009         layout.operator("view3d.view_axis", text="Front").type = 'FRONT'
1010         layout.operator("view3d.view_axis", text="Back").type = 'BACK'
1011
1012         layout.separator()
1013
1014         layout.operator("view3d.view_axis", text="Right").type = 'RIGHT'
1015         layout.operator("view3d.view_axis", text="Left").type = 'LEFT'
1016
1017
1018 class VIEW3D_MT_view_navigation(Menu):
1019     bl_label = "Navigation"
1020
1021     def draw(self, _context):
1022         from math import pi
1023         layout = self.layout
1024
1025         layout.operator_enum("view3d.view_orbit", "type")
1026         props = layout.operator("view3d.view_orbit", text="Orbit Opposite")
1027         props.type = 'ORBITRIGHT'
1028         props.angle = pi
1029
1030         layout.separator()
1031
1032         layout.operator("view3d.view_roll", text="Roll Left").type = 'LEFT'
1033         layout.operator("view3d.view_roll", text="Roll Right").type = 'RIGHT'
1034
1035         layout.separator()
1036
1037         layout.operator_enum("view3d.view_pan", "type")
1038
1039         layout.separator()
1040
1041         layout.operator("view3d.zoom", text="Zoom In").delta = 1
1042         layout.operator("view3d.zoom", text="Zoom Out").delta = -1
1043         layout.operator("view3d.zoom_border", text="Zoom Border...")
1044         layout.operator("view3d.zoom_camera_1_to_1", text="Zoom Camera 1:1")
1045
1046         layout.separator()
1047
1048         layout.operator("view3d.fly")
1049         layout.operator("view3d.walk")
1050
1051
1052 class VIEW3D_MT_view_align(Menu):
1053     bl_label = "Align View"
1054
1055     def draw(self, _context):
1056         layout = self.layout
1057
1058         layout.menu("VIEW3D_MT_view_align_selected")
1059
1060         layout.separator()
1061
1062         layout.operator("view3d.camera_to_view", text="Align Active Camera to View")
1063         layout.operator("view3d.camera_to_view_selected", text="Align Active Camera to Selected")
1064
1065         layout.separator()
1066
1067         layout.operator("view3d.view_all", text="Center Cursor and View All").center = True
1068         layout.operator("view3d.view_center_cursor")
1069
1070         layout.separator()
1071
1072         layout.operator("view3d.view_lock_to_active")
1073         layout.operator("view3d.view_lock_clear")
1074
1075
1076 class VIEW3D_MT_view_align_selected(Menu):
1077     bl_label = "Align View to Active"
1078
1079     def draw(self, _context):
1080         layout = self.layout
1081
1082         props = layout.operator("view3d.view_axis", text="Top")
1083         props.align_active = True
1084         props.type = 'TOP'
1085
1086         props = layout.operator("view3d.view_axis", text="Bottom")
1087         props.align_active = True
1088         props.type = 'BOTTOM'
1089
1090         layout.separator()
1091
1092         props = layout.operator("view3d.view_axis", text="Front")
1093         props.align_active = True
1094         props.type = 'FRONT'
1095
1096         props = layout.operator("view3d.view_axis", text="Back")
1097         props.align_active = True
1098         props.type = 'BACK'
1099
1100         layout.separator()
1101
1102         props = layout.operator("view3d.view_axis", text="Right")
1103         props.align_active = True
1104         props.type = 'RIGHT'
1105
1106         props = layout.operator("view3d.view_axis", text="Left")
1107         props.align_active = True
1108         props.type = 'LEFT'
1109
1110
1111 class VIEW3D_MT_view_regions(Menu):
1112     bl_label = "View Regions"
1113
1114     def draw(self, _context):
1115         layout = self.layout
1116         layout.operator("view3d.clip_border", text="Clipping Region...")
1117         layout.operator("view3d.render_border", text="Render Region...")
1118
1119         layout.separator()
1120
1121         layout.operator("view3d.clear_render_border")
1122
1123
1124 # ********** Select menus, suffix from context.mode **********
1125
1126 class VIEW3D_MT_select_object_more_less(Menu):
1127     bl_label = "Select More/Less"
1128
1129     def draw(self, _context):
1130         layout = self.layout
1131
1132         layout = self.layout
1133
1134         layout.operator("object.select_more", text="More")
1135         layout.operator("object.select_less", text="Less")
1136
1137         layout.separator()
1138
1139         props = layout.operator("object.select_hierarchy", text="Parent")
1140         props.extend = False
1141         props.direction = 'PARENT'
1142
1143         props = layout.operator("object.select_hierarchy", text="Child")
1144         props.extend = False
1145         props.direction = 'CHILD'
1146
1147         layout.separator()
1148
1149         props = layout.operator("object.select_hierarchy", text="Extend Parent")
1150         props.extend = True
1151         props.direction = 'PARENT'
1152
1153         props = layout.operator("object.select_hierarchy", text="Extend Child")
1154         props.extend = True
1155         props.direction = 'CHILD'
1156
1157
1158 class VIEW3D_MT_select_object(Menu):
1159     bl_label = "Select"
1160
1161     def draw(self, _context):
1162         layout = self.layout
1163
1164         layout.operator("object.select_all", text="All").action = 'SELECT'
1165         layout.operator("object.select_all", text="None").action = 'DESELECT'
1166         layout.operator("object.select_all", text="Invert").action = 'INVERT'
1167
1168         layout.separator()
1169
1170         layout.operator("view3d.select_box")
1171         layout.operator("view3d.select_circle")
1172
1173         layout.separator()
1174
1175         layout.operator_menu_enum("object.select_by_type", "type", text="Select All by Type...")
1176         layout.operator("object.select_camera", text="Select Active Camera")
1177         layout.operator("object.select_mirror", text="Mirror Selection")
1178         layout.operator("object.select_random", text="Select Random")
1179
1180         layout.separator()
1181
1182         layout.menu("VIEW3D_MT_select_object_more_less")
1183
1184         layout.separator()
1185
1186         layout.operator_menu_enum("object.select_grouped", "type", text="Select Grouped")
1187         layout.operator_menu_enum("object.select_linked", "type", text="Select Linked")
1188         layout.operator("object.select_pattern", text="Select Pattern...")
1189
1190
1191 class VIEW3D_MT_select_pose_more_less(Menu):
1192     bl_label = "Select More/Less"
1193
1194     def draw(self, _context):
1195         layout = self.layout
1196
1197         layout = self.layout
1198
1199         props = layout.operator("pose.select_hierarchy", text="Parent")
1200         props.extend = False
1201         props.direction = 'PARENT'
1202
1203         props = layout.operator("pose.select_hierarchy", text="Child")
1204         props.extend = False
1205         props.direction = 'CHILD'
1206
1207         layout.separator()
1208
1209         props = layout.operator("pose.select_hierarchy", text="Extend Parent")
1210         props.extend = True
1211         props.direction = 'PARENT'
1212
1213         props = layout.operator("pose.select_hierarchy", text="Extend Child")
1214         props.extend = True
1215         props.direction = 'CHILD'
1216
1217
1218 class VIEW3D_MT_select_pose(Menu):
1219     bl_label = "Select"
1220
1221     def draw(self, _context):
1222         layout = self.layout
1223
1224         layout.operator("pose.select_all", text="All").action = 'SELECT'
1225         layout.operator("pose.select_all", text="None").action = 'DESELECT'
1226         layout.operator("pose.select_all", text="Invert").action = 'INVERT'
1227
1228         layout.separator()
1229
1230         layout.operator("view3d.select_box")
1231         layout.operator("view3d.select_circle")
1232
1233         layout.separator()
1234
1235         layout.operator("pose.select_mirror", text="Flip Active")
1236
1237         layout.separator()
1238
1239         layout.operator("pose.select_constraint_target", text="Constraint Target")
1240         layout.operator("pose.select_linked", text="Linked")
1241
1242         layout.separator()
1243
1244         layout.menu("VIEW3D_MT_select_pose_more_less")
1245
1246         layout.separator()
1247
1248         layout.operator_menu_enum("pose.select_grouped", "type", text="Grouped")
1249         layout.operator("object.select_pattern", text="Select Pattern...")
1250
1251
1252 class VIEW3D_MT_select_particle(Menu):
1253     bl_label = "Select"
1254
1255     def draw(self, _context):
1256         layout = self.layout
1257
1258         layout.operator("particle.select_all", text="All").action = 'SELECT'
1259         layout.operator("particle.select_all", text="None").action = 'DESELECT'
1260         layout.operator("particle.select_all", text="Invert").action = 'INVERT'
1261
1262         layout.separator()
1263
1264         layout.operator("view3d.select_box")
1265         layout.operator("view3d.select_circle")
1266
1267         layout.separator()
1268
1269         layout.operator("particle.select_linked")
1270
1271         layout.separator()
1272
1273         layout.operator("particle.select_more")
1274         layout.operator("particle.select_less")
1275
1276         layout.separator()
1277
1278         layout.operator("particle.select_random")
1279
1280         layout.separator()
1281
1282         layout.operator("particle.select_roots", text="Roots")
1283         layout.operator("particle.select_tips", text="Tips")
1284
1285
1286 class VIEW3D_MT_edit_mesh_select_similar(Menu):
1287     bl_label = "Select Similar"
1288
1289     def draw(self, _context):
1290         layout = self.layout
1291
1292         layout.operator_enum("mesh.select_similar", "type")
1293
1294         layout.separator()
1295
1296         layout.operator("mesh.select_similar_region", text="Face Regions")
1297
1298
1299 class VIEW3D_MT_edit_mesh_select_by_trait(Menu):
1300     bl_label = "Select All by Trait"
1301
1302     def draw(self, context):
1303         layout = self.layout
1304         tool_settings = context.tool_settings
1305         if tool_settings.mesh_select_mode[2] is False:
1306             layout.operator("mesh.select_non_manifold", text="Non Manifold")
1307         layout.operator("mesh.select_loose", text="Loose Geometry")
1308         layout.operator("mesh.select_interior_faces", text="Interior Faces")
1309         layout.operator("mesh.select_face_by_sides", text="Faces by Sides")
1310
1311         layout.separator()
1312
1313         layout.operator("mesh.select_ungrouped", text="Ungrouped Verts")
1314
1315
1316 class VIEW3D_MT_edit_mesh_select_more_less(Menu):
1317     bl_label = "Select More/Less"
1318
1319     def draw(self, _context):
1320         layout = self.layout
1321
1322         layout.operator("mesh.select_more", text="More")
1323         layout.operator("mesh.select_less", text="Less")
1324
1325         layout.separator()
1326
1327         layout.operator("mesh.select_next_item", text="Next Active")
1328         layout.operator("mesh.select_prev_item", text="Previous Active")
1329
1330
1331 class VIEW3D_MT_edit_mesh_select_linked(Menu):
1332     bl_label = "Select Linked"
1333
1334     def draw(self, _context):
1335         layout = self.layout
1336
1337         layout.operator("mesh.select_linked", text="Linked")
1338         layout.operator("mesh.shortest_path_select", text="Shortest Path")
1339         layout.operator("mesh.faces_select_linked_flat", text="Linked Flat Faces")
1340
1341
1342 class VIEW3D_MT_edit_mesh_select_loops(Menu):
1343     bl_label = "Select Loops"
1344
1345     def draw(self, _context):
1346         layout = self.layout
1347
1348         layout.operator("mesh.loop_multi_select", text="Edge Loops").ring = False
1349         layout.operator("mesh.loop_multi_select", text="Edge Rings").ring = True
1350
1351         layout.separator()
1352
1353         layout.operator("mesh.loop_to_region")
1354         layout.operator("mesh.region_to_loop")
1355
1356
1357 class VIEW3D_MT_select_edit_mesh(Menu):
1358     bl_label = "Select"
1359
1360     def draw(self, _context):
1361         layout = self.layout
1362
1363         # primitive
1364         layout.operator("mesh.select_all", text="All").action = 'SELECT'
1365         layout.operator("mesh.select_all", text="None").action = 'DESELECT'
1366         layout.operator("mesh.select_all", text="Invert").action = 'INVERT'
1367
1368         layout.separator()
1369
1370         layout.operator("view3d.select_box")
1371         layout.operator("view3d.select_circle")
1372
1373         layout.separator()
1374
1375         # numeric
1376         layout.operator("mesh.select_random", text="Select Random")
1377         layout.operator("mesh.select_nth")
1378
1379         layout.separator()
1380
1381         # geometric
1382         layout.operator("mesh.edges_select_sharp", text="Select Sharp Edges")
1383
1384         layout.separator()
1385
1386         # other ...
1387         layout.menu("VIEW3D_MT_edit_mesh_select_similar")
1388
1389         layout.separator()
1390
1391         layout.menu("VIEW3D_MT_edit_mesh_select_by_trait")
1392
1393         layout.separator()
1394
1395         layout.menu("VIEW3D_MT_edit_mesh_select_more_less")
1396
1397         layout.separator()
1398
1399         layout.menu("VIEW3D_MT_edit_mesh_select_loops")
1400
1401         layout.separator()
1402
1403         layout.menu("VIEW3D_MT_edit_mesh_select_linked")
1404
1405         layout.separator()
1406
1407         layout.operator("mesh.select_axis", text="Side of Active")
1408         layout.operator("mesh.select_mirror", text="Mirror Selection")
1409
1410
1411 class VIEW3D_MT_select_edit_curve(Menu):
1412     bl_label = "Select"
1413
1414     def draw(self, _context):
1415         layout = self.layout
1416
1417         layout.operator("curve.select_all", text="All").action = 'SELECT'
1418         layout.operator("curve.select_all", text="None").action = 'DESELECT'
1419         layout.operator("curve.select_all", text="Invert").action = 'INVERT'
1420
1421         layout.separator()
1422
1423         layout.operator("view3d.select_box")
1424         layout.operator("view3d.select_circle")
1425
1426         layout.separator()
1427
1428         layout.operator("curve.select_random")
1429         layout.operator("curve.select_nth")
1430         layout.operator("curve.select_linked", text="Select Linked")
1431         layout.operator("curve.select_similar", text="Select Similar")
1432
1433         layout.separator()
1434
1435         layout.operator("curve.de_select_first")
1436         layout.operator("curve.de_select_last")
1437         layout.operator("curve.select_next")
1438         layout.operator("curve.select_previous")
1439
1440         layout.separator()
1441
1442         layout.operator("curve.select_more")
1443         layout.operator("curve.select_less")
1444
1445
1446 class VIEW3D_MT_select_edit_surface(Menu):
1447     bl_label = "Select"
1448
1449     def draw(self, _context):
1450         layout = self.layout
1451
1452         layout.operator("curve.select_all", text="All").action = 'SELECT'
1453         layout.operator("curve.select_all", text="None").action = 'DESELECT'
1454         layout.operator("curve.select_all", text="Invert").action = 'INVERT'
1455
1456         layout.separator()
1457
1458         layout.operator("view3d.select_box")
1459         layout.operator("view3d.select_circle")
1460
1461         layout.separator()
1462
1463         layout.operator("curve.select_random")
1464         layout.operator("curve.select_nth")
1465         layout.operator("curve.select_linked", text="Select Linked")
1466         layout.operator("curve.select_similar", text="Select Similar")
1467
1468         layout.separator()
1469
1470         layout.operator("curve.select_row")
1471
1472         layout.separator()
1473
1474         layout.operator("curve.select_more")
1475         layout.operator("curve.select_less")
1476
1477
1478 class VIEW3D_MT_edit_text_context_menu(Menu):
1479     bl_label = "Text Context Menu"
1480
1481     def draw(self, _context):
1482         layout = self.layout
1483
1484         layout = self.layout
1485
1486         layout.operator_context = 'INVOKE_DEFAULT'
1487
1488         layout.operator("font.text_cut", text="Cut")
1489         layout.operator("font.text_copy", text="Copy", icon='COPYDOWN')
1490         layout.operator("font.text_paste", text="Paste", icon='PASTEDOWN')
1491
1492         layout.separator()
1493
1494         layout.operator("font.select_all")
1495
1496         layout.separator()
1497
1498         layout.menu("VIEW3D_MT_edit_font")
1499
1500
1501 class VIEW3D_MT_select_edit_text(Menu):
1502     # intentional name mismatch
1503     # select menu for 3d-text doesn't make sense
1504     bl_label = "Edit"
1505
1506     def draw(self, _context):
1507         layout = self.layout
1508
1509         layout.operator("font.text_cut", text="Cut")
1510         layout.operator("font.text_copy", text="Copy", icon='COPYDOWN')
1511         layout.operator("font.text_paste", text="Paste", icon='PASTEDOWN')
1512
1513         layout.separator()
1514
1515         layout.operator("font.text_paste_from_file")
1516
1517         layout.separator()
1518
1519         layout.operator("font.select_all")
1520
1521         layout.menu("VIEW3D_MT_edit_font")
1522
1523
1524 class VIEW3D_MT_select_edit_metaball(Menu):
1525     bl_label = "Select"
1526
1527     def draw(self, _context):
1528         layout = self.layout
1529
1530         layout.operator("mball.select_all", text="All").action = 'SELECT'
1531         layout.operator("mball.select_all", text="None").action = 'DESELECT'
1532         layout.operator("mball.select_all", text="Invert").action = 'INVERT'
1533
1534         layout.separator()
1535
1536         layout.operator("view3d.select_box")
1537         layout.operator("view3d.select_circle")
1538
1539         layout.separator()
1540
1541         layout.operator("mball.select_random_metaelems")
1542
1543         layout.separator()
1544
1545         layout.operator_menu_enum("mball.select_similar", "type", text="Similar")
1546
1547
1548 class VIEW3D_MT_edit_lattice_context_menu(Menu):
1549     bl_label = "Lattice Context Menu"
1550
1551     def draw(self, _context):
1552         layout = self.layout
1553
1554         layout = self.layout
1555
1556         layout.menu("VIEW3D_MT_mirror")
1557         layout.operator_menu_enum("lattice.flip", "axis")
1558         layout.menu("VIEW3D_MT_snap")
1559
1560         layout.separator()
1561
1562         layout.operator("lattice.make_regular")
1563
1564
1565 class VIEW3D_MT_select_edit_lattice(Menu):
1566     bl_label = "Select"
1567
1568     def draw(self, _context):
1569         layout = self.layout
1570
1571         layout.operator("lattice.select_all", text="All").action = 'SELECT'
1572         layout.operator("lattice.select_all", text="None").action = 'DESELECT'
1573         layout.operator("lattice.select_all", text="Invert").action = 'INVERT'
1574
1575         layout.separator()
1576
1577         layout.operator("view3d.select_box")
1578         layout.operator("view3d.select_circle")
1579
1580         layout.separator()
1581
1582         layout.operator("lattice.select_mirror")
1583         layout.operator("lattice.select_random")
1584
1585         layout.separator()
1586
1587         layout.operator("lattice.select_more")
1588         layout.operator("lattice.select_less")
1589
1590         layout.separator()
1591
1592         layout.operator("lattice.select_ungrouped", text="Ungrouped Verts")
1593
1594
1595 class VIEW3D_MT_select_edit_armature(Menu):
1596     bl_label = "Select"
1597
1598     def draw(self, _context):
1599         layout = self.layout
1600
1601         layout.operator("armature.select_all", text="All").action = 'SELECT'
1602         layout.operator("armature.select_all", text="None").action = 'DESELECT'
1603         layout.operator("armature.select_all", text="Invert").action = 'INVERT'
1604
1605         layout.separator()
1606
1607         layout.operator("view3d.select_box")
1608         layout.operator("view3d.select_circle")
1609
1610         layout.separator()
1611
1612         layout.operator("armature.select_mirror", text="Mirror").extend = False
1613
1614         layout.separator()
1615
1616         layout.operator("armature.select_more", text="More")
1617         layout.operator("armature.select_less", text="Less")
1618
1619         layout.separator()
1620
1621         props = layout.operator("armature.select_hierarchy", text="Parent")
1622         props.extend = False
1623         props.direction = 'PARENT'
1624
1625         props = layout.operator("armature.select_hierarchy", text="Child")
1626         props.extend = False
1627         props.direction = 'CHILD'
1628
1629         layout.separator()
1630
1631         props = layout.operator("armature.select_hierarchy", text="Extend Parent")
1632         props.extend = True
1633         props.direction = 'PARENT'
1634
1635         props = layout.operator("armature.select_hierarchy", text="Extend Child")
1636         props.extend = True
1637         props.direction = 'CHILD'
1638
1639         layout.operator_menu_enum("armature.select_similar", "type", text="Similar")
1640         layout.operator("object.select_pattern", text="Select Pattern...")
1641
1642
1643 class VIEW3D_MT_select_gpencil(Menu):
1644     bl_label = "Select"
1645
1646     def draw(self, _context):
1647         layout = self.layout
1648
1649         layout.operator("gpencil.select_all", text="All").action = 'SELECT'
1650         layout.operator("gpencil.select_all", text="None").action = 'DESELECT'
1651         layout.operator("gpencil.select_all", text="Invert").action = 'INVERT'
1652
1653         layout.separator()
1654
1655         layout.operator("gpencil.select_box")
1656         layout.operator("gpencil.select_circle")
1657
1658         layout.separator()
1659
1660         layout.operator("gpencil.select_linked", text="Linked")
1661         layout.operator("gpencil.select_alternate")
1662         layout.operator_menu_enum("gpencil.select_grouped", "type", text="Grouped")
1663
1664         layout.separator()
1665
1666         layout.operator("gpencil.select_first")
1667         layout.operator("gpencil.select_last")
1668
1669         layout.separator()
1670
1671         layout.operator("gpencil.select_more")
1672         layout.operator("gpencil.select_less")
1673
1674
1675 class VIEW3D_MT_select_paint_mask(Menu):
1676     bl_label = "Select"
1677
1678     def draw(self, _context):
1679         layout = self.layout
1680
1681         layout.operator("paint.face_select_all", text="All").action = 'SELECT'
1682         layout.operator("paint.face_select_all", text="None").action = 'DESELECT'
1683         layout.operator("paint.face_select_all", text="Invert").action = 'INVERT'
1684
1685         layout.separator()
1686
1687         layout.operator("view3d.select_box")
1688         layout.operator("view3d.select_circle")
1689
1690         layout.separator()
1691
1692         layout.operator("paint.face_select_linked", text="Linked")
1693
1694
1695 class VIEW3D_MT_select_paint_mask_vertex(Menu):
1696     bl_label = "Select"
1697
1698     def draw(self, _context):
1699         layout = self.layout
1700
1701         layout.operator("paint.vert_select_all", text="All").action = 'SELECT'
1702         layout.operator("paint.vert_select_all", text="None").action = 'DESELECT'
1703         layout.operator("paint.vert_select_all", text="Invert").action = 'INVERT'
1704
1705         layout.separator()
1706
1707         layout.operator("view3d.select_box")
1708         layout.operator("view3d.select_circle")
1709
1710         layout.separator()
1711
1712         layout.operator("paint.vert_select_ungrouped", text="Ungrouped Verts")
1713
1714
1715 class VIEW3D_MT_angle_control(Menu):
1716     bl_label = "Angle Control"
1717
1718     @classmethod
1719     def poll(cls, context):
1720         settings = UnifiedPaintPanel.paint_settings(context)
1721         if not settings:
1722             return False
1723
1724         brush = settings.brush
1725         tex_slot = brush.texture_slot
1726
1727         return tex_slot.has_texture_angle and tex_slot.has_texture_angle_source
1728
1729     def draw(self, context):
1730         layout = self.layout
1731
1732         settings = UnifiedPaintPanel.paint_settings(context)
1733         brush = settings.brush
1734
1735         sculpt = (context.sculpt_object is not None)
1736
1737         tex_slot = brush.texture_slot
1738
1739         layout.prop(tex_slot, "use_rake", text="Rake")
1740
1741         if brush.brush_capabilities.has_random_texture_angle and tex_slot.has_random_texture_angle:
1742             if sculpt:
1743                 if brush.sculpt_capabilities.has_random_texture_angle:
1744                     layout.prop(tex_slot, "use_random", text="Random")
1745             else:
1746                 layout.prop(tex_slot, "use_random", text="Random")
1747
1748
1749 class VIEW3D_MT_mesh_add(Menu):
1750     bl_idname = "VIEW3D_MT_mesh_add"
1751     bl_label = "Mesh"
1752
1753     def draw(self, _context):
1754         layout = self.layout
1755
1756         layout.operator_context = 'INVOKE_REGION_WIN'
1757
1758         layout.operator("mesh.primitive_plane_add", text="Plane", icon='MESH_PLANE')
1759         layout.operator("mesh.primitive_cube_add", text="Cube", icon='MESH_CUBE')
1760         layout.operator("mesh.primitive_circle_add", text="Circle", icon='MESH_CIRCLE')
1761         layout.operator("mesh.primitive_uv_sphere_add", text="UV Sphere", icon='MESH_UVSPHERE')
1762         layout.operator("mesh.primitive_ico_sphere_add", text="Ico Sphere", icon='MESH_ICOSPHERE')
1763         layout.operator("mesh.primitive_cylinder_add", text="Cylinder", icon='MESH_CYLINDER')
1764         layout.operator("mesh.primitive_cone_add", text="Cone", icon='MESH_CONE')
1765         layout.operator("mesh.primitive_torus_add", text="Torus", icon='MESH_TORUS')
1766
1767         layout.separator()
1768
1769         layout.operator("mesh.primitive_grid_add", text="Grid", icon='MESH_GRID')
1770         layout.operator("mesh.primitive_monkey_add", text="Monkey", icon='MESH_MONKEY')
1771
1772
1773 class VIEW3D_MT_curve_add(Menu):
1774     bl_idname = "VIEW3D_MT_curve_add"
1775     bl_label = "Curve"
1776
1777     def draw(self, _context):
1778         layout = self.layout
1779
1780         layout.operator_context = 'INVOKE_REGION_WIN'
1781
1782         layout.operator("curve.primitive_bezier_curve_add", text="Bezier", icon='CURVE_BEZCURVE')
1783         layout.operator("curve.primitive_bezier_circle_add", text="Circle", icon='CURVE_BEZCIRCLE')
1784
1785         layout.separator()
1786
1787         layout.operator("curve.primitive_nurbs_curve_add", text="Nurbs Curve", icon='CURVE_NCURVE')
1788         layout.operator("curve.primitive_nurbs_circle_add", text="Nurbs Circle", icon='CURVE_NCIRCLE')
1789         layout.operator("curve.primitive_nurbs_path_add", text="Path", icon='CURVE_PATH')
1790
1791
1792 class VIEW3D_MT_surface_add(Menu):
1793     bl_idname = "VIEW3D_MT_surface_add"
1794     bl_label = "Surface"
1795
1796     def draw(self, _context):
1797         layout = self.layout
1798
1799         layout.operator_context = 'INVOKE_REGION_WIN'
1800
1801         layout.operator("surface.primitive_nurbs_surface_curve_add", text="Nurbs Curve", icon='SURFACE_NCURVE')
1802         layout.operator("surface.primitive_nurbs_surface_circle_add", text="Nurbs Circle", icon='SURFACE_NCIRCLE')
1803         layout.operator("surface.primitive_nurbs_surface_surface_add", text="Nurbs Surface", icon='SURFACE_NSURFACE')
1804         layout.operator("surface.primitive_nurbs_surface_cylinder_add",
1805                         text="Nurbs Cylinder", icon='SURFACE_NCYLINDER')
1806         layout.operator("surface.primitive_nurbs_surface_sphere_add", text="Nurbs Sphere", icon='SURFACE_NSPHERE')
1807         layout.operator("surface.primitive_nurbs_surface_torus_add", text="Nurbs Torus", icon='SURFACE_NTORUS')
1808
1809
1810 class VIEW3D_MT_edit_metaball_context_menu(Menu):
1811     bl_label = "Metaball Context Menu"
1812
1813     def draw(self, _context):
1814         layout = self.layout
1815
1816         layout.operator_context = 'INVOKE_REGION_WIN'
1817
1818         # Add
1819         layout.operator("mball.duplicate_move")
1820
1821         layout.separator()
1822
1823         # Modify
1824         layout.menu("VIEW3D_MT_mirror")
1825         layout.menu("VIEW3D_MT_snap")
1826
1827         layout.separator()
1828
1829         # Remove
1830         layout.operator_context = 'EXEC_DEFAULT'
1831         layout.operator("mball.delete_metaelems", text="Delete")
1832
1833
1834 class VIEW3D_MT_metaball_add(Menu):
1835     bl_idname = "VIEW3D_MT_metaball_add"
1836     bl_label = "Metaball"
1837
1838     def draw(self, _context):
1839         layout = self.layout
1840
1841         layout.operator_context = 'INVOKE_REGION_WIN'
1842         layout.operator_enum("object.metaball_add", "type")
1843
1844
1845 class TOPBAR_MT_edit_curve_add(Menu):
1846     bl_idname = "TOPBAR_MT_edit_curve_add"
1847     bl_label = "Add"
1848
1849     def draw(self, context):
1850         is_surf = context.active_object.type == 'SURFACE'
1851
1852         layout = self.layout
1853         layout.operator_context = 'EXEC_REGION_WIN'
1854
1855         if is_surf:
1856             VIEW3D_MT_surface_add.draw(self, context)
1857         else:
1858             VIEW3D_MT_curve_add.draw(self, context)
1859
1860
1861 class TOPBAR_MT_edit_armature_add(Menu):
1862     bl_idname = "TOPBAR_MT_edit_armature_add"
1863     bl_label = "Armature"
1864
1865     def draw(self, _context):
1866         layout = self.layout
1867
1868         layout.operator_context = 'EXEC_REGION_WIN'
1869         layout.operator("armature.bone_primitive_add", text="Single Bone", icon='BONE_DATA')
1870
1871
1872 class VIEW3D_MT_armature_add(Menu):
1873     bl_idname = "VIEW3D_MT_armature_add"
1874     bl_label = "Armature"
1875
1876     def draw(self, _context):
1877         layout = self.layout
1878
1879         layout.operator_context = 'EXEC_REGION_WIN'
1880         layout.operator("object.armature_add", text="Single Bone", icon='BONE_DATA')
1881
1882
1883 class VIEW3D_MT_light_add(Menu):
1884     bl_idname = "VIEW3D_MT_light_add"
1885     bl_label = "Light"
1886
1887     def draw(self, _context):
1888         layout = self.layout
1889
1890         layout.operator_context = 'INVOKE_REGION_WIN'
1891         layout.operator_enum("object.light_add", "type")
1892
1893
1894 class VIEW3D_MT_lightprobe_add(Menu):
1895     bl_idname = "VIEW3D_MT_lightprobe_add"
1896     bl_label = "Light Probe"
1897
1898     def draw(self, _context):
1899         layout = self.layout
1900
1901         layout.operator_context = 'INVOKE_REGION_WIN'
1902         layout.operator_enum("object.lightprobe_add", "type")
1903
1904
1905 class VIEW3D_MT_camera_add(Menu):
1906     bl_idname = "VIEW3D_MT_camera_add"
1907     bl_label = "Camera"
1908
1909     def draw(self, _context):
1910         layout = self.layout
1911         layout.operator_context = 'EXEC_REGION_WIN'
1912         layout.operator("object.camera_add", text="Camera", icon='OUTLINER_OB_CAMERA')
1913
1914
1915 class VIEW3D_MT_add(Menu):
1916     bl_label = "Add"
1917
1918     def draw(self, context):
1919         layout = self.layout
1920
1921         # note, don't use 'EXEC_SCREEN' or operators won't get the 'v3d' context.
1922
1923         # Note: was EXEC_AREA, but this context does not have the 'rv3d', which prevents
1924         #       "align_view" to work on first call (see [#32719]).
1925         layout.operator_context = 'EXEC_REGION_WIN'
1926
1927         # layout.operator_menu_enum("object.mesh_add", "type", text="Mesh", icon='OUTLINER_OB_MESH')
1928         layout.menu("VIEW3D_MT_mesh_add", icon='OUTLINER_OB_MESH')
1929
1930         # layout.operator_menu_enum("object.curve_add", "type", text="Curve", icon='OUTLINER_OB_CURVE')
1931         layout.menu("VIEW3D_MT_curve_add", icon='OUTLINER_OB_CURVE')
1932         # layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE')
1933         layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE')
1934         layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META')
1935         layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT')
1936         layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
1937
1938         layout.separator()
1939
1940         if VIEW3D_MT_armature_add.is_extended():
1941             layout.menu("VIEW3D_MT_armature_add", icon='OUTLINER_OB_ARMATURE')
1942         else:
1943             layout.operator("object.armature_add", text="Armature", icon='OUTLINER_OB_ARMATURE')
1944
1945         layout.operator("object.add", text="Lattice", icon='OUTLINER_OB_LATTICE').type = 'LATTICE'
1946
1947         layout.separator()
1948
1949         layout.operator_menu_enum("object.empty_add", "type", text="Empty", icon='OUTLINER_OB_EMPTY')
1950         layout.menu("VIEW3D_MT_image_add", text="Image", icon='OUTLINER_OB_IMAGE')
1951
1952         layout.separator()
1953
1954         layout.menu("VIEW3D_MT_light_add", icon='OUTLINER_OB_LIGHT')
1955         layout.menu("VIEW3D_MT_lightprobe_add", icon='OUTLINER_OB_LIGHTPROBE')
1956
1957         layout.separator()
1958
1959         if VIEW3D_MT_camera_add.is_extended():
1960             layout.menu("VIEW3D_MT_camera_add", icon='OUTLINER_OB_CAMERA')
1961         else:
1962             VIEW3D_MT_camera_add.draw(self, context)
1963
1964         layout.separator()
1965
1966         layout.operator("object.speaker_add", text="Speaker", icon='OUTLINER_OB_SPEAKER')
1967
1968         layout.separator()
1969
1970         layout.operator_menu_enum("object.effector_add", "type", text="Force Field", icon='OUTLINER_OB_FORCE_FIELD')
1971
1972         layout.separator()
1973
1974         has_collections = bool(bpy.data.collections)
1975         col = layout.column()
1976         col.enabled = has_collections
1977
1978         if not has_collections or len(bpy.data.collections) > 10:
1979             col.operator_context = 'INVOKE_REGION_WIN'
1980             col.operator(
1981                 "object.collection_instance_add",
1982                 text="Collection Instance..." if has_collections else "No Collections to Instance",
1983                 icon='OUTLINER_OB_GROUP_INSTANCE',
1984             )
1985         else:
1986             col.operator_menu_enum(
1987                 "object.collection_instance_add",
1988                 "collection",
1989                 text="Collection Instance",
1990                 icon='OUTLINER_OB_GROUP_INSTANCE',
1991             )
1992
1993
1994 class VIEW3D_MT_image_add(Menu):
1995     bl_label = "Add Image"
1996
1997     def draw(self, _context):
1998         layout = self.layout
1999         layout.operator("object.load_reference_image", text="Reference", icon='IMAGE_REFERENCE')
2000         layout.operator("object.load_background_image", text="Background", icon='IMAGE_BACKGROUND')
2001
2002
2003 class VIEW3D_MT_object_relations(Menu):
2004     bl_label = "Relations"
2005
2006     def draw(self, _context):
2007         layout = self.layout
2008
2009         layout.operator("object.proxy_make", text="Make Proxy...")
2010
2011         layout.operator("object.make_dupli_face")
2012
2013         layout.separator()
2014
2015         layout.operator_menu_enum("object.make_local", "type", text="Make Local...")
2016         layout.menu("VIEW3D_MT_make_single_user")
2017
2018         layout.separator()
2019
2020         layout.operator("object.data_transfer")
2021         layout.operator("object.datalayout_transfer")
2022
2023
2024 class VIEW3D_MT_object(Menu):
2025     bl_context = "objectmode"
2026     bl_label = "Object"
2027
2028     def draw(self, context):
2029         layout = self.layout
2030
2031         layout.menu("VIEW3D_MT_transform_object")
2032         layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type")
2033         layout.menu("VIEW3D_MT_mirror")
2034         layout.menu("VIEW3D_MT_object_clear")
2035         layout.menu("VIEW3D_MT_object_apply")
2036         layout.menu("VIEW3D_MT_snap")
2037
2038         layout.separator()
2039
2040         layout.operator("object.duplicate_move")
2041         layout.operator("object.duplicate_move_linked")
2042         layout.operator("object.join")
2043
2044         layout.separator()
2045
2046         layout.operator("view3d.copybuffer", text="Copy Objects", icon='COPYDOWN')
2047         layout.operator("view3d.pastebuffer", text="Paste Objects", icon='PASTEDOWN')
2048
2049         layout.separator()
2050
2051         layout.menu("VIEW3D_MT_object_parent")
2052         layout.menu("VIEW3D_MT_object_collection")
2053         layout.menu("VIEW3D_MT_object_relations")
2054         layout.menu("VIEW3D_MT_object_constraints")
2055         layout.menu("VIEW3D_MT_object_track")
2056         layout.menu("VIEW3D_MT_make_links", text="Make Links")
2057
2058         layout.separator()
2059
2060         layout.operator("object.shade_smooth")
2061         layout.operator("object.shade_flat")
2062
2063         layout.separator()
2064
2065         layout.menu("VIEW3D_MT_object_animation")
2066         layout.menu("VIEW3D_MT_object_rigid_body")
2067
2068         layout.separator()
2069
2070         layout.menu("VIEW3D_MT_object_quick_effects")
2071
2072         layout.separator()
2073
2074         ob = context.active_object
2075         if ob and ob.type == 'GPENCIL' and context.gpencil_data:
2076             layout.operator_menu_enum("gpencil.convert", "type", text="Convert to")
2077         else:
2078             layout.operator_menu_enum("object.convert", "target")
2079
2080         layout.separator()
2081
2082         layout.menu("VIEW3D_MT_object_showhide")
2083
2084         layout.separator()
2085
2086         layout.operator_context = 'EXEC_DEFAULT'
2087         layout.operator("object.delete", text="Delete").use_global = False
2088         layout.operator("object.delete", text="Delete Global").use_global = True
2089
2090
2091 class VIEW3D_MT_object_animation(Menu):
2092     bl_label = "Animation"
2093
2094     def draw(self, _context):
2095         layout = self.layout
2096
2097         layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe...")
2098         layout.operator("anim.keyframe_delete_v3d", text="Delete Keyframes...")
2099         layout.operator("anim.keyframe_clear_v3d", text="Clear Keyframes...")
2100         layout.operator("anim.keying_set_active_set", text="Change Keying Set...")
2101
2102         layout.separator()
2103
2104         layout.operator("nla.bake", text="Bake Action...")
2105
2106
2107 class VIEW3D_MT_object_rigid_body(Menu):
2108     bl_label = "Rigid Body"
2109
2110     def draw(self, _context):
2111         layout = self.layout
2112
2113         layout.operator("rigidbody.objects_add", text="Add Active").type = 'ACTIVE'
2114         layout.operator("rigidbody.objects_add", text="Add Passive").type = 'PASSIVE'
2115
2116         layout.separator()
2117
2118         layout.operator("rigidbody.objects_remove", text="Remove")
2119
2120         layout.separator()
2121
2122         layout.operator("rigidbody.shape_change", text="Change Shape")
2123         layout.operator("rigidbody.mass_calculate", text="Calculate Mass")
2124         layout.operator("rigidbody.object_settings_copy", text="Copy from Active")
2125         layout.operator("object.visual_transform_apply", text="Apply Transformation")
2126         layout.operator("rigidbody.bake_to_keyframes", text="Bake To Keyframes")
2127
2128         layout.separator()
2129
2130         layout.operator("rigidbody.connect", text="Connect")
2131
2132
2133 class VIEW3D_MT_object_clear(Menu):
2134     bl_label = "Clear"
2135
2136     def draw(self, _context):
2137         layout = self.layout
2138
2139         layout.operator("object.location_clear", text="Location").clear_delta = False
2140         layout.operator("object.rotation_clear", text="Rotation").clear_delta = False
2141         layout.operator("object.scale_clear", text="Scale").clear_delta = False
2142
2143         layout.separator()
2144
2145         layout.operator("object.origin_clear", text="Origin")
2146
2147
2148 class VIEW3D_MT_object_context_menu(Menu):
2149     bl_label = "Object Context Menu"
2150
2151     def draw(self, context):
2152
2153         layout = self.layout
2154         view = context.space_data
2155
2156         obj = context.object
2157         is_eevee = context.scene.render.engine == 'BLENDER_EEVEE'
2158
2159         selected_objects_len = len(context.selected_objects)
2160
2161         # If nothing is selected
2162         # (disabled for now until it can be made more useful).
2163         '''
2164         if selected_objects_len == 0:
2165
2166             layout.menu("VIEW3D_MT_add", text="Add")
2167             layout.operator("view3d.pastebuffer", text="Paste Objects", icon='PASTEDOWN')
2168
2169             return
2170         '''
2171
2172         # If something is selected
2173         if obj is not None and obj.type in {'MESH', 'CURVE', 'SURFACE'}:
2174             layout.operator("object.shade_smooth", text="Shade Smooth")
2175             layout.operator("object.shade_flat", text="Shade Flat")
2176
2177             layout.separator()
2178
2179         if obj is None:
2180             pass
2181         elif obj.type == 'MESH':
2182             layout.operator_context = 'INVOKE_REGION_WIN'
2183             layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type")
2184
2185             layout.operator_context = 'INVOKE_DEFAULT'
2186             # If more than one object is selected
2187             if selected_objects_len > 1:
2188                 layout.operator("object.join")
2189
2190             layout.separator()
2191
2192         elif obj.type == 'CAMERA':
2193             layout.operator_context = 'INVOKE_REGION_WIN'
2194
2195             if obj.data.type == 'PERSP':
2196                 props = layout.operator("wm.context_modal_mouse", text="Camera Lens Angle")
2197                 props.data_path_iter = "selected_editable_objects"
2198                 props.data_path_item = "data.lens"
2199                 props.input_scale = 0.1
2200                 if obj.data.lens_unit == 'MILLIMETERS':
2201                     props.header_text = "Camera Lens Angle: %.1fmm"
2202                 else:
2203                     props.header_text = "Camera Lens Angle: %.1f\u00B0"
2204
2205             else:
2206                 props = layout.operator("wm.context_modal_mouse", text="Camera Lens Scale")
2207                 props.data_path_iter = "selected_editable_objects"
2208                 props.data_path_item = "data.ortho_scale"
2209                 props.input_scale = 0.01
2210                 props.header_text = "Camera Lens Scale: %.3f"
2211
2212             if not obj.data.dof_object:
2213                 if view and view.camera == obj and view.region_3d.view_perspective == 'CAMERA':
2214                     props = layout.operator("ui.eyedropper_depth", text="DOF Distance (Pick)")
2215                 else:
2216                     props = layout.operator("wm.context_modal_mouse", text="DOF Distance")
2217                     props.data_path_iter = "selected_editable_objects"
2218                     props.data_path_item = "data.dof_distance"
2219                     props.input_scale = 0.02
2220                     props.header_text = "DOF Distance: %.3f"
2221
2222             layout.separator()
2223
2224         elif obj.type in {'CURVE', 'FONT'}:
2225             layout.operator_context = 'INVOKE_REGION_WIN'
2226
2227             props = layout.operator("wm.context_modal_mouse", text="Extrude Size")
2228             props.data_path_iter = "selected_editable_objects"
2229             props.data_path_item = "data.extrude"
2230             props.input_scale = 0.01
2231             props.header_text = "Extrude Size: %.3f"
2232
2233             props = layout.operator("wm.context_modal_mouse", text="Width Size")
2234             props.data_path_iter = "selected_editable_objects"
2235             props.data_path_item = "data.offset"
2236             props.input_scale = 0.01
2237             props.header_text = "Width Size: %.3f"
2238
2239             layout.separator()
2240
2241             layout.operator("object.convert", text="Convert to Mesh").target = 'MESH'
2242             layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type")
2243
2244             layout.separator()
2245
2246         elif obj.type == 'GPENCIL':
2247             layout.operator("gpencil.convert", text="Convert to Path").type = 'PATH'
2248             layout.operator("gpencil.convert", text="Convert to Bezier Curves").type = 'CURVE'
2249             layout.operator("gpencil.convert", text="Convert to Mesh").type = 'POLY'
2250
2251             layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type")
2252
2253             layout.separator()
2254
2255         elif obj.type == 'EMPTY':
2256             layout.operator_context = 'INVOKE_REGION_WIN'
2257
2258             props = layout.operator("wm.context_modal_mouse", text="Empty Draw Size")
2259             props.data_path_iter = "selected_editable_objects"
2260             props.data_path_item = "empty_display_size"
2261             props.input_scale = 0.01
2262             props.header_text = "Empty Draw Size: %.3f"
2263
2264             layout.separator()
2265
2266         elif obj.type == 'LIGHT':
2267             light = obj.data
2268
2269             layout.operator_context = 'INVOKE_REGION_WIN'
2270
2271             emission_node = None
2272             if light.node_tree:
2273                 for node in light.node_tree.nodes:
2274                     if getattr(node, "type", None) == 'EMISSION':
2275                         emission_node = node
2276                         break
2277
2278             if is_eevee and not emission_node:
2279                 props = layout.operator("wm.context_modal_mouse", text="Energy")
2280                 props.data_path_iter = "selected_editable_objects"
2281                 props.data_path_item = "data.energy"
2282                 props.header_text = "Light Energy: %.3f"
2283
2284             if emission_node is not None:
2285                 props = layout.operator("wm.context_modal_mouse", text="Energy")
2286                 props.data_path_iter = "selected_editable_objects"
2287                 props.data_path_item = (
2288                     "data.node_tree"
2289                     ".nodes[\"" + emission_node.name + "\"]"
2290                     ".inputs[\"Strength\"].default_value"
2291                 )
2292                 props.header_text = "Light Energy: %.3f"
2293                 props.input_scale = 0.1
2294
2295             if light.type == 'AREA':
2296                 props = layout.operator("wm.context_modal_mouse", text="Size X")
2297                 props.data_path_iter = "selected_editable_objects"
2298                 props.data_path_item = "data.size"
2299                 props.header_text = "Light Size X: %.3f"
2300
2301                 if light.shape in {'RECTANGLE', 'ELLIPSE'}:
2302                     props = layout.operator("wm.context_modal_mouse", text="Size Y")
2303                     props.data_path_iter = "selected_editable_objects"
2304                     props.data_path_item = "data.size_y"
2305                     props.header_text = "Light Size Y: %.3f"
2306
2307             elif light.type in {'SPOT', 'POINT', 'SUN'}:
2308                 props = layout.operator("wm.context_modal_mouse", text="Radius")
2309                 props.data_path_iter = "selected_editable_objects"
2310                 props.data_path_item = "data.shadow_soft_size"
2311                 props.header_text = "Light Radius: %.3f"
2312
2313             if light.type == 'SPOT':
2314                 layout.separator()
2315
2316                 props = layout.operator("wm.context_modal_mouse", text="Spot Size")
2317                 props.data_path_iter = "selected_editable_objects"
2318                 props.data_path_item = "data.spot_size"
2319                 props.input_scale = 0.01
2320                 props.header_text = "Spot Size: %.2f"
2321
2322                 props = layout.operator("wm.context_modal_mouse", text="Spot Blend")
2323                 props.data_path_iter = "selected_editable_objects"
2324                 props.data_path_item = "data.spot_blend"
2325                 props.input_scale = -0.01
2326                 props.header_text = "Spot Blend: %.2f"
2327
2328             layout.separator()
2329
2330         layout.operator("view3d.copybuffer", text="Copy Objects", icon='COPYDOWN')
2331         layout.operator("view3d.pastebuffer", text="Paste Objects", icon='PASTEDOWN')
2332
2333         layout.separator()
2334
2335         layout.operator("object.duplicate_move", icon='DUPLICATE')
2336         layout.operator("object.duplicate_move_linked")
2337
2338         layout.separator()
2339
2340         props = layout.operator("wm.call_panel", text="Rename Active Object...")
2341         props.name = "TOPBAR_PT_name"
2342         props.keep_open = False
2343
2344         layout.separator()
2345
2346         layout.menu("VIEW3D_MT_mirror")
2347         layout.menu("VIEW3D_MT_snap")
2348         layout.menu("VIEW3D_MT_object_parent")
2349         layout.operator_context = 'INVOKE_REGION_WIN'
2350
2351         if view and view.local_view:
2352             layout.operator("view3d.localview_remove_from")
2353         else:
2354             layout.operator("object.move_to_collection")
2355
2356         layout.separator()
2357
2358         layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe...")
2359
2360         layout.separator()
2361
2362         layout.operator_context = 'EXEC_DEFAULT'
2363         layout.operator("object.delete", text="Delete").use_global = False
2364
2365
2366 class VIEW3D_MT_object_shading(Menu):
2367     # XXX, this menu is a place to store shading operator in object mode
2368     bl_label = "Shading"
2369
2370     def draw(self, _context):
2371         layout = self.layout
2372         layout.operator("object.shade_smooth", text="Smooth")
2373         layout.operator("object.shade_flat", text="Flat")
2374
2375
2376 class VIEW3D_MT_object_apply(Menu):
2377     bl_label = "Apply"
2378
2379     def draw(self, _context):
2380         layout = self.layout
2381
2382         props = layout.operator("object.transform_apply", text="Location", text_ctxt=i18n_contexts.default)
2383         props.location, props.rotation, props.scale = True, False, False
2384
2385         props = layout.operator("object.transform_apply", text="Rotation", text_ctxt=i18n_contexts.default)
2386         props.location, props.rotation, props.scale = False, True, False
2387
2388         props = layout.operator("object.transform_apply", text="Scale", text_ctxt=i18n_contexts.default)
2389         props.location, props.rotation, props.scale = False, False, True
2390
2391         props = layout.operator("object.transform_apply", text="All Transforms", text_ctxt=i18n_contexts.default)
2392         props.location, props.rotation, props.scale = True, True, True
2393
2394         props = layout.operator("object.transform_apply", text="Rotation & Scale", text_ctxt=i18n_contexts.default)
2395         props.location, props.rotation, props.scale = False, True, True
2396
2397         layout.separator()
2398
2399         layout.operator(
2400             "object.transforms_to_deltas",
2401             text="Location to Deltas",
2402             text_ctxt=i18n_contexts.default,
2403         ).mode = 'LOC'
2404         layout.operator(
2405             "object.transforms_to_deltas",
2406             text="Rotation to Deltas",
2407             text_ctxt=i18n_contexts.default,
2408         ).mode = 'ROT'
2409         layout.operator(
2410             "object.transforms_to_deltas",
2411             text="Scale to Deltas",
2412             text_ctxt=i18n_contexts.default,
2413         ).mode = 'SCALE'
2414
2415         layout.operator(
2416             "object.transforms_to_deltas",
2417             text="All Transforms to Deltas",
2418             text_ctxt=i18n_contexts.default,
2419         ).mode = 'ALL'
2420         layout.operator("object.anim_transforms_to_deltas")
2421
2422         layout.separator()
2423
2424         layout.operator(
2425             "object.visual_transform_apply",
2426             text="Visual Transform",
2427             text_ctxt=i18n_contexts.default,
2428         )
2429         layout.operator(
2430             "object.convert",
2431             text="Visual Geometry to Mesh",
2432             text_ctxt=i18n_contexts.default,
2433         ).target = 'MESH'
2434         layout.operator("object.duplicates_make_real")
2435
2436
2437 class VIEW3D_MT_object_parent(Menu):
2438     bl_label = "Parent"
2439
2440     def draw(self, _context):
2441         layout = self.layout
2442
2443         layout.operator_enum("object.parent_set", "type")
2444
2445         layout.separator()
2446
2447         layout.operator_enum("object.parent_clear", "type")
2448
2449
2450 class VIEW3D_MT_object_track(Menu):
2451     bl_label = "Track"
2452
2453     def draw(self, _context):
2454         layout = self.layout
2455
2456         layout.operator_enum("object.track_set", "type")
2457
2458         layout.separator()
2459
2460         layout.operator_enum("object.track_clear", "type")
2461
2462
2463 class VIEW3D_MT_object_collection(Menu):
2464     bl_label = "Collection"
2465
2466     def draw(self, _context):
2467         layout = self.layout
2468
2469         layout.operator("object.move_to_collection")
2470         layout.operator("object.link_to_collection")
2471
2472         layout.separator()
2473
2474         layout.operator("collection.create")
2475         # layout.operator_menu_enum("collection.objects_remove", "collection")  # BUGGY
2476         layout.operator("collection.objects_remove")
2477         layout.operator("collection.objects_remove_all")
2478
2479         layout.separator()
2480
2481         layout.operator("collection.objects_add_active")
2482         layout.operator("collection.objects_remove_active")
2483
2484
2485 class VIEW3D_MT_object_constraints(Menu):
2486     bl_label = "Constraints"
2487
2488     def draw(self, _context):
2489         layout = self.layout
2490
2491         layout.operator("object.constraint_add_with_targets")
2492         layout.operator("object.constraints_copy")
2493
2494         layout.separator()
2495
2496         layout.operator("object.constraints_clear")
2497
2498
2499 class VIEW3D_MT_object_quick_effects(Menu):
2500     bl_label = "Quick Effects"
2501
2502     def draw(self, _context):
2503         layout = self.layout
2504
2505         layout.operator("object.quick_fur")
2506         layout.operator("object.quick_explode")
2507         layout.operator("object.quick_smoke")
2508         layout.operator("object.quick_fluid")
2509
2510
2511 class VIEW3D_MT_object_showhide(Menu):
2512     bl_label = "Show/Hide"
2513
2514     def draw(self, _context):
2515         layout = self.layout
2516
2517         layout.operator("object.hide_view_clear")
2518
2519         layout.separator()
2520
2521         layout.operator("object.hide_view_set", text="Hide Selected").unselected = False
2522         layout.operator("object.hide_view_set", text="Hide Unselected").unselected = True
2523
2524
2525 class VIEW3D_MT_make_single_user(Menu):
2526     bl_label = "Make Single User"
2527
2528     def draw(self, _context):
2529         layout = self.layout
2530
2531         props = layout.operator("object.make_single_user", text="Object")
2532         props.object = True
2533         props.obdata = props.material = props.animation = False
2534
2535         props = layout.operator("object.make_single_user", text="Object & Data")
2536         props.object = props.obdata = True
2537         props.material = props.animation = False
2538
2539         props = layout.operator("object.make_single_user", text="Object & Data & Materials")
2540         props.object = props.obdata = props.material = True
2541         props.animation = False
2542
2543         props = layout.operator("object.make_single_user", text="Materials")
2544         props.material = True
2545         props.object = props.obdata = props.animation = False
2546
2547         props = layout.operator("object.make_single_user", text="Object Animation")
2548         props.animation = True
2549         props.object = props.obdata = props.material = False
2550
2551
2552 class VIEW3D_MT_make_links(Menu):
2553     bl_label = "Make Links"
2554
2555     def draw(self, _context):
2556         layout = self.layout
2557         operator_context_default = layout.operator_context
2558
2559         if len(bpy.data.scenes) > 10:
2560             layout.operator_context = 'INVOKE_REGION_WIN'
2561             layout.operator("object.make_links_scene", text="Objects to Scene...", icon='OUTLINER_OB_EMPTY')
2562         else:
2563             layout.operator_context = 'EXEC_REGION_WIN'
2564             layout.operator_menu_enum("object.make_links_scene", "scene", text="Objects to Scene")
2565
2566         layout.separator()
2567
2568         layout.operator_context = operator_context_default
2569
2570         layout.operator_enum("object.make_links_data", "type")  # inline
2571
2572         layout.operator("object.join_uvs")  # stupid place to add this!
2573
2574
2575 class VIEW3D_MT_brush(Menu):
2576     bl_label = "Brush"
2577
2578     def draw(self, context):
2579         layout = self.layout
2580
2581         tool_settings = context.tool_settings
2582         settings = UnifiedPaintPanel.paint_settings(context)
2583         brush = getattr(settings, "brush", None)
2584
2585         ups = tool_settings.unified_paint_settings
2586         layout.prop(ups, "use_unified_size", text="Unified Size")
2587         layout.prop(ups, "use_unified_strength", text="Unified Strength")
2588         if context.image_paint_object or context.vertex_paint_object:
2589             layout.prop(ups, "use_unified_color", text="Unified Color")
2590         layout.separator()
2591
2592         # skip if no active brush
2593         if not brush:
2594             layout.label(text="No Brushes currently available", icon='INFO')
2595             return
2596
2597         # brush paint modes
2598         layout.menu("VIEW3D_MT_brush_paint_modes")
2599
2600         # brush tool
2601         if context.sculpt_object:
2602             layout.operator("brush.reset")
2603             layout.prop_menu_enum(brush, "sculpt_tool")
2604         elif context.image_paint_object:
2605             layout.prop_menu_enum(brush, "image_tool")
2606         elif context.vertex_paint_object:
2607             layout.prop_menu_enum(brush, "vertex_tool")
2608         elif context.weight_paint_object:
2609             layout.prop_menu_enum(brush, "weight_tool")
2610
2611         # TODO: still missing a lot of brush options here
2612
2613         # sculpt options
2614         if context.sculpt_object:
2615
2616             sculpt_tool = brush.sculpt_tool
2617
2618             layout.separator()
2619             layout.operator_menu_enum("brush.curve_preset", "shape", text="Curve Preset")
2620             layout.separator()
2621
2622             if sculpt_tool != 'GRAB':
2623                 layout.prop_menu_enum(brush, "stroke_method")
2624
2625                 if sculpt_tool in {'DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'}:
2626                     layout.prop_menu_enum(brush, "direction")
2627
2628                 if sculpt_tool == 'LAYER':
2629                     layout.prop(brush, "use_persistent")
2630                     layout.operator("sculpt.set_persistent_base")
2631
2632
2633 class VIEW3D_MT_brush_paint_modes(Menu):
2634     bl_label = "Enabled Modes"
2635
2636     def draw(self, context):
2637         layout = self.layout
2638
2639         settings = UnifiedPaintPanel.paint_settings(context)
2640         brush = settings.brush
2641
2642         layout.prop(brush, "use_paint_sculpt", text="Sculpt")
2643         layout.prop(brush, "use_paint_vertex", text="Vertex Paint")
2644         layout.prop(brush, "use_paint_weight", text="Weight Paint")
2645         layout.prop(brush, "use_paint_image", text="Texture Paint")
2646
2647
2648 class VIEW3D_MT_paint_vertex(Menu):
2649     bl_label = "Paint"
2650
2651     def draw(self, _context):
2652         layout = self.layout
2653
2654         layout.operator("paint.vertex_color_set")
2655         layout.operator("paint.vertex_color_smooth")
2656         layout.operator("paint.vertex_color_dirt")
2657         layout.operator("paint.vertex_color_from_weight")
2658
2659         layout.separator()
2660
2661         layout.operator("paint.vertex_color_invert", text="Invert")
2662         layout.operator("paint.vertex_color_levels", text="Levels")
2663         layout.operator("paint.vertex_color_hsv", text="Hue Saturation Value")
2664         layout.operator("paint.vertex_color_brightness_contrast", text="Bright/Contrast")
2665
2666
2667 class VIEW3D_MT_hook(Menu):
2668     bl_label = "Hooks"
2669
2670     def draw(self, context):
2671         layout = self.layout
2672         layout.operator_context = 'EXEC_AREA'
2673         layout.operator("object.hook_add_newob")
2674         layout.operator("object.hook_add_selob").use_bone = False
2675         layout.operator("object.hook_add_selob", text="Hook to Selected Object Bone").use_bone = True
2676
2677         if [mod.type == 'HOOK' for mod in context.active_object.modifiers]:
2678             layout.separator()
2679
2680             layout.operator_menu_enum("object.hook_assign", "modifier")
2681             layout.operator_menu_enum("object.hook_remove", "modifier")
2682
2683             layout.separator()
2684
2685             layout.operator_menu_enum("object.hook_select", "modifier")
2686             layout.operator_menu_enum("object.hook_reset", "modifier")
2687             layout.operator_menu_enum("object.hook_recenter", "modifier")
2688
2689
2690 class VIEW3D_MT_vertex_group(Menu):
2691     bl_label = "Vertex Groups"
2692
2693     def draw(self, context):
2694         layout = self.layout
2695
2696         layout.operator_context = 'EXEC_AREA'
2697         layout.operator("object.vertex_group_assign_new")
2698
2699         ob = context.active_object
2700         if ob.mode == 'EDIT' or (ob.mode == 'WEIGHT_PAINT' and ob.type == 'MESH' and ob.data.use_paint_mask_vertex):
2701             if ob.vertex_groups.active:
2702                 layout.separator()
2703
2704                 layout.operator("object.vertex_group_assign", text="Assign to Active Group")
2705                 layout.operator(
2706                     "object.vertex_group_remove_from",
2707                     text="Remove from Active Group",
2708                 ).use_all_groups = False
2709                 layout.operator("object.vertex_group_remove_from", text="Remove from All").use_all_groups = True
2710
2711         if ob.vertex_groups.active:
2712             layout.separator()
2713
2714             layout.operator_menu_enum("object.vertex_group_set_active", "group", text="Set Active Group")
2715             layout.operator("object.vertex_group_remove", text="Remove Active Group").all = False
2716             layout.operator("object.vertex_group_remove", text="Remove All Groups").all = True
2717
2718
2719 class VIEW3D_MT_paint_weight(Menu):
2720     bl_label = "Weights"
2721
2722     @staticmethod
2723     def draw_generic(layout, is_editmode=False):
2724
2725         if not is_editmode:
2726
2727             layout.operator("paint.weight_from_bones", text="Assign Automatic From Bones").type = 'AUTOMATIC'
2728             layout.operator("paint.weight_from_bones", text="Assign From Bone Envelopes").type = 'ENVELOPES'
2729
2730             layout.separator()
2731
2732         layout.operator("object.vertex_group_normalize_all", text="Normalize All")
2733         layout.operator("object.vertex_group_normalize", text="Normalize")
2734
2735         layout.separator()
2736
2737         layout.operator("object.vertex_group_mirror", text="Mirror")
2738         layout.operator("object.vertex_group_invert", text="Invert")
2739         layout.operator("object.vertex_group_clean", text="Clean")
2740
2741         layout.separator()
2742
2743         layout.operator("object.vertex_group_quantize", text="Quantize")
2744         layout.operator("object.vertex_group_levels", text="Levels")
2745         layout.operator("object.vertex_group_smooth", text="Smooth")
2746
2747         if not is_editmode:
2748             props = layout.operator("object.data_transfer", text="Transfer Weights")
2749             props.use_reverse_transfer = True
2750             props.data_type = 'VGROUP_WEIGHTS'
2751
2752         layout.operator("object.vertex_group_limit_total", text="Limit Total")
2753         layout.operator("object.vertex_group_fix", text="Fix Deforms")
2754
2755         if not is_editmode:
2756             layout.separator()
2757
2758             layout.operator("paint.weight_set")
2759
2760     def draw(self, _context):
2761         self.draw_generic(self.layout, is_editmode=False)
2762
2763
2764 class VIEW3D_MT_sculpt(Menu):
2765     bl_label = "Sculpt"
2766
2767     def draw(self, context):
2768         layout = self.layout
2769
2770         tool_settings = context.tool_settings
2771         sculpt = tool_settings.sculpt
2772
2773         layout.operator("sculpt.dynamic_topology_toggle", text="Toggle Dynamic Topology")
2774
2775         layout.separator()
2776
2777         layout.prop(sculpt, "use_symmetry_x")
2778         layout.prop(sculpt, "use_symmetry_y")
2779         layout.prop(sculpt, "use_symmetry_z")
2780
2781         layout.separator()
2782
2783         layout.prop(sculpt, "lock_x")
2784         layout.prop(sculpt, "lock_y")
2785         layout.prop(sculpt, "lock_z")
2786
2787         layout.separator()
2788
2789         layout.prop(sculpt, "use_threaded", text="Threaded Sculpt")
2790         layout.prop(sculpt, "show_low_resolution")
2791         layout.prop(sculpt, "show_brush")
2792         layout.prop(sculpt, "use_deform_only")
2793         layout.prop(sculpt, "show_diffuse_color")
2794         layout.prop(sculpt, "show_mask")
2795
2796
2797 class VIEW3D_MT_hide_mask(Menu):
2798     bl_label = "Hide/Mask"
2799
2800     def draw(self, _context):
2801         layout = self.layout
2802
2803         props = layout.operator("paint.hide_show", text="Show All")
2804         props.action = 'SHOW'
2805         props.area = 'ALL'
2806
2807         props = layout.operator("paint.hide_show", text="Hide Bounding Box")
2808         props.action = 'HIDE'
2809         props.area = 'INSIDE'
2810
2811         props = layout.operator("paint.hide_show", text="Show Bounding Box")
2812         props.action = 'SHOW'
2813         props.area = 'INSIDE'
2814
2815         props = layout.operator("paint.hide_show", text="Hide Masked")
2816         props.area = 'MASKED'
2817         props.action = 'HIDE'
2818
2819         layout.separator()
2820
2821         props = layout.operator("paint.mask_flood_fill", text="Invert Mask")
2822         props.mode = 'INVERT'
2823
2824         props = layout.operator("paint.mask_flood_fill", text="Fill Mask")
2825         props.mode = 'VALUE'
2826         props.value = 1
2827
2828         props = layout.operator("paint.mask_flood_fill", text="Clear Mask")
2829         props.mode = 'VALUE'
2830         props.value = 0
2831
2832         props = layout.operator("view3d.select_box", text="Box Mask")
2833         props = layout.operator("paint.mask_lasso_gesture", text="Lasso Mask")
2834
2835
2836 class VIEW3D_MT_particle(Menu):
2837     bl_label = "Particle"
2838
2839     def draw(self, context):
2840         layout = self.layout
2841         tool_settings = context.tool_settings
2842
2843         particle_edit = tool_settings.particle_edit
2844
2845         layout.operator("particle.mirror")
2846
2847         layout.operator("particle.remove_doubles")
2848
2849         layout.separator()
2850
2851         if particle_edit.select_mode == 'POINT':
2852             layout.operator("particle.subdivide")
2853
2854         layout.operator("particle.unify_length")
2855         layout.operator("particle.rekey")
2856         layout.operator("particle.weight_set")
2857
2858         layout.separator()
2859
2860         layout.menu("VIEW3D_MT_particle_showhide")
2861
2862         layout.separator()
2863
2864         layout.operator("particle.delete")
2865
2866
2867 class VIEW3D_MT_particle_context_menu(Menu):
2868     bl_label = "Particle Context Menu"
2869
2870     def draw(self, context):
2871         layout = self.layout
2872         tool_settings = context.tool_settings
2873
2874         particle_edit = tool_settings.particle_edit
2875
2876         layout.operator("particle.rekey")
2877
2878         layout.separator()
2879
2880         layout.operator("particle.delete")
2881
2882         layout.separator()
2883
2884         layout.operator("particle.remove_doubles")
2885         layout.operator("particle.unify_length")
2886
2887         if particle_edit.select_mode == 'POINT':
2888             layout.operator("particle.subdivide")
2889
2890         layout.operator("particle.weight_set")
2891
2892         layout.separator()
2893
2894         layout.operator("particle.mirror")
2895
2896         if particle_edit.select_mode == 'POINT':
2897             layout.separator()
2898
2899             layout.operator("particle.select_all", text="All").action = 'SELECT'
2900             layout.operator("particle.select_all", text="None").action = 'DESELECT'
2901             layout.operator("particle.select_all", text="Invert").action = 'INVERT'
2902
2903             layout.separator()
2904
2905             layout.operator("particle.select_roots")
2906             layout.operator("particle.select_tips")
2907
2908             layout.separator()
2909
2910             layout.operator("particle.select_random")
2911
2912             layout.separator()
2913
2914             layout.operator("particle.select_more")
2915             layout.operator("particle.select_less")
2916
2917             layout.separator()
2918
2919             layout.operator("particle.select_linked")
2920
2921
2922 class VIEW3D_MT_particle_showhide(ShowHideMenu, Menu):
2923     _operator_name = "particle"
2924
2925
2926 class VIEW3D_MT_pose(Menu):
2927     bl_label = "Pose"
2928
2929     def draw(self, _context):
2930         layout = self.layout
2931
2932         layout.menu("VIEW3D_MT_transform_armature")
2933
2934         layout.menu("VIEW3D_MT_pose_transform")
2935         layout.menu("VIEW3D_MT_pose_apply")
2936
2937         layout.menu("VIEW3D_MT_snap")
2938
2939         layout.separator()
2940
2941         layout.menu("VIEW3D_MT_object_animation")
2942
2943         layout.separator()
2944
2945         layout.menu("VIEW3D_MT_pose_slide")
2946         layout.menu("VIEW3D_MT_pose_propagate")
2947
2948         layout.separator()
2949
2950         layout.operator("pose.copy", icon='COPYDOWN')
2951         layout.operator("pose.paste", icon='PASTEDOWN').flipped = False
2952         layout.operator("pose.paste", icon='PASTEFLIPDOWN', text="Paste Pose Flipped").flipped = True
2953
2954         layout.separator()
2955
2956         layout.menu("VIEW3D_MT_pose_library")
2957         layout.menu("VIEW3D_MT_pose_motion")
2958         layout.menu("VIEW3D_MT_pose_group")
2959
2960         layout.separator()
2961
2962         layout.menu("VIEW3D_MT_object_parent")
2963         layout.menu("VIEW3D_MT_pose_ik")
2964         layout.menu("VIEW3D_MT_pose_constraints")
2965
2966         layout.separator()
2967
2968         layout.operator_context = 'EXEC_AREA'
2969         layout.operator("pose.autoside_names", text="AutoName Left/Right").axis = 'XAXIS'
2970         layout.operator("pose.autoside_names", text="AutoName Front/Back").axis = 'YAXIS'
2971         layout.operator("pose.autoside_names", text="AutoName Top/Bottom").axis = 'ZAXIS'
2972
2973         layout.operator("pose.flip_names")
2974
2975         layout.operator("pose.quaternions_flip")
2976
2977         layout.separator()
2978
2979         layout.operator_context = 'INVOKE_AREA'
2980         layout.operator("armature.armature_layers", text="Change Armature Layers...")
2981         layout.operator("pose.bone_layers", text="Change Bone Layers...")
2982
2983         layout.separator()
2984
2985         layout.menu("VIEW3D_MT_pose_showhide")
2986         layout.menu("VIEW3D_MT_bone_options_toggle", text="Bone Settings")
2987
2988
2989 class VIEW3D_MT_pose_transform(Menu):
2990     bl_label = "Clear Transform"
2991
2992     def draw(self, _context):
2993         layout = self.layout
2994
2995         layout.operator("pose.transforms_clear", text="All")
2996
2997         layout.separator()
2998
2999         layout.operator("pose.loc_clear", text="Location")
3000         layout.operator("pose.rot_clear", text="Rotation")
3001         layout.operator("pose.scale_clear", text="Scale")
3002
3003         layout.separator()
3004
3005         layout.operator("pose.user_transforms_clear", text="Reset Unkeyed")
3006
3007
3008 class VIEW3D_MT_pose_slide(Menu):
3009     bl_label = "In-Betweens"
3010
3011     def draw(self, _context):
3012         layout = self.layout
3013
3014         layout.operator("pose.push")
3015         layout.operator("pose.relax")
3016         layout.operator("pose.breakdown")
3017
3018
3019 class VIEW3D_MT_pose_propagate(Menu):
3020     bl_label = "Propagate"
3021
3022     def draw(self, _context):
3023         layout = self.layout
3024
3025         layout.operator("pose.propagate").mode = 'WHILE_HELD'
3026
3027         layout.separator()
3028
3029         layout.operator("pose.propagate", text="To Next Keyframe").mode = 'NEXT_KEY'
3030         layout.operator("pose.propagate", text="To Last Keyframe (Make Cyclic)").mode = 'LAST_KEY'
3031
3032         layout.separator()
3033
3034         layout.operator("pose.propagate", text="On Selected Keyframes").mode = 'SELECTED_KEYS'
3035
3036         layout.separator()
3037
3038         layout.operator("pose.propagate", text="On Selected Markers").mode = 'SELECTED_MARKERS'
3039
3040
3041 class VIEW3D_MT_pose_library(Menu):
3042     bl_label = "Pose Library"
3043
3044     def draw(self, _context):
3045         layout = self.layout
3046
3047         layout.operator("poselib.browse_interactive", text="Browse Poses...")
3048
3049         layout.separator()
3050
3051         layout.operator("poselib.pose_add", text="Add Pose...")
3052         layout.operator("poselib.pose_rename", text="Rename Pose...")
3053         layout.operator("poselib.pose_remove", text="Remove Pose...")
3054
3055
3056 class VIEW3D_MT_pose_motion(Menu):
3057     bl_label = "Motion Paths"
3058
3059     def draw(self, _context):
3060         layout = self.layout
3061
3062         layout.operator("pose.paths_calculate", text="Calculate")
3063         layout.operator("pose.paths_clear", text="Clear")
3064
3065
3066 class VIEW3D_MT_pose_group(Menu):
3067     bl_label = "Bone Groups"
3068
3069     def draw(self, context):
3070         layout = self.layout
3071
3072         pose = context.active_object.pose
3073
3074         layout.operator_context = 'EXEC_AREA'
3075         layout.operator("pose.group_assign", text="Assign to New Group").type = 0
3076
3077         if pose.bone_groups:
3078             active_group = pose.bone_groups.active_index + 1
3079             layout.operator("pose.group_assign", text="Assign to Group").type = active_group
3080
3081             layout.separator()
3082
3083             # layout.operator_context = 'INVOKE_AREA'
3084             layout.operator("pose.group_unassign")
3085             layout.operator("pose.group_remove")
3086
3087
3088 class VIEW3D_MT_pose_ik(Menu):
3089     bl_label = "Inverse Kinematics"
3090
3091     def draw(self, _context):
3092         layout = self.layout
3093
3094         layout.operator("pose.ik_add")
3095         layout.operator("pose.ik_clear")
3096
3097
3098 class VIEW3D_MT_pose_constraints(Menu):
3099     bl_label = "Constraints"
3100
3101     def draw(self, _context):
3102         layout = self.layout
3103
3104         layout.operator("pose.constraint_add_with_targets", text="Add (With Targets)...")
3105         layout.operator("pose.constraints_copy")
3106         layout.operator("pose.constraints_clear")
3107
3108
3109 class VIEW3D_MT_pose_showhide(ShowHideMenu, Menu):
3110     _operator_name = "pose"
3111
3112
3113 class VIEW3D_MT_pose_apply(Menu):
3114     bl_label = "Apply"
3115
3116     def draw(self, _context):
3117         layout = self.layout
3118
3119         layout.operator("pose.armature_apply")
3120         layout.operator("pose.visual_transform_apply")
3121
3122         layout.separator()
3123
3124         props = layout.operator("object.assign_property_defaults")
3125         props.process_bones = True
3126
3127
3128 class VIEW3D_MT_pose_context_menu(Menu):
3129     bl_label = "Pose Context Menu"
3130
3131     def draw(self, _context):
3132         layout = self.layout
3133
3134         layout.operator_context = 'INVOKE_REGION_WIN'
3135
3136         layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe...")
3137
3138         layout.separator()
3139
3140         layout.operator("pose.copy", icon='COPYDOWN')
3141         layout.operator("pose.paste", icon='PASTEDOWN').flipped = False
3142         layout.operator("pose.paste", icon='PASTEFLIPDOWN', text="Paste X-Flipped Pose").flipped = True
3143
3144         layout.separator()
3145
3146         props = layout.operator("wm.call_panel", text="Rename Active Bone...")
3147         props.name = "TOPBAR_PT_name"
3148         props.keep_open = False
3149
3150         layout.separator()
3151
3152         layout.operator("pose.paths_calculate", text="Calculate")
3153         layout.operator("pose.paths_clear", text="Clear")
3154
3155         layout.separator()
3156
3157         layout.operator("pose.push")
3158         layout.operator("pose.relax")
3159         layout.operator("pose.breakdown")
3160
3161         layout.separator()
3162
3163         layout.operator("pose.paths_calculate", text="Calculate Motion Paths")
3164         layout.operator("pose.paths_clear", text="Clear Motion Paths")
3165
3166         layout.separator()
3167
3168         layout.operator("pose.hide").unselected = False
3169         layout.operator("pose.reveal")
3170
3171         layout.separator()
3172
3173         layout.operator("pose.user_transforms_clear")
3174
3175
3176 class BoneOptions:
3177     def draw(self, context):
3178         layout = self.layout
3179
3180         options = [
3181             "show_wire",
3182             "use_deform",
3183             "use_envelope_multiply",
3184             "use_inherit_rotation",
3185             "use_inherit_scale",
3186         ]
3187
3188         if context.mode == 'EDIT_ARMATURE':
3189             bone_props = bpy.types.EditBone.bl_rna.properties
3190             data_path_iter = "selected_bones"
3191             opt_suffix = ""
3192             options.append("lock")
3193         else:  # pose-mode
3194             bone_props = bpy.types.Bone.bl_rna.properties
3195             data_path_iter = "selected_pose_bones"
3196             opt_suffix = "bone."
3197
3198         for opt in options:
3199             props = layout.operator("wm.context_collection_boolean_set", text=bone_props[opt].name,
3200                                     text_ctxt=i18n_contexts.default)
3201             props.data_path_iter = data_path_iter
3202             props.data_path_item = opt_suffix + opt
3203             props.type = self.type
3204
3205
3206 class VIEW3D_MT_bone_options_toggle(Menu, BoneOptions):
3207     bl_label = "Toggle Bone Options"
3208     type = 'TOGGLE'
3209
3210
3211 class VIEW3D_MT_bone_options_enable(Menu, BoneOptions):
3212     bl_label = "Enable Bone Options"
3213     type = 'ENABLE'
3214
3215
3216 class VIEW3D_MT_bone_options_disable(Menu, BoneOptions):
3217     bl_label = "Disable Bone Options"
3218     type = 'DISABLE'
3219
3220
3221 # ********** Edit Menus, suffix from ob.type **********
3222
3223
3224 class VIEW3D_MT_edit_mesh(Menu):
3225     bl_label = "Mesh"
3226
3227     def draw(self, _context):
3228         layout = self.layout
3229
3230         with_bullet = bpy.app.build_options.bullet
3231
3232         layout.menu("VIEW3D_MT_transform")
3233         layout.menu("VIEW3D_MT_mirror")
3234         layout.menu("VIEW3D_MT_snap")
3235
3236         layout.separator()
3237
3238         layout.operator("mesh.duplicate_move", text="Duplicate")
3239         layout.menu("VIEW3D_MT_edit_mesh_extrude")
3240         layout.operator("mesh.split")
3241         layout.operator("mesh.bisect")
3242         layout.operator("mesh.knife_project")
3243
3244         if with_bullet:
3245             layout.operator("mesh.convex_hull")
3246
3247         layout.separator()
3248
3249         layout.operator("mesh.symmetrize")
3250         layout.operator("mesh.symmetry_snap")
3251
3252         layout.separator()
3253
3254         layout.menu("VIEW3D_MT_edit_mesh_normals")
3255         layout.menu("VIEW3D_MT_edit_mesh_shading")
3256         layout.menu("VIEW3D_MT_edit_mesh_weights")
3257         layout.operator_menu_enum("mesh.sort_elements", "type", text="Sort Elements...")
3258
3259         layout.separator()
3260
3261         layout.menu("VIEW3D_MT_edit_mesh_showhide")
3262         layout.operator_menu_enum("mesh.separate", "type")
3263         layout.menu("VIEW3D_MT_edit_mesh_clean")
3264
3265         layout.separator()
3266
3267         layout.menu("VIEW3D_MT_edit_mesh_delete")
3268
3269
3270 class VIEW3D_MT_edit_mesh_context_menu(Menu):
3271     bl_label = ""
3272
3273     def draw(self, context):
3274
3275         def count_selected_items_for_objects_in_mode():
3276             selected_verts_len = 0
3277             selected_edges_len = 0
3278             selected_faces_len = 0
3279             for ob in context.objects_in_mode_unique_data:
3280                 v, e, f = ob.data.count_selected_items()
3281                 selected_verts_len += v
3282                 selected_edges_len += e
3283                 selected_faces_len += f
3284             return (selected_verts_len, selected_edges_len, selected_faces_len)
3285
3286         is_vert_mode, is_edge_mode, is_face_mode = context.tool_settings.mesh_select_mode
3287         selected_verts_len, selected_edges_len, selected_faces_len = count_selected_items_for_objects_in_mode()
3288
3289         del count_selected_items_for_objects_in_mode
3290
3291         layout = self.layout
3292
3293         layout.operator_context = 'INVOKE_REGION_WIN'
3294
3295         # If nothing is selected
3296         # (disabled for now until it can be made more useful).
3297         '''
3298         # If nothing is selected
3299         if not (selected_verts_len or selected_edges_len or selected_faces_len):
3300             layout.menu("VIEW3D_MT_mesh_add", text="Add")
3301
3302             return
3303         '''
3304
3305         # Else something is selected
3306
3307         row = layout.row()
3308
3309         if is_vert_mode:
3310             col = row.column()
3311
3312             col.label(text="Vertex Context Menu", icon='VERTEXSEL')
3313             col.separator()
3314
3315             # Additive Operators
3316             col.operator("mesh.subdivide", text="Subdivide")
3317
3318             col.separator()
3319
3320             col.operator("mesh.extrude_vertices_move", text="Extrude Vertices")
3321             col.operator("mesh.bevel", text="Bevel Vertices").vertex_only = True
3322
3323             if selected_verts_len > 1:
3324                 col.separator()
3325                 col.operator("mesh.edge_face_add", text="New Edge/Face from Vertices")
3326                 col.operator("mesh.vert_connect_path", text="Connect Vertex Path")
3327                 col.operator("mesh.vert_connect", text="Connect Vertex Pairs")
3328
3329             col.separator()
3330
3331             # Deform Operators
3332             col.operator("transform.push_pull", text="Push/Pull")
3333             col.operator("transform.shrink_fatten", text="Shrink/Fatten")
3334             col.operator("transform.shear", text="Shear")
3335             col.operator("transform.vert_slide", text="Slide Vertices")
3336             col.operator("transform.vertex_random", text="Randomize Vertices")
3337             col.operator("mesh.vertices_smooth", text="Smooth Vertices")
3338             col.operator("mesh.vertices_smooth_laplacian", text="Smooth Laplacian")
3339
3340             col.separator()
3341
3342             col.menu("VIEW3D_MT_mirror", text="Mirror Vertices")
3343             col.menu("VIEW3D_MT_snap", text="Snap Vertices")
3344
3345             col.separator()
3346
3347             # Removal Operators
3348             if selected_verts_len > 1:
3349                 col.operator("mesh.merge", text="Merge Vertices...")
3350                 col.operator("mesh.remove_doubles", text="Remove Double Vertices")
3351             col.operator("mesh.dissolve_verts")
3352             col.operator("mesh.delete", text="Delete Vertices").type = 'VERT'
3353
3354         if is_edge_mode:
3355             render = context.scene.render
3356
3357             col = row.column()
3358             col.label(text="Edge Context Menu", icon='EDGESEL')
3359             col.separator()
3360
3361             # Additive Operators
3362             col.operator("mesh.subdivide", text="Subdivide")
3363
3364             col.separator()
3365
3366             col.operator("mesh.extrude_edges_move", text="Extrude Edges")
3367             col.operator("mesh.bevel", text="Bevel Edges").vertex_only = False
3368             if selected_edges_len >= 2:
3369                 col.operator("mesh.bridge_edge_loops")
3370             if selected_edges_len >= 1:
3371                 col.operator("mesh.edge_face_add", text="New Face from Edges")
3372             if selected_edges_len >= 2:
3373                 col.operator("mesh.fill")
3374
3375             col.separator()
3376
3377             col.operator("mesh.loopcut_slide")
3378             col.operator("mesh.offset_edge_loops_slide")
3379             col.operator("mesh.knife_tool")
3380             col.operator("mesh.bisect")
3381             col.operator("mesh.bridge_edge_loops", text="Bridge Edge Loops")
3382
3383             col.separator()
3384
3385             # Deform Operators
3386             col.operator("mesh.edge_rotate", text="Rotate Edge CW").use_ccw = False
3387             col.operator("transform.edge_slide")
3388             col.operator("mesh.edge_split")
3389
3390             col.separator()
3391
3392             # Edge Flags
3393             col.operator("transform.edge_crease")
3394             col.operator("transform.edge_bevelweight")
3395
3396             col.separator()
3397
3398             col.operator("mesh.mark_seam").clear = False
3399             col.operator("mesh.mark_seam", text="Clear Seam").clear = True
3400
3401             col.separator()
3402
3403             col.operator("mesh.mark_sharp")
3404             col.operator("mesh.mark_sharp", text="Clear Sharp").clear = True
3405
3406             if render.use_freestyle:
3407                 col.separator()
3408
3409                 col.operator("mesh.mark_freestyle_edge").clear = False
3410                 col.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
3411
3412             col.separator()
3413
3414             # Removal Operators
3415             col.operator("mesh.unsubdivide")
3416             col.operator("mesh.dissolve_edges")
3417             col.operator("mesh.delete", text="Delete Edges").type = 'EDGE'
3418
3419         if is_face_mode:
3420             col = row.column()
3421
3422             col.label(text="Face Context Menu", icon='FACESEL')
3423             col.separator()
3424
3425             # Additive Operators
3426             col.operator("mesh.subdivide", text="Subdivide")
3427
3428             col.separator()
3429
3430             col.operator("view3d.edit_mesh_extrude_move_normal", text="Extrude Faces")
3431             col.operator("view3d.edit_mesh_extrude_move_shrink_fatten", text="Extrude Faces Along Normals")
3432             col.operator("mesh.extrude_faces_move", text="Extrude Individual Faces")
3433
3434             col.operator("mesh.inset")
3435             col.operator("mesh.poke")
3436
3437             if selected_faces_len >= 2:
3438                 col.operator("mesh.bridge_edge_loops", text="Bridge Faces")
3439
3440             col.separator()
3441
3442             # Modify Operators
3443             col.menu("VIEW3D_MT_uv_map", text="UV Unwrap Faces")
3444
3445             col.separator()
3446
3447             props = col.operator("mesh.quads_convert_to_tris")
3448             props.quad_method = props.ngon_method = 'BEAUTY'
3449             col.operator("mesh.tris_convert_to_quads")
3450
3451             col.separator()
3452
3453             col.operator("mesh.faces_shade_smooth")
3454             col.operator("mesh.faces_shade_flat")
3455
3456             col.separator()
3457
3458             # Removal Operators
3459             col.operator("mesh.unsubdivide")
3460             col.operator("mesh.dissolve_faces")
3461             col.operator("mesh.delete", text="Delete Faces").type = 'FACE'
3462
3463
3464 class VIEW3D_MT_edit_mesh_select_mode(Menu):
3465     bl_label = "Mesh Select Mode"
3466
3467     def draw(self, _context):
3468         layout = self.layout
3469
3470         layout.operator_context = 'INVOKE_REGION_WIN'
3471         layout.operator("mesh.select_mode", text="Vertex", icon='VERTEXSEL').type = 'VERT'
3472         layout.operator("mesh.select_mode", text="Edge", icon='EDGESEL').type = 'EDGE'
3473         layout.operator("mesh.select_mode", text="Face", icon='FACESEL').type = 'FACE'
3474
3475
3476 class VIEW3D_MT_edit_mesh_extrude(Menu):
3477     bl_label = "Extrude"
3478
3479     _extrude_funcs = {
3480         'VERT': lambda layout:
3481         layout.operator("mesh.extrude_vertices_move", text="Extrude Vertices"),
3482         'EDGE': lambda layout:
3483         layout.operator("mesh.extrude_edges_move", text="Extrude Edges"),
3484         'REGION': lambda layout:
3485         layout.operator("view3d.edit_mesh_extrude_move_normal", text="Extrude Faces"),
3486         'REGION_VERT_NORMAL': lambda layout:
3487         layout.operator("view3d.edit_mesh_extrude_move_shrink_fatten", text="Extrude Faces Along Normals"),
3488         'FACE': lambda layout:
3489         layout.operator("mesh.extrude_faces_move", text="Extrude Individual Faces"),
3490     }
3491
3492     @staticmethod
3493     def extrude_options(context):
3494         tool_settings = context.tool_settings
3495         select_mode = tool_settings.mesh_select_mode
3496         mesh = context.object.data
3497
3498         menu = []
3499         if mesh.total_face_sel:
3500             menu += ['REGION', 'REGION_VERT_NORMAL', 'FACE']
3501         if mesh.total_edge_sel and (select_mode[0] or select_mode[1]):
3502             menu += ['EDGE']
3503         if mesh.total_vert_sel and select_mode[0]:
3504             menu += ['VERT']
3505
3506         # should never get here
3507         return menu
3508
3509     def draw(self, context):
3510         layout = self.layout
3511         layout.operator_context = 'INVOKE_REGION_WIN'
3512
3513         for menu_id in self.extrude_options(context):
3514             self._extrude_funcs[menu_id](layout)
3515
3516