48922911507877f9b830206cd6a048397d721324
[blender.git] / release / scripts / startup / bl_ui / space_topbar.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 Header, Menu, Panel
22 from .properties_grease_pencil_common import (
23     GPENCIL_UL_layer,
24 )
25
26
27 class TOPBAR_HT_upper_bar(Header):
28     bl_space_type = 'TOPBAR'
29
30     def draw(self, context):
31         region = context.region
32
33         if region.alignment == 'RIGHT':
34             self.draw_right(context)
35         else:
36             self.draw_left(context)
37
38     def draw_left(self, context):
39         layout = self.layout
40
41         window = context.window
42         screen = context.screen
43
44         layout.operator("wm.splash", text="", icon='BLENDER', emboss=False)
45
46         TOPBAR_MT_editor_menus.draw_collapsible(context, layout)
47
48         layout.separator()
49
50         if not screen.show_fullscreen:
51             layout.template_ID_tabs(
52                 window, "workspace",
53                 new="workspace.add",
54                 menu="TOPBAR_MT_workspace_menu",
55             )
56         else:
57             layout.operator(
58                 "screen.back_to_previous",
59                 icon='SCREEN_BACK',
60                 text="Back to Previous",
61             )
62
63     def draw_right(self, context):
64         layout = self.layout
65
66         window = context.window
67         screen = context.screen
68         scene = window.scene
69
70         # If statusbar is hidden, still show messages at the top
71         if not screen.show_statusbar:
72             layout.template_reports_banner()
73             layout.template_running_jobs()
74
75         # Active workspace view-layer is retrieved through window, not through workspace.
76         layout.template_ID(window, "scene", new="scene.new", unlink="scene.delete")
77
78         row = layout.row(align=True)
79         row.template_search(
80             window, "view_layer",
81             scene, "view_layers",
82             new="scene.view_layer_add",
83             unlink="scene.view_layer_remove")
84
85
86 class TOPBAR_HT_lower_bar(Header):
87     bl_space_type = 'TOPBAR'
88     bl_region_type = 'WINDOW'
89
90     def draw(self, context):
91         region = context.region
92
93         if region.alignment == 'LEFT':
94             self.draw_left(context)
95         elif region.alignment == 'RIGHT':
96             self.draw_right(context)
97         else:
98             self.draw_center(context)
99
100     def draw_left(self, context):
101         layout = self.layout
102
103         # Active Tool
104         # -----------
105         from .space_toolsystem_common import ToolSelectPanelHelper
106         tool = ToolSelectPanelHelper.draw_active_tool_header(context, layout)
107         tool_space_type = 'VIEW_3D' if tool is None else tool.space_type
108         tool_mode = context.mode if tool is None else tool.mode
109
110         # Object Mode Options
111         # -------------------
112
113         # Example of how tool_settings can be accessed as pop-overs.
114
115         # TODO(campbell): editing options should be after active tool options
116         # (obviously separated for from the users POV)
117         draw_fn = getattr(getattr(_draw_left_context_mode, tool_space_type, None), tool_mode, None)
118         if draw_fn is not None:
119             draw_fn(context, layout, tool)
120
121         if tool_space_type == 'VIEW_3D':
122             # Note: general mode options should be added to 'draw_right'.
123             if tool_mode == 'SCULPT':
124                 if (tool is not None) and tool.has_datablock:
125                     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
126             elif tool_mode == 'PAINT_VERTEX':
127                 if (tool is not None) and tool.has_datablock:
128                     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
129             elif tool_mode == 'PAINT_WEIGHT':
130                 if (tool is not None) and tool.has_datablock:
131                     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
132             elif tool_mode == 'PAINT_TEXTURE':
133                 if (tool is not None) and tool.has_datablock:
134                     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
135             elif tool_mode == 'EDIT_ARMATURE':
136                 pass
137             elif tool_mode == 'EDIT_CURVE':
138                 pass
139             elif tool_mode == 'EDIT_MESH':
140                 pass
141             elif tool_mode == 'POSE':
142                 pass
143             elif tool_mode == 'PARTICLE':
144                 # Disable, only shows "Brush" panel, which is already in the top-bar.
145                 # if tool.has_datablock:
146                 #     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
147                 pass
148             elif tool_mode == 'PAINT_GPENCIL':
149                 if (tool is not None) and tool.has_datablock:
150                     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_paint", category="")
151             elif tool_mode == 'SCULPT_GPENCIL':
152                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_sculpt", category="")
153             elif tool_mode == 'WEIGHT_GPENCIL':
154                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_weight", category="")
155         elif tool_space_type == 'IMAGE_EDITOR':
156             if tool_mode == 'PAINT':
157                 if (tool is not None) and tool.has_datablock:
158                     layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common_2d", category="")
159             elif context.uv_sculpt_object is not None:
160                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".uv_sculpt", category="")
161
162     def draw_center(self, context):
163         pass
164
165     def draw_right(self, context):
166         layout = self.layout
167
168         # Active Tool
169         # -----------
170         from .space_toolsystem_common import ToolSelectPanelHelper
171         tool = ToolSelectPanelHelper.tool_active_from_context(context)
172         tool_space_type = 'VIEW_3D' if tool is None else tool.space_type
173         tool_mode = context.mode if tool is None else tool.mode
174
175         if tool_space_type == 'VIEW_3D':
176             if tool_mode == 'SCULPT':
177                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".sculpt_mode", category="")
178             elif tool_mode == 'PAINT_VERTEX':
179                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".vertexpaint", category="")
180             elif tool_mode == 'PAINT_WEIGHT':
181                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".weightpaint", category="")
182             elif tool_mode == 'PAINT_TEXTURE':
183                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".imagepaint", category="")
184             elif tool_mode == 'EDIT_TEXT':
185                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".text_edit", category="")
186             elif tool_mode == 'EDIT_ARMATURE':
187                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".armature_edit", category="")
188             elif tool_mode == 'EDIT_METABALL':
189                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".mball_edit", category="")
190             elif tool_mode == 'EDIT_LATTICE':
191                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".lattice_edit", category="")
192             elif tool_mode == 'EDIT_CURVE':
193                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".curve_edit", category="")
194             elif tool_mode == 'EDIT_MESH':
195                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".mesh_edit", category="")
196             elif tool_mode == 'POSE':
197                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".posemode", category="")
198             elif tool_mode == 'PARTICLE':
199                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".particlemode", category="")
200             elif tool_mode == 'OBJECT':
201                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".objectmode", category="")
202             elif tool_mode in {'PAINT_GPENCIL', 'EDIT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
203                 # Grease pencil layer.
204                 gpl = context.active_gpencil_layer
205                 if gpl and gpl.info is not None:
206                     text = gpl.info
207                     maxw = 25
208                     if len(text) > maxw:
209                         text = text[:maxw - 5] + '..' + text[-3:]
210                 else:
211                     text = ""
212
213                 layout.label(text="Layer:")
214                 sub = layout.row()
215                 sub.ui_units_x = 8
216                 sub.popover(
217                     panel="TOPBAR_PT_gpencil_layers",
218                     text=text,
219                 )
220         elif tool_space_type == 'IMAGE_EDITOR':
221             if tool_mode == 'PAINT':
222                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".imagepaint_2d", category="")
223
224
225 class _draw_left_context_mode:
226     class VIEW_3D:
227         @staticmethod
228         def SCULPT(context, layout, tool):
229             if (tool is None) or (not tool.has_datablock):
230                 return
231
232             paint = context.tool_settings.sculpt
233             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
234
235             brush = paint.brush
236             if brush is None:
237                 return
238
239             from .properties_paint_common import (
240                 brush_basic_sculpt_settings,
241             )
242             brush_basic_sculpt_settings(layout, context, brush, compact=True)
243
244         @staticmethod
245         def PAINT_TEXTURE(context, layout, tool):
246             if (tool is None) or (not tool.has_datablock):
247                 return
248
249             paint = context.tool_settings.image_paint
250             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
251
252             brush = paint.brush
253             if brush is None:
254                 return
255
256             from .properties_paint_common import (
257                 UnifiedPaintPanel,
258                 brush_basic_texpaint_settings,
259             )
260             capabilities = brush.image_paint_capabilities
261             if capabilities.has_color:
262                 UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
263             brush_basic_texpaint_settings(layout, context, brush, compact=True)
264
265         @staticmethod
266         def PAINT_VERTEX(context, layout, tool):
267             if (tool is None) or (not tool.has_datablock):
268                 return
269
270             paint = context.tool_settings.vertex_paint
271             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
272
273             brush = paint.brush
274             if brush is None:
275                 return
276
277             from .properties_paint_common import (
278                 UnifiedPaintPanel,
279                 brush_basic_vpaint_settings,
280             )
281             capabilities = brush.vertex_paint_capabilities
282             if capabilities.has_color:
283                 UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
284             brush_basic_vpaint_settings(layout, context, brush, compact=True)
285
286         @staticmethod
287         def PAINT_WEIGHT(context, layout, tool):
288             if (tool is None) or (not tool.has_datablock):
289                 return
290
291             paint = context.tool_settings.weight_paint
292             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
293             brush = paint.brush
294             if brush is None:
295                 return
296
297             from .properties_paint_common import brush_basic_wpaint_settings
298             brush_basic_wpaint_settings(layout, context, brush, compact=True)
299
300         @staticmethod
301         def PAINT_GPENCIL(context, layout, tool):
302             if tool is None:
303                 return
304
305             is_paint = True
306             if tool.name in {"Line", "Box", "Circle", "Arc", "Curve"}:
307                 is_paint = False
308             elif tool.name == "Cutter":
309                 row = layout.row(align=True)
310                 row.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
311                 return
312             elif not tool.has_datablock:
313                 return
314
315             paint = context.tool_settings.gpencil_paint
316             brush = paint.brush
317             if brush is None:
318                 return
319
320             gp_settings = brush.gpencil_settings
321
322             def draw_color_selector():
323                 ma = gp_settings.material
324                 row = layout.row(align=True)
325
326                 icon_id = 0
327                 if ma:
328                     icon_id = ma.id_data.preview.icon_id
329                     txt_ma = ma.name
330                     maxw = 25
331                     if len(txt_ma) > maxw:
332                         txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:]
333                 else:
334                     txt_ma = ""
335
336                 row.label(text="Material:")
337                 sub = row.row()
338                 sub.ui_units_x = 8
339                 sub.popover(
340                     panel="TOPBAR_PT_gpencil_materials",
341                     text=txt_ma,
342                     icon_value=icon_id,
343                 )
344
345                 row.prop(gp_settings, "use_material_pin", text="")
346
347             row = layout.row(align=True)
348             tool_settings = context.scene.tool_settings
349             settings = tool_settings.gpencil_paint
350             row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
351
352             if brush.gpencil_tool in {'FILL', 'DRAW'}:
353                 draw_color_selector()
354
355             from .properties_paint_common import (
356                 brush_basic_gpencil_paint_settings,
357             )
358             brush_basic_gpencil_paint_settings(layout, context, brush, compact=True)
359
360             if tool.name in {"Arc", "Curve", "Line", "Box", "Circle"}:
361                 settings = context.tool_settings.gpencil_sculpt
362                 row = layout.row(align=True)
363                 row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA')
364                 sub = row.row(align=True)
365                 sub.active = settings.use_thickness_curve
366                 sub.popover(
367                     panel="TOPBAR_PT_gpencil_primitive",
368                     text="Thickness Profile"
369                 )
370
371         @staticmethod
372         def SCULPT_GPENCIL(context, layout, tool):
373             if (tool is None) or (not tool.has_datablock):
374                 return
375             tool_settings = context.tool_settings
376             settings = tool_settings.gpencil_sculpt
377             brush = settings.brush
378
379             from .properties_paint_common import (
380                 brush_basic_gpencil_sculpt_settings,
381             )
382             brush_basic_gpencil_sculpt_settings(layout, context, brush, compact=True)
383
384         @staticmethod
385         def WEIGHT_GPENCIL(context, layout, tool):
386             if (tool is None) or (not tool.has_datablock):
387                 return
388             tool_settings = context.tool_settings
389             settings = tool_settings.gpencil_sculpt
390             brush = settings.brush
391
392             from .properties_paint_common import (
393                 brush_basic_gpencil_weight_settings,
394             )
395             brush_basic_gpencil_weight_settings(layout, context, brush, compact=True)
396
397         @staticmethod
398         def PARTICLE(context, layout, tool):
399             if (tool is None) or (not tool.has_datablock):
400                 return
401
402             # See: 'VIEW3D_PT_tools_brush', basically a duplicate
403             settings = context.tool_settings.particle_edit
404             brush = settings.brush
405             tool = settings.tool
406             if tool != 'NONE':
407                 layout.prop(brush, "size", slider=True)
408                 if tool == 'ADD':
409                     layout.prop(brush, "count")
410
411                     layout.prop(settings, "use_default_interpolate")
412                     layout.prop(brush, "steps", slider=True)
413                     layout.prop(settings, "default_key_count", slider=True)
414                 else:
415                     layout.prop(brush, "strength", slider=True)
416
417                     if tool == 'LENGTH':
418                         layout.row().prop(brush, "length_mode", expand=True)
419                     elif tool == 'PUFF':
420                         layout.row().prop(brush, "puff_mode", expand=True)
421                         layout.prop(brush, "use_puff_volume")
422                     elif tool == 'COMB':
423                         # Note: actually in 'Options' panel,
424                         # disabled when used in popover.
425                         row = layout.row()
426                         row.active = settings.is_editable
427                         row.prop(settings, "use_emitter_deflect", text="Deflect Emitter")
428                         sub = row.row(align=True)
429                         sub.active = settings.use_emitter_deflect
430                         sub.prop(settings, "emitter_distance", text="Distance")
431
432     class IMAGE_EDITOR:
433         @staticmethod
434         def VIEW(context, layout, tool):
435             tool_settings = context.tool_settings
436             if tool_settings.use_uv_sculpt:
437                 if context.mode == 'EDIT_MESH':
438                     uv_sculpt = tool_settings.uv_sculpt
439                     brush = uv_sculpt.brush
440                     if brush:
441                         from .properties_paint_common import UnifiedPaintPanel
442
443                         row = layout.row(align=True)
444                         UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True)
445                         UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size", text="")
446
447                         row = layout.row(align=True)
448                         UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True)
449                         UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength", text="")
450
451         @staticmethod
452         def PAINT(context, layout, tool):
453             if (tool is None) or (not tool.has_datablock):
454                 return
455
456             paint = context.tool_settings.image_paint
457             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
458
459             brush = paint.brush
460             if brush is None:
461                 return
462
463             from .properties_paint_common import (
464                 UnifiedPaintPanel,
465                 brush_basic_texpaint_settings,
466             )
467             capabilities = brush.image_paint_capabilities
468             if capabilities.has_color:
469                 UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
470             brush_basic_texpaint_settings(layout, context, brush, compact=True)
471
472
473 class TOPBAR_PT_gpencil_layers(Panel):
474     bl_space_type = 'VIEW_3D'
475     bl_region_type = 'HEADER'
476     bl_label = "Layers"
477     bl_ui_units_x = 14
478
479     @classmethod
480     def poll(cls, context):
481         if context.gpencil_data is None:
482             return False
483
484         ob = context.object
485         if ob is not None and ob.type == 'GPENCIL':
486             return True
487
488         return False
489
490     def draw(self, context):
491         layout = self.layout
492         gpd = context.gpencil_data
493
494         # Grease Pencil data...
495         if (gpd is None) or (not gpd.layers):
496             layout.operator("gpencil.layer_add", text="New Layer")
497         else:
498             self.draw_layers(context, layout, gpd)
499
500     def draw_layers(self, context, layout, gpd):
501         row = layout.row()
502
503         col = row.column()
504         layer_rows = 10
505         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
506                           rows=layer_rows, sort_reverse=True, sort_lock=True)
507
508         gpl = context.active_gpencil_layer
509         if gpl:
510             srow = col.row(align=True)
511             srow.prop(gpl, "blend_mode", text="Blend")
512
513             srow = col.row(align=True)
514             srow.prop(gpl, "opacity", text="Opacity", slider=True)
515             srow.prop(gpl, "clamp_layer", text="",
516                       icon='MOD_MASK' if gpl.clamp_layer else 'LAYER_ACTIVE')
517
518             srow = col.row(align=True)
519             srow.prop(gpl, "use_solo_mode", text="Show Only On Keyframed")
520
521         col = row.column()
522
523         sub = col.column(align=True)
524         sub.operator("gpencil.layer_add", icon='ADD', text="")
525         sub.operator("gpencil.layer_remove", icon='REMOVE', text="")
526
527         gpl = context.active_gpencil_layer
528         if gpl:
529             sub.menu("GPENCIL_MT_layer_context_menu", icon='DOWNARROW_HLT', text="")
530
531             if len(gpd.layers) > 1:
532                 col.separator()
533
534                 sub = col.column(align=True)
535                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
536                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
537
538                 col.separator()
539
540                 sub = col.column(align=True)
541                 sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
542                 sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', text="").affect_visibility = True
543
544
545 class TOPBAR_MT_editor_menus(Menu):
546     bl_idname = "TOPBAR_MT_editor_menus"
547     bl_label = ""
548
549     def draw(self, context):
550         layout = self.layout
551         layout.menu("TOPBAR_MT_file")
552         layout.menu("TOPBAR_MT_edit")
553
554         layout.menu("TOPBAR_MT_render")
555
556         layout.menu("TOPBAR_MT_window")
557         layout.menu("TOPBAR_MT_help")
558
559
560 class TOPBAR_MT_file(Menu):
561     bl_label = "File"
562
563     def draw(self, context):
564         layout = self.layout
565
566         layout.operator_context = 'INVOKE_AREA'
567         layout.menu("TOPBAR_MT_file_new", text="New", icon='FILE_NEW')
568         layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
569         layout.menu("TOPBAR_MT_file_open_recent")
570         layout.operator("wm.revert_mainfile")
571         layout.operator("wm.recover_last_session")
572         layout.operator("wm.recover_auto_save", text="Recover Auto Save...")
573
574         layout.separator()
575
576         layout.operator_context = 'EXEC_AREA' if context.blend_data.is_saved else 'INVOKE_AREA'
577         layout.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
578
579         layout.operator_context = 'INVOKE_AREA'
580         layout.operator("wm.save_as_mainfile", text="Save As...")
581         layout.operator_context = 'INVOKE_AREA'
582         layout.operator("wm.save_as_mainfile", text="Save Copy...").copy = True
583
584         layout.separator()
585         layout.operator_context = 'INVOKE_AREA'
586
587         if any(bpy.utils.app_template_paths()):
588             app_template = context.preferences.app_template
589         else:
590             app_template = None
591
592         if app_template:
593             layout.label(text=bpy.path.display_name(app_template, has_ext=False))
594             layout.operator("wm.save_homefile")
595             layout.operator(
596                 "wm.read_factory_settings",
597                 text="Load Factory Settings",
598             ).app_template = app_template
599         else:
600             layout.operator("wm.save_homefile")
601             layout.operator("wm.read_factory_settings")
602
603         layout.separator()
604
605         layout.operator("preferences.app_template_install", text="Install Application Template...")
606
607         layout.separator()
608
609         layout.operator_context = 'INVOKE_AREA'
610         layout.operator("wm.link", text="Link...", icon='LINK_BLEND')
611         layout.operator("wm.append", text="Append...", icon='APPEND_BLEND')
612         layout.menu("TOPBAR_MT_file_previews")
613
614         layout.separator()
615
616         layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
617         layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
618
619         layout.separator()
620
621         layout.menu("TOPBAR_MT_file_external_data")
622
623         layout.separator()
624
625         layout.operator_context = 'EXEC_AREA'
626         if bpy.data.is_dirty:
627             layout.operator_context = 'INVOKE_SCREEN'  # quit dialog
628         layout.operator("wm.quit_blender", text="Quit", icon='QUIT')
629
630
631 class TOPBAR_MT_file_new(Menu):
632     bl_label = "New File"
633
634     @staticmethod
635     def app_template_paths():
636         import os
637
638         template_paths = bpy.utils.app_template_paths()
639
640         # expand template paths
641         app_templates = []
642         for path in template_paths:
643             for d in os.listdir(path):
644                 if d.startswith(("__", ".")):
645                     continue
646                 template = os.path.join(path, d)
647                 if os.path.isdir(template):
648                     # template_paths_expand.append(template)
649                     app_templates.append(d)
650
651         return sorted(app_templates)
652
653     def draw_ex(layout, context, *, use_splash=False, use_more=False):
654         layout.operator_context = 'EXEC_DEFAULT'
655
656         # Limit number of templates in splash screen, spill over into more menu.
657         paths = TOPBAR_MT_file_new.app_template_paths()
658         splash_limit = 5
659
660         if use_splash:
661             icon = 'FILE_NEW'
662             show_more = len(paths) > (splash_limit - 1)
663             if show_more:
664                 paths = paths[:splash_limit - 2]
665         elif use_more:
666             icon = 'FILE_NEW'
667             paths = paths[splash_limit - 2:]
668             show_more = False
669         else:
670             icon = 'NONE'
671             show_more = False
672
673         # Draw application templates.
674         if not use_more:
675             props = layout.operator("wm.read_homefile", text="General", icon=icon)
676             props.app_template = ""
677
678         for d in paths:
679             props = layout.operator(
680                 "wm.read_homefile",
681                 text=bpy.path.display_name(d),
682                 icon=icon,
683             )
684             props.app_template = d
685
686         if show_more:
687             layout.menu("TOPBAR_MT_templates_more", text="...")
688
689     def draw(self, context):
690         TOPBAR_MT_file_new.draw_ex(self.layout, context)
691
692
693 class TOPBAR_MT_templates_more(Menu):
694     bl_label = "Templates"
695
696     def draw(self, context):
697         bpy.types.TOPBAR_MT_file_new.draw_ex(self.layout, context, use_more=True)
698
699
700 class TOPBAR_MT_file_import(Menu):
701     bl_idname = "TOPBAR_MT_file_import"
702     bl_label = "Import"
703
704     def draw(self, context):
705         if bpy.app.build_options.collada:
706             self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)")
707         if bpy.app.build_options.alembic:
708             self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
709
710
711 class TOPBAR_MT_file_export(Menu):
712     bl_idname = "TOPBAR_MT_file_export"
713     bl_label = "Export"
714
715     def draw(self, context):
716         if bpy.app.build_options.collada:
717             self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)")
718         if bpy.app.build_options.alembic:
719             self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
720
721
722 class TOPBAR_MT_file_external_data(Menu):
723     bl_label = "External Data"
724
725     def draw(self, context):
726         layout = self.layout
727
728         icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT'
729         layout.operator("file.autopack_toggle", icon=icon)
730
731         layout.separator()
732
733         pack_all = layout.row()
734         pack_all.operator("file.pack_all")
735         pack_all.active = not bpy.data.use_autopack
736
737         unpack_all = layout.row()
738         unpack_all.operator("file.unpack_all")
739         unpack_all.active = not bpy.data.use_autopack
740
741         layout.separator()
742
743         layout.operator("file.make_paths_relative")
744         layout.operator("file.make_paths_absolute")
745         layout.operator("file.report_missing_files")
746         layout.operator("file.find_missing_files")
747
748
749 class TOPBAR_MT_file_previews(Menu):
750     bl_label = "Data Previews"
751
752     def draw(self, context):
753         layout = self.layout
754
755         layout.operator("wm.previews_ensure")
756         layout.operator("wm.previews_batch_generate")
757
758         layout.separator()
759
760         layout.operator("wm.previews_clear")
761         layout.operator("wm.previews_batch_clear")
762
763
764 class TOPBAR_MT_render(Menu):
765     bl_label = "Render"
766
767     def draw(self, context):
768         layout = self.layout
769
770         rd = context.scene.render
771
772         layout.operator("render.render", text="Render Image", icon='RENDER_STILL').use_viewport = True
773         props = layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION')
774         props.animation = True
775         props.use_viewport = True
776
777         layout.separator()
778
779         layout.operator("sound.mixdown", text="Render Audio...")
780
781         layout.separator()
782
783         layout.operator("render.view_show", text="View Render")
784         layout.operator("render.play_rendered_anim", text="View Animation")
785         layout.prop_menu_enum(rd, "display_mode", text="Display Mode")
786
787         layout.separator()
788
789         layout.prop(rd, "use_lock_interface", text="Lock Interface")
790
791
792 class TOPBAR_MT_edit(Menu):
793     bl_label = "Edit"
794
795     def draw(self, context):
796         layout = self.layout
797
798         layout.operator("ed.undo")
799         layout.operator("ed.redo")
800
801         layout.separator()
802
803         layout.operator("ed.undo_history", text="Undo History...")
804
805         layout.separator()
806
807         layout.operator("screen.repeat_last")
808         layout.operator("screen.repeat_history", text="Repeat History...")
809
810         layout.separator()
811
812         layout.operator("screen.redo_last", text="Adjust Last Operation...")
813
814         layout.separator()
815
816         layout.operator("wm.search_menu", text="Operator Search...", icon='VIEWZOOM')
817
818         layout.separator()
819
820         # Should move elsewhere (impacts outliner & 3D view).
821         tool_settings = context.tool_settings
822         layout.prop(tool_settings, "lock_object_mode")
823
824         layout.separator()
825
826         layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES')
827
828
829 class TOPBAR_MT_window(Menu):
830     bl_label = "Window"
831
832     def draw(self, context):
833         import sys
834
835         layout = self.layout
836
837         layout.operator("wm.window_new")
838         layout.operator("wm.window_new_main")
839
840         layout.separator()
841
842         layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
843
844         layout.separator()
845
846         layout.operator("screen.workspace_cycle", text="Next Workspace").direction = 'NEXT'
847         layout.operator("screen.workspace_cycle", text="Previous Workspace").direction = 'PREV'
848
849         layout.separator()
850
851         layout.prop(context.screen, "show_topbar")
852         layout.prop(context.screen, "show_statusbar")
853
854         layout.separator()
855
856         layout.operator("screen.screenshot")
857
858         if sys.platform[:3] == "win":
859             layout.separator()
860             layout.operator("wm.console_toggle", icon='CONSOLE')
861
862         if context.scene.render.use_multiview:
863             layout.separator()
864             layout.operator("wm.set_stereo_3d")
865
866
867 class TOPBAR_MT_help(Menu):
868     bl_label = "Help"
869
870     def draw(self, context):
871         # If 'url_prefill_from_blender' becomes slow it could be made into a separate operator
872         # to avoid constructing the bug report just to show this menu.
873         from bl_ui_utils.bug_report_url import url_prefill_from_blender
874
875         layout = self.layout
876
877         show_developer = context.preferences.view.show_developer_ui
878
879         layout.operator(
880             "wm.url_open", text="Manual", icon='HELP',
881         ).url = "https://docs.blender.org/manual/en/dev/"
882
883         layout.operator(
884             "wm.url_open", text="Report a Bug", icon='URL',
885         ).url = url_prefill_from_blender()
886
887         layout.separator()
888
889         layout.operator(
890             "wm.url_open", text="User Communities", icon='URL',
891         ).url = "https://www.blender.org/community/"
892         layout.operator(
893             "wm.url_open", text="Developer Community", icon='URL',
894         ).url = "https://www.blender.org/get-involved/developers/"
895
896         layout.separator()
897
898         layout.operator(
899             "wm.url_open", text="Blender Website", icon='URL',
900         ).url = "https://www.blender.org"
901         layout.operator(
902             "wm.url_open", text="Release Notes", icon='URL',
903         ).url = "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2]
904         layout.operator(
905             "wm.url_open", text="Credits", icon='URL',
906         ).url = "https://www.blender.org/about/credits/"
907
908         layout.separator()
909
910         layout.operator(
911             "wm.url_open", text="Blender Store", icon='URL',
912         ).url = "https://store.blender.org"
913         layout.operator(
914             "wm.url_open", text="Development Fund", icon='URL'
915         ).url = "https://fund.blender.org"
916         layout.operator(
917             "wm.url_open", text="Donate", icon='URL',
918         ).url = "https://www.blender.org/foundation/donation-payment/"
919
920         layout.separator()
921
922         if show_developer:
923             layout.operator(
924                 "wm.url_open", text="Python API Reference", icon='URL',
925             ).url = bpy.types.WM_OT_doc_view._prefix
926
927             layout.operator("wm.operator_cheat_sheet", icon='TEXT')
928
929         layout.operator("wm.sysinfo")
930
931         layout.separator()
932
933         layout.operator("wm.splash", icon='BLENDER')
934
935
936 class TOPBAR_MT_file_context_menu(Menu):
937     bl_label = "File Context Menu"
938
939     def draw(self, context):
940         layout = self.layout
941
942         layout.operator_context = 'INVOKE_AREA'
943         layout.operator("wm.read_homefile", text="New", icon='FILE_NEW')
944         layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
945
946         layout.separator()
947
948         layout.operator("wm.link", text="Link...", icon='LINK_BLEND')
949         layout.operator("wm.append", text="Append...", icon='APPEND_BLEND')
950
951         layout.separator()
952
953         layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
954         layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
955
956
957 class TOPBAR_MT_window_context_menu(Menu):
958     bl_label = "Window Context Menu"
959
960     def draw(self, context):
961         layout = self.layout
962
963         layout.operator_context = 'EXEC_AREA'
964
965         layout.operator("wm.window_new")
966         layout.operator("wm.window_new_main")
967
968         layout.operator_context = 'INVOKE_AREA'
969
970         layout.operator("screen.area_dupli", icon='DUPLICATE')
971
972         layout.separator()
973
974         layout.operator("screen.area_split", text="Horizontal Split").direction = 'HORIZONTAL'
975         layout.operator("screen.area_split", text="Vertical Split").direction = 'VERTICAL'
976
977         layout.separator()
978
979         layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
980
981         layout.separator()
982
983         layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES')
984
985
986 class TOPBAR_MT_workspace_menu(Menu):
987     bl_label = "Workspace"
988
989     def draw(self, context):
990         layout = self.layout
991
992         layout.operator("workspace.duplicate", text="Duplicate", icon='DUPLICATE')
993         if len(bpy.data.workspaces) > 1:
994             layout.operator("workspace.delete", text="Delete", icon='REMOVE')
995
996         layout.separator()
997
998         layout.operator("workspace.reorder_to_front", text="Reorder to Front", icon='TRIA_LEFT_BAR')
999         layout.operator("workspace.reorder_to_back", text="Reorder to Back", icon='TRIA_RIGHT_BAR')
1000
1001         layout.separator()
1002
1003         # For key binding discoverability.
1004         props = layout.operator("screen.workspace_cycle", text="Previous Workspace")
1005         props.direction = 'PREV'
1006         props = layout.operator("screen.workspace_cycle", text="Next Workspace")
1007         props.direction = 'NEXT'
1008
1009
1010 class TOPBAR_PT_active_tool(Panel):
1011     bl_space_type = 'PROPERTIES'
1012     bl_region_type = 'WINDOW'
1013     bl_category = ""
1014     bl_context = ".active_tool"  # dot on purpose (access from tool settings)
1015     bl_label = "Active Tool"
1016     bl_options = {'HIDE_HEADER'}
1017
1018     def draw(self, context):
1019         layout = self.layout
1020
1021         # Panel display of topbar tool settings.
1022         # currently displays in tool settings, keep here since the same functionality is used for the topbar.
1023
1024         layout.use_property_split = True
1025         layout.use_property_decorate = False
1026
1027         from .space_toolsystem_common import ToolSelectPanelHelper
1028         ToolSelectPanelHelper.draw_active_tool_header(context, layout, show_tool_name=True)
1029
1030
1031 # Grease Pencil Object - Primitive curve
1032 class TOPBAR_PT_gpencil_primitive(Panel):
1033     bl_space_type = 'VIEW_3D'
1034     bl_region_type = 'HEADER'
1035     bl_label = "Primitives"
1036
1037     def draw(self, context):
1038         settings = context.tool_settings.gpencil_sculpt
1039
1040         layout = self.layout
1041         # Curve
1042         layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True)
1043
1044
1045 classes = (
1046     TOPBAR_HT_upper_bar,
1047     TOPBAR_HT_lower_bar,
1048     TOPBAR_MT_file_context_menu,
1049     TOPBAR_MT_window_context_menu,
1050     TOPBAR_MT_workspace_menu,
1051     TOPBAR_MT_editor_menus,
1052     TOPBAR_MT_file,
1053     TOPBAR_MT_file_new,
1054     TOPBAR_MT_templates_more,
1055     TOPBAR_MT_file_import,
1056     TOPBAR_MT_file_export,
1057     TOPBAR_MT_file_external_data,
1058     TOPBAR_MT_file_previews,
1059     TOPBAR_MT_edit,
1060     TOPBAR_MT_render,
1061     TOPBAR_MT_window,
1062     TOPBAR_MT_help,
1063     TOPBAR_PT_active_tool,
1064     TOPBAR_PT_gpencil_layers,
1065     TOPBAR_PT_gpencil_primitive,
1066 )
1067
1068 if __name__ == "__main__":  # only for live edit.
1069     from bpy.utils import register_class
1070     for cls in classes:
1071         register_class(cls)