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