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