UI: show workspace cycling key bindings in menu
[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 toolsettings 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 == 'GPENCIL_PAINT':
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 == 'GPENCIL_SCULPT':
152                 layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_sculpt", category="")
153             elif tool_mode == 'GPENCIL_WEIGHT':
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 {'GPENCIL_PAINT', 'GPENCIL_EDIT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'}:
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 UnifiedPaintPanel
240
241             UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
242             UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
243             layout.prop(brush, "direction", text="", expand=True)
244
245         @staticmethod
246         def PAINT_TEXTURE(context, layout, tool):
247             if (tool is None) or (not tool.has_datablock):
248                 return
249
250             paint = context.tool_settings.image_paint
251             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
252
253             brush = paint.brush
254             if brush is None:
255                 return
256
257             from .properties_paint_common import UnifiedPaintPanel
258
259             UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
260             UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
261             UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
262
263         @staticmethod
264         def PAINT_VERTEX(context, layout, tool):
265             if (tool is None) or (not tool.has_datablock):
266                 return
267
268             paint = context.tool_settings.vertex_paint
269             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
270
271             brush = paint.brush
272             if brush is None:
273                 return
274
275             from .properties_paint_common import UnifiedPaintPanel
276
277             UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
278             UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
279             UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
280
281         @staticmethod
282         def PAINT_WEIGHT(context, layout, tool):
283             if (tool is None) or (not tool.has_datablock):
284                 return
285
286             paint = context.tool_settings.weight_paint
287             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
288             brush = paint.brush
289             if brush is None:
290                 return
291
292             from .properties_paint_common import UnifiedPaintPanel
293
294             UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True, text="Weight")
295             UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
296             UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
297
298         @staticmethod
299         def GPENCIL_PAINT(context, layout, tool):
300             if tool is None:
301                 return
302
303             is_paint = True
304             if (tool.name in {"Line", "Box", "Circle", "Arc"}):
305                 is_paint = False
306             elif (not tool.has_datablock):
307                 return
308
309             paint = context.tool_settings.gpencil_paint
310             brush = paint.brush
311             if brush is None:
312                 return
313
314             gp_settings = brush.gpencil_settings
315
316             def draw_color_selector():
317                 ma = gp_settings.material
318                 row = layout.row(align=True)
319
320                 icon_id = 0
321                 if ma:
322                     icon_id = ma.id_data.preview.icon_id
323                     txt_ma = ma.name
324                     maxw = 25
325                     if len(txt_ma) > maxw:
326                         txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:]
327                 else:
328                     txt_ma = ""
329
330                 row.label(text="Material:")
331                 sub = row.row()
332                 sub.ui_units_x = 8
333                 sub.popover(
334                     panel="TOPBAR_PT_gpencil_materials",
335                     text=txt_ma,
336                     icon_value=icon_id,
337                 )
338
339                 row.prop(gp_settings, "use_material_pin", text="")
340
341             row = layout.row(align=True)
342             ts = context.scene.tool_settings
343             settings = ts.gpencil_paint
344             row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
345
346             if brush.gpencil_tool == 'ERASE':
347                 row = layout.row(align=True)
348                 row.prop(brush, "size", text="Radius")
349                 row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
350                 if gp_settings.eraser_mode == 'SOFT':
351                     row = layout.row(align=True)
352                     row.prop(gp_settings, "pen_strength", slider=True)
353                     row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
354             elif brush.gpencil_tool == 'FILL':
355                 row = layout.row()
356                 row.prop(gp_settings, "fill_leak", text="Leak Size")
357                 row.prop(brush, "size", text="Thickness")
358                 row.prop(gp_settings, "fill_simplify_level", text="Simplify")
359
360                 draw_color_selector()
361
362                 row = layout.row(align=True)
363                 row.prop(gp_settings, "fill_draw_mode", text="")
364                 row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID')
365
366             else:  # brush.gpencil_tool == 'DRAW':
367                 row = layout.row(align=True)
368                 row.prop(brush, "size", text="Radius")
369                 if is_paint:
370                     row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
371                 row = layout.row(align=True)
372                 row.prop(gp_settings, "pen_strength", slider=True)
373                 if is_paint:
374                     row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
375
376                 draw_color_selector()
377
378         @staticmethod
379         def GPENCIL_SCULPT(context, layout, tool):
380             if (tool is None) or (not tool.has_datablock):
381                 return
382             tool_settings = context.tool_settings
383             settings = tool_settings.gpencil_sculpt
384             tool = settings.sculpt_tool
385             brush = settings.brush
386
387             row = layout.row(align=True)
388             row.prop(brush, "size", slider=True)
389             sub = row.row(align=True)
390             sub.enabled = tool not in {'GRAB', 'CLONE'}
391             sub.prop(brush, "use_pressure_radius", text="")
392
393             row = layout.row(align=True)
394             row.prop(brush, "strength", slider=True)
395             row.prop(brush, "use_pressure_strength", text="")
396
397             if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}:
398                 row.separator()
399                 row.prop(brush, "direction", expand=True, text="")
400
401         @staticmethod
402         def GPENCIL_WEIGHT(context, layout, tool):
403             if (tool is None) or (not tool.has_datablock):
404                 return
405             tool_settings = context.tool_settings
406             settings = tool_settings.gpencil_sculpt
407             brush = settings.brush
408
409             layout.prop(brush, "size", slider=True)
410
411             row = layout.row(align=True)
412             row.prop(brush, "strength", slider=True)
413             row.prop(brush, "use_pressure_strength", text="")
414
415             layout.prop(brush, "target_weight", slider=True)
416
417         @staticmethod
418         def PARTICLE(context, layout, tool):
419             # See: 'VIEW3D_PT_tools_brush', basically a duplicate
420             settings = context.tool_settings.particle_edit
421             brush = settings.brush
422             tool = settings.tool
423             if tool != 'NONE':
424                 layout.prop(brush, "size", slider=True)
425                 if tool == 'ADD':
426                     layout.prop(brush, "count")
427
428                     layout.prop(settings, "use_default_interpolate")
429                     layout.prop(brush, "steps", slider=True)
430                     layout.prop(settings, "default_key_count", slider=True)
431                 else:
432                     layout.prop(brush, "strength", slider=True)
433
434                     if tool == 'LENGTH':
435                         layout.row().prop(brush, "length_mode", expand=True)
436                     elif tool == 'PUFF':
437                         layout.row().prop(brush, "puff_mode", expand=True)
438                         layout.prop(brush, "use_puff_volume")
439                     elif tool == 'COMB':
440                         # Note: actually in 'Options' panel,
441                         # disabled when used in popover.
442                         row = layout.row()
443                         row.active = settings.is_editable
444                         row.prop(settings, "use_emitter_deflect", text="Deflect Emitter")
445                         sub = row.row(align=True)
446                         sub.active = settings.use_emitter_deflect
447                         sub.prop(settings, "emitter_distance", text="Distance")
448
449     class IMAGE_EDITOR:
450         @staticmethod
451         def VIEW(context, layout, tool):
452             tool_settings = context.tool_settings
453             if tool_settings.use_uv_sculpt:
454                 if context.mode == 'EDIT_MESH':
455                     uv_sculpt = tool_settings.uv_sculpt
456                     brush = uv_sculpt.brush
457                     if brush:
458                         from .properties_paint_common import UnifiedPaintPanel
459
460                         row = layout.row(align=True)
461                         UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
462                         UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size")
463
464                         row = layout.row(align=True)
465                         UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True, text="Strength")
466                         UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength")
467
468         @staticmethod
469         def PAINT(context, layout, tool):
470             if (tool is None) or (not tool.has_datablock):
471                 return
472
473             paint = context.tool_settings.image_paint
474             layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
475
476             brush = paint.brush
477             if brush is None:
478                 return
479
480             from .properties_paint_common import UnifiedPaintPanel
481
482             UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="")
483             UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
484             UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
485
486
487 class TOPBAR_PT_gpencil_layers(Panel):
488     bl_space_type = 'VIEW_3D'
489     bl_region_type = 'HEADER'
490     bl_label = "Layers"
491     bl_ui_units_x = 14
492
493     @classmethod
494     def poll(cls, context):
495         if context.gpencil_data is None:
496             return False
497
498         ob = context.object
499         if ob is not None and ob.type == 'GPENCIL':
500             return True
501
502         return False
503
504     @staticmethod
505     def draw(self, context):
506         layout = self.layout
507         gpd = context.gpencil_data
508
509         # Grease Pencil data...
510         if (gpd is None) or (not gpd.layers):
511             layout.operator("gpencil.layer_add", text="New Layer")
512         else:
513             self.draw_layers(context, layout, gpd)
514
515     def draw_layers(self, context, layout, gpd):
516         row = layout.row()
517
518         col = row.column()
519         layer_rows = 10
520         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
521                           rows=layer_rows, reverse=True)
522
523         gpl = context.active_gpencil_layer
524         if gpl:
525             srow = col.row(align=True)
526             srow.prop(gpl, "blend_mode", text="Blend")
527
528             srow = col.row(align=True)
529             srow.prop(gpl, "opacity", text="Opacity", slider=True)
530             srow.prop(gpl, "clamp_layer", text="",
531                      icon='MOD_MASK' if gpl.clamp_layer else 'LAYER_ACTIVE')
532
533         col = row.column()
534
535         sub = col.column(align=True)
536         sub.operator("gpencil.layer_add", icon='ADD', text="")
537         sub.operator("gpencil.layer_remove", icon='REMOVE', text="")
538
539         gpl = context.active_gpencil_layer
540         if gpl:
541             sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
542
543             if len(gpd.layers) > 1:
544                 col.separator()
545
546                 sub = col.column(align=True)
547                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
548                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
549
550                 col.separator()
551
552                 sub = col.column(align=True)
553                 sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
554                 sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', text="").affect_visibility = True
555
556
557 class TOPBAR_MT_editor_menus(Menu):
558     bl_idname = "TOPBAR_MT_editor_menus"
559     bl_label = ""
560
561     def draw(self, context):
562         self.draw_menus(self.layout, context)
563
564     @staticmethod
565     def draw_menus(layout, context):
566         layout.menu("TOPBAR_MT_file")
567         layout.menu("TOPBAR_MT_edit")
568
569         layout.menu("TOPBAR_MT_render")
570
571         layout.menu("TOPBAR_MT_window")
572         layout.menu("TOPBAR_MT_help")
573
574
575 class TOPBAR_MT_file(Menu):
576     bl_label = "File"
577
578     def draw(self, context):
579         layout = self.layout
580
581         layout.operator_context = 'INVOKE_AREA'
582         layout.menu("TOPBAR_MT_file_new", text="New", icon='FILE_NEW')
583         layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
584         layout.menu("TOPBAR_MT_file_open_recent")
585         layout.operator("wm.revert_mainfile")
586         layout.operator("wm.recover_last_session")
587         layout.operator("wm.recover_auto_save", text="Recover Auto Save...")
588
589         layout.separator()
590
591         layout.operator_context = 'EXEC_AREA' if context.blend_data.is_saved else 'INVOKE_AREA'
592         layout.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
593
594         layout.operator_context = 'INVOKE_AREA'
595         layout.operator("wm.save_as_mainfile", text="Save As...")
596         layout.operator_context = 'INVOKE_AREA'
597         layout.operator("wm.save_as_mainfile", text="Save Copy...").copy = True
598
599         layout.separator()
600         layout.operator_context = 'INVOKE_AREA'
601
602         if any(bpy.utils.app_template_paths()):
603             app_template = context.user_preferences.app_template
604         else:
605             app_template = None
606
607         if app_template:
608             layout.label(text=bpy.path.display_name(app_template))
609             layout.operator("wm.save_homefile")
610             layout.operator(
611                 "wm.read_factory_settings",
612                 text="Load Factory Settings",
613             ).app_template = app_template
614         else:
615             layout.operator("wm.save_homefile")
616             layout.operator("wm.read_factory_settings")
617
618         layout.separator()
619
620         layout.operator("wm.app_template_install", text="Install Application Template...")
621
622         layout.separator()
623
624         layout.operator_context = 'INVOKE_AREA'
625         layout.operator("wm.link", text="Link...", icon='LINK_BLEND')
626         layout.operator("wm.append", text="Append...", icon='APPEND_BLEND')
627         layout.menu("TOPBAR_MT_file_previews")
628
629         layout.separator()
630
631         layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
632         layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
633
634         layout.separator()
635
636         layout.menu("TOPBAR_MT_file_external_data")
637
638         layout.separator()
639
640         layout.operator_context = 'EXEC_AREA'
641         if bpy.data.is_dirty and context.user_preferences.view.use_quit_dialog:
642             layout.operator_context = 'INVOKE_SCREEN'  # quit dialog
643         layout.operator("wm.quit_blender", text="Quit", icon='QUIT')
644
645
646 class TOPBAR_MT_file_new(Menu):
647     bl_label = "New File"
648
649     @staticmethod
650     def app_template_paths():
651         import os
652
653         template_paths = bpy.utils.app_template_paths()
654
655         # expand template paths
656         app_templates = []
657         for path in template_paths:
658             for d in os.listdir(path):
659                 if d.startswith(("__", ".")):
660                     continue
661                 template = os.path.join(path, d)
662                 if os.path.isdir(template):
663                     # template_paths_expand.append(template)
664                     app_templates.append(d)
665
666         return sorted(app_templates)
667
668     def draw_ex(layout, context, *, use_splash=False, use_more=False):
669         layout.operator_context = 'EXEC_DEFAULT'
670
671         # Limit number of templates in splash screen, spill over into more menu.
672         paths = TOPBAR_MT_file_new.app_template_paths()
673         splash_limit = 5
674
675         if use_splash:
676             icon = 'FILE_NEW'
677             show_more = len(paths) > (splash_limit - 1)
678             if show_more:
679                 paths = paths[:splash_limit - 2]
680         elif use_more:
681             icon = 'FILE_NEW'
682             paths = paths[splash_limit - 2:]
683             show_more = False
684         else:
685             icon = 'NONE'
686             show_more = False
687
688         # Draw application templates.
689         if not use_more:
690             props = layout.operator("wm.read_homefile", text="General", icon=icon)
691             props.app_template = ""
692
693         for d in paths:
694             props = layout.operator(
695                 "wm.read_homefile",
696                 text=bpy.path.display_name(d),
697                 icon=icon,
698             )
699             props.app_template = d
700
701         if show_more:
702             layout.menu("TOPBAR_MT_templates_more", text="...")
703
704     def draw(self, context):
705         TOPBAR_MT_file_new.draw_ex(self.layout, context)
706
707
708 class TOPBAR_MT_templates_more(Menu):
709     bl_label = "Templates"
710
711     def draw(self, context):
712         bpy.types.TOPBAR_MT_file_new.draw_ex(self.layout, context, use_more=True)
713
714
715 class TOPBAR_MT_file_import(Menu):
716     bl_idname = "TOPBAR_MT_file_import"
717     bl_label = "Import"
718
719     def draw(self, context):
720         if bpy.app.build_options.collada:
721             self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)")
722         if bpy.app.build_options.alembic:
723             self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
724
725
726 class TOPBAR_MT_file_export(Menu):
727     bl_idname = "TOPBAR_MT_file_export"
728     bl_label = "Export"
729
730     def draw(self, context):
731         if bpy.app.build_options.collada:
732             self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)")
733         if bpy.app.build_options.alembic:
734             self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
735
736
737 class TOPBAR_MT_file_external_data(Menu):
738     bl_label = "External Data"
739
740     def draw(self, context):
741         layout = self.layout
742
743         icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT'
744         layout.operator("file.autopack_toggle", icon=icon)
745
746         layout.separator()
747
748         pack_all = layout.row()
749         pack_all.operator("file.pack_all")
750         pack_all.active = not bpy.data.use_autopack
751
752         unpack_all = layout.row()
753         unpack_all.operator("file.unpack_all")
754         unpack_all.active = not bpy.data.use_autopack
755
756         layout.separator()
757
758         layout.operator("file.make_paths_relative")
759         layout.operator("file.make_paths_absolute")
760         layout.operator("file.report_missing_files")
761         layout.operator("file.find_missing_files")
762
763
764 class TOPBAR_MT_file_previews(Menu):
765     bl_label = "Data Previews"
766
767     def draw(self, context):
768         layout = self.layout
769
770         layout.operator("wm.previews_ensure")
771         layout.operator("wm.previews_batch_generate")
772
773         layout.separator()
774
775         layout.operator("wm.previews_clear")
776         layout.operator("wm.previews_batch_clear")
777
778
779 class TOPBAR_MT_render(Menu):
780     bl_label = "Render"
781
782     def draw(self, context):
783         layout = self.layout
784
785         rd = context.scene.render
786
787         layout.operator("render.render", text="Render Image", icon='RENDER_STILL').use_viewport = True
788         props = layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION')
789         props.animation = True
790         props.use_viewport = True
791
792         layout.separator()
793
794         layout.operator("sound.mixdown", text="Render Audio...")
795
796         layout.separator()
797
798         layout.operator("render.view_show", text="View Render")
799         layout.operator("render.play_rendered_anim", text="View Animation")
800         layout.prop_menu_enum(rd, "display_mode", text="Display Mode")
801
802         layout.separator()
803
804         layout.prop(rd, "use_lock_interface", text="Lock Interface")
805
806
807 class TOPBAR_MT_edit(Menu):
808     bl_label = "Edit"
809
810     def draw(self, context):
811         layout = self.layout
812
813         layout.operator("ed.undo")
814         layout.operator("ed.redo")
815
816         layout.separator()
817
818         layout.operator("ed.undo_history", text="Undo History...")
819
820         layout.separator()
821
822         layout.operator("screen.repeat_last")
823         layout.operator("screen.repeat_history", text="Repeat History...")
824
825         layout.separator()
826
827         layout.operator("screen.redo_last", text="Adjust Last Operation...")
828
829         layout.separator()
830
831         layout.operator("wm.search_menu", text="Operator Search...", icon='VIEWZOOM')
832
833         layout.separator()
834
835         # Should move elsewhere (impacts outliner & 3D view).
836         tool_settings = context.tool_settings
837         layout.prop(tool_settings, "lock_object_mode")
838
839         layout.separator()
840
841         layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES')
842
843
844 class TOPBAR_MT_window(Menu):
845     bl_label = "Window"
846
847     def draw(self, context):
848         import sys
849
850         layout = self.layout
851
852         layout.operator("wm.window_new")
853         layout.operator("wm.window_new_main")
854
855         layout.separator()
856
857         layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
858
859         layout.separator()
860
861         layout.operator("screen.workspace_cycle", text="Next Workspace").direction = 'NEXT'
862         layout.operator("screen.workspace_cycle", text="Previous Workspace").direction = 'PREV'
863
864         layout.separator()
865
866         layout.prop(context.screen, "show_topbar")
867         layout.prop(context.screen, "show_statusbar")
868
869         layout.separator()
870
871         layout.operator("screen.screenshot")
872
873         if sys.platform[:3] == "win":
874             layout.separator()
875             layout.operator("wm.console_toggle", icon='CONSOLE')
876
877         if context.scene.render.use_multiview:
878             layout.separator()
879             layout.operator("wm.set_stereo_3d")
880
881
882 class TOPBAR_MT_help(Menu):
883     bl_label = "Help"
884
885     def draw(self, context):
886         layout = self.layout
887
888         show_developer = context.user_preferences.view.show_developer_ui
889
890         layout.operator(
891             "wm.url_open", text="Manual", icon='HELP',
892         ).url = "https://docs.blender.org/manual/en/dev/"
893
894         layout.operator(
895             "wm.url_open", text="Report a Bug", icon='URL',
896         ).url = "https://developer.blender.org/maniphest/task/edit/form/1"
897
898         layout.separator()
899
900         layout.operator(
901             "wm.url_open", text="User Communities", icon='URL',
902         ).url = "https://www.blender.org/community/"
903         layout.operator(
904             "wm.url_open", text="Developer Community", icon='URL',
905         ).url = "https://www.blender.org/get-involved/developers/"
906
907         layout.separator()
908
909         layout.operator(
910             "wm.url_open", text="Blender Website", icon='URL',
911         ).url = "https://www.blender.org"
912         layout.operator(
913             "wm.url_open", text="Release Notes", icon='URL',
914         ).url = "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2]
915         layout.operator(
916             "wm.url_open", text="Credits", icon='URL',
917         ).url = "https://www.blender.org/about/credits/"
918
919         layout.separator()
920
921         layout.operator(
922             "wm.url_open", text="Blender Store", icon='URL',
923         ).url = "https://store.blender.org"
924         layout.operator(
925             "wm.url_open", text="Development Fund", icon='URL'
926         ).url = "https://fund.blender.org"
927         layout.operator(
928             "wm.url_open", text="Donate", icon='URL',
929         ).url = "https://www.blender.org/foundation/donation-payment/"
930
931         layout.separator()
932
933         if show_developer:
934             layout.operator(
935                 "wm.url_open", text="Python API Reference", icon='URL',
936             ).url = bpy.types.WM_OT_doc_view._prefix
937
938             layout.operator("wm.operator_cheat_sheet", icon='TEXT')
939
940         layout.operator("wm.sysinfo")
941
942         layout.separator()
943
944         layout.operator("wm.splash", icon='BLENDER')
945
946
947 class TOPBAR_MT_file_specials(Menu):
948     bl_label = "File Context Menu"
949
950     def draw(self, context):
951         layout = self.layout
952
953         layout.operator_context = 'INVOKE_AREA'
954         layout.operator("wm.read_homefile", text="New", icon='FILE_NEW')
955         layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
956
957         layout.separator()
958
959         layout.operator("wm.link", text="Link...", icon='LINK_BLEND')
960         layout.operator("wm.append", text="Append...", icon='APPEND_BLEND')
961
962         layout.separator()
963
964         layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
965         layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
966
967
968 class TOPBAR_MT_window_specials(Menu):
969     bl_label = "Window Context Menu"
970
971     def draw(self, context):
972         layout = self.layout
973
974         layout.operator_context = 'EXEC_AREA'
975
976         layout.operator("wm.window_new")
977         layout.operator("wm.window_new_main")
978
979         layout.operator_context = 'INVOKE_AREA'
980
981         layout.operator("screen.area_dupli", icon='DUPLICATE')
982
983         layout.separator()
984
985         layout.operator("screen.area_split", text="Horizontal Split").direction = 'HORIZONTAL'
986         layout.operator("screen.area_split", text="Vertical Split").direction = 'VERTICAL'
987
988         layout.separator()
989
990         layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
991
992         layout.separator()
993
994         layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES')
995
996
997 class TOPBAR_MT_workspace_menu(Menu):
998     bl_label = "Workspace"
999
1000     def draw(self, context):
1001         layout = self.layout
1002
1003         layout.operator("workspace.duplicate", text="Duplicate", icon='DUPLICATE')
1004         if len(bpy.data.workspaces) > 1:
1005             layout.operator("workspace.delete", text="Delete", icon='REMOVE')
1006
1007         layout.separator()
1008
1009         layout.operator("workspace.reorder_to_front", text="Reorder to Front", icon='TRIA_LEFT_BAR')
1010         layout.operator("workspace.reorder_to_back", text="Reorder to Back", icon='TRIA_RIGHT_BAR')
1011
1012         layout.separator()
1013
1014         # For key binding discoverability.
1015         props = layout.operator("screen.workspace_cycle", text="Previous Workspace")
1016         props.direction = 'PREV'
1017         props = layout.operator("screen.workspace_cycle", text="Next Workspace")
1018         props.direction = 'NEXT'
1019
1020
1021 class TOPBAR_PT_active_tool(Panel):
1022     bl_space_type = 'PROPERTIES'
1023     bl_region_type = 'WINDOW'
1024     bl_category = ""
1025     bl_context = ".active_tool"  # dot on purpose (access from tool settings)
1026     bl_label = "Active Tool"
1027     bl_options = {'HIDE_HEADER'}
1028
1029     def draw(self, context):
1030         layout = self.layout
1031
1032         # Panel display of topbar tool settings.
1033         # currently displays in tool settings, keep here since the same functionality is used for the topbar.
1034
1035         layout.use_property_split = True
1036         layout.use_property_decorate = False
1037
1038         from .space_toolsystem_common import ToolSelectPanelHelper
1039         ToolSelectPanelHelper.draw_active_tool_header(context, layout, show_tool_name=True)
1040
1041
1042 classes = (
1043     TOPBAR_HT_upper_bar,
1044     TOPBAR_HT_lower_bar,
1045     TOPBAR_MT_file_specials,
1046     TOPBAR_MT_window_specials,
1047     TOPBAR_MT_workspace_menu,
1048     TOPBAR_MT_editor_menus,
1049     TOPBAR_MT_file,
1050     TOPBAR_MT_file_new,
1051     TOPBAR_MT_templates_more,
1052     TOPBAR_MT_file_import,
1053     TOPBAR_MT_file_export,
1054     TOPBAR_MT_file_external_data,
1055     TOPBAR_MT_file_previews,
1056     TOPBAR_MT_edit,
1057     TOPBAR_MT_render,
1058     TOPBAR_MT_window,
1059     TOPBAR_MT_help,
1060     TOPBAR_PT_active_tool,
1061     TOPBAR_PT_gpencil_layers,
1062 )
1063
1064 if __name__ == "__main__":  # only for live edit.
1065     from bpy.utils import register_class
1066     for cls in classes:
1067         register_class(cls)