This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/release/scripts/startup/bl_ui/space_topbar.py
Campbell Barton 995fa1f4c0 UI: new tool properties space type
This currently shows panels that were in the 2.79 3D view toolbar
which are now popovers.

In some cases it's useful for these to stay open.
This commit adds a space type to do this.

Note this is currently empty in object mode.
2018-05-31 21:45:26 +02:00

707 lines
24 KiB
Python

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.types import Header, Menu, Panel
class TOPBAR_HT_upper_bar(Header):
bl_space_type = 'TOPBAR'
def draw(self, context):
region = context.region
if region.alignment == 'RIGHT':
self.draw_right(context)
else:
self.draw_left(context)
def draw_left(self, context):
layout = self.layout
window = context.window
screen = context.screen
layout.operator("wm.splash", text="", icon='BLENDER', emboss=False)
INFO_MT_editor_menus.draw_collapsible(context, layout)
layout.separator()
if not screen.show_fullscreen:
layout.template_ID_tabs(
window, "workspace",
new="workspace.workspace_add_menu",
unlink="workspace.workspace_delete",
)
else:
layout.operator(
"screen.back_to_previous",
icon='SCREEN_BACK',
text="Back to Previous",
)
def draw_right(self, context):
layout = self.layout
window = context.window
scene = window.scene
# Active workspace view-layer is retrieved through window, not through workspace.
layout.template_ID(window, "scene", new="scene.new", unlink="scene.delete")
row = layout.row(align=True)
row.template_search(
window, "view_layer",
scene, "view_layers",
new="scene.view_layer_add",
unlink="scene.view_layer_remove")
class TOPBAR_HT_lower_bar(Header):
bl_space_type = 'TOPBAR'
bl_region_type = 'WINDOW'
def draw(self, context):
layout = self.layout
region = context.region
if region.alignment == 'LEFT':
self.draw_left(context)
elif region.alignment == 'RIGHT':
self.draw_right(context)
else:
self.draw_center(context)
def draw_left(self, context):
layout = self.layout
layer = context.view_layer
object = layer.objects.active
# Object Mode
# -----------
object_mode = 'OBJECT' if object is None else object.mode
act_mode_item = bpy.types.Object.bl_rna.properties['mode'].enum_items[object_mode]
layout.operator_menu_enum("object.mode_set", "mode", text=act_mode_item.name, icon=act_mode_item.icon)
layout.template_header_3D_mode()
def draw_center(self, context):
layout = self.layout
mode = context.mode
# Active Tool
# -----------
from .space_toolsystem_common import ToolSelectPanelHelper
ToolSelectPanelHelper.draw_active_tool_header(context, layout)
# Object Mode Options
# -------------------
# Example of how toolsettings can be accessed as pop-overs.
# TODO(campbell): editing options should be after active tool options
# (obviously separated for from the users POV)
draw_fn = getattr(_draw_left_context_mode, mode, None)
if draw_fn is not None:
draw_fn(context, layout)
# Note: general mode options should be added to 'draw_right'.
if mode == 'SCULPT':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
elif mode == 'PAINT_VERTEX':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
elif mode == 'PAINT_WEIGHT':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
elif mode == 'PAINT_TEXTURE':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="")
elif mode == 'EDIT_ARMATURE':
pass
elif mode == 'EDIT_CURVE':
pass
elif mode == 'EDIT_MESH':
pass
elif mode == 'POSE':
pass
elif mode == 'PARTICLE':
pass
def draw_right(self, context):
layout = self.layout
# General options, note, these _could_ display at the RHS of the draw_left callback.
# we just want them not to be confused with tool options.
mode = context.mode
if mode == 'SCULPT':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".sculpt_mode", category="")
elif mode == 'PAINT_VERTEX':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".vertexpaint", category="")
elif mode == 'PAINT_WEIGHT':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".weightpaint", category="")
elif mode == 'PAINT_TEXTURE':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".imagepaint", category="")
elif mode == 'EDIT_ARMATURE':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".armature_edit", category="")
elif mode == 'EDIT_CURVE':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".curve_edit", category="")
elif mode == 'EDIT_MESH':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".mesh_edit", category="")
elif mode == 'POSE':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".posemode", category="")
elif mode == 'PARTICLE':
layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".particlemode", category="")
# 3D View Options, tsk. maybe users aren't always using 3D view?
toolsettings = context.tool_settings
scene = context.scene
obj = context.active_object
object_mode = 'OBJECT' if obj is None else obj.mode
# Pivot & Orientation
pivot_point = context.tool_settings.transform_pivot_point
act_pivot_point = bpy.types.ToolSettings.bl_rna.properties['transform_pivot_point'].enum_items[pivot_point]
row = layout.row(align=True)
row.popover(
space_type='TOPBAR',
region_type='HEADER',
panel_type="TOPBAR_PT_pivot_point",
icon=act_pivot_point.icon,
text="",
)
if obj:
# Proportional editing
if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode:
row = layout.row(align=True)
row.prop(toolsettings, "proportional_edit", icon_only=True)
sub = row.row(align=True)
sub.active = toolsettings.proportional_edit != 'DISABLED'
sub.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
elif object_mode in {'EDIT', 'PARTICLE_EDIT'}:
row = layout.row(align=True)
row.prop(toolsettings, "proportional_edit", icon_only=True)
sub = row.row(align=True)
sub.active = toolsettings.proportional_edit != 'DISABLED'
sub.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
elif object_mode == 'OBJECT':
row = layout.row(align=True)
row.prop(toolsettings, "use_proportional_edit_objects", icon_only=True)
sub = row.row(align=True)
sub.active = toolsettings.use_proportional_edit_objects
sub.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
else:
# Proportional editing
if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode:
row = layout.row(align=True)
row.prop(toolsettings, "proportional_edit", icon_only=True)
sub = row.row(align=True)
sub.active = toolsettings.proportional_edit != 'DISABLED'
sub.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
# Snap
show_snap = False
if obj is None:
show_snap = True
else:
if object_mode not in {'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT'}:
show_snap = True
else:
from .properties_paint_common import UnifiedPaintPanel
paint_settings = UnifiedPaintPanel.paint_settings(context)
if paint_settings:
brush = paint_settings.brush
if brush and brush.stroke_method == 'CURVE':
show_snap = True
if show_snap:
snap_items = bpy.types.ToolSettings.bl_rna.properties['snap_elements'].enum_items
for elem in toolsettings.snap_elements:
# TODO: Display multiple icons.
# (Currently only one of the enabled modes icons is displayed)
icon = snap_items[elem].icon
break
else:
icon = 'NONE'
row = layout.row(align=True)
row.prop(toolsettings, "use_snap", text="")
sub = row.row(align=True)
sub.popover(
space_type='TOPBAR',
region_type='HEADER',
panel_type="TOPBAR_PT_snapping",
icon=icon,
text=""
)
layout.prop(scene, "transform_orientation", text="")
# Command Settings (redo)
op = context.active_operator
row = layout.row()
row.enabled = op is not None
row.popover(
space_type='TOPBAR',
region_type='HEADER',
panel_type="TOPBAR_PT_redo",
text=op.name + " Settings" if op else "Command Settings",
)
class _draw_left_context_mode:
@staticmethod
def SCULPT(context, layout):
brush = context.tool_settings.sculpt.brush
if brush is None:
return
from .properties_paint_common import UnifiedPaintPanel
UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
layout.prop(brush, "direction", text="", expand=True)
def PAINT_TEXTURE(context, layout):
brush = context.tool_settings.vertex_paint.brush
if brush is None:
return
from .properties_paint_common import UnifiedPaintPanel
layout.prop(brush, "color", text="")
UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
def PAINT_VERTEX(context, layout):
brush = context.tool_settings.vertex_paint.brush
if brush is None:
return
from .properties_paint_common import UnifiedPaintPanel
layout.prop(brush, "color", text="")
UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
def PAINT_WEIGHT(context, layout):
brush = context.tool_settings.weight_paint.brush
if brush is None:
return
from .properties_paint_common import UnifiedPaintPanel
UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True, text="Weight")
UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True, text="Radius")
UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True, text="Strength")
class TOPBAR_PT_pivot_point(Panel):
bl_space_type = 'TOPBAR'
bl_region_type = 'HEADER'
bl_label = "Pivot Point"
def draw(self, context):
toolsettings = context.tool_settings
obj = context.active_object
mode = context.mode
layout = self.layout
col = layout.column()
col.label("Pivot Point")
col.prop(toolsettings, "transform_pivot_point", expand=True)
col.separator()
if (obj is None) or (mode in {'OBJECT', 'POSE', 'WEIGHT_PAINT'}):
col.prop(
toolsettings,
"use_transform_pivot_point_align",
text="Center Points Only",
)
class TOPBAR_PT_snapping(Panel):
bl_space_type = 'TOPBAR'
bl_region_type = 'HEADER'
bl_label = "Snapping"
def draw(self, context):
toolsettings = context.tool_settings
snap_elements = toolsettings.snap_elements
obj = context.active_object
mode = context.mode
object_mode = 'OBJECT' if obj is None else obj.mode
layout = self.layout
col = layout.column()
col.label("Snapping")
col.prop(toolsettings, "snap_elements", expand=True)
col.separator()
if 'INCREMENT' in snap_elements:
col.prop(toolsettings, "use_snap_grid_absolute")
if snap_elements != {'INCREMENT'}:
col.label("Target")
row = col.row(align=True)
row.prop(toolsettings, "snap_target", expand=True)
if obj:
if object_mode == 'EDIT':
col.prop(toolsettings, "use_snap_self")
if object_mode in {'OBJECT', 'POSE', 'EDIT'}:
col.prop(toolsettings, "use_snap_align_rotation", text="Align Rotation")
if 'FACE' in snap_elements:
col.prop(toolsettings, "use_snap_project", text="Project Elements")
if 'VOLUME' in snap_elements:
col.prop(toolsettings, "use_snap_peel_object")
# Auto-Merge Editing
if obj:
if (object_mode == 'EDIT' and obj.type == 'MESH'):
col.prop(toolsettings, "use_mesh_automerge", icon='AUTOMERGE_ON')
class INFO_MT_editor_menus(Menu):
bl_idname = "INFO_MT_editor_menus"
bl_label = ""
def draw(self, context):
self.draw_menus(self.layout, context)
@staticmethod
def draw_menus(layout, context):
layout.menu("INFO_MT_file")
layout.menu("INFO_MT_edit")
layout.menu("INFO_MT_render")
layout.menu("INFO_MT_window")
layout.menu("INFO_MT_help")
class INFO_MT_file(Menu):
bl_label = "File"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.read_homefile", text="New", icon='NEW')
layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
layout.menu("INFO_MT_file_open_recent", icon='OPEN_RECENT')
layout.operator("wm.revert_mainfile", icon='FILE_REFRESH')
layout.operator("wm.recover_last_session", icon='RECOVER_LAST')
layout.operator("wm.recover_auto_save", text="Recover Auto Save...", icon='RECOVER_AUTO')
layout.separator()
layout.operator_context = 'EXEC_AREA' if context.blend_data.is_saved else 'INVOKE_AREA'
layout.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.save_as_mainfile", text="Save As...", icon='SAVE_AS')
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.save_as_mainfile", text="Save Copy...", icon='SAVE_COPY').copy = True
layout.separator()
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.save_homefile", icon='SAVE_PREFS')
layout.operator("wm.read_factory_settings", icon='LOAD_FACTORY')
layout.separator()
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.link", text="Link", icon='LINK_BLEND')
layout.operator("wm.append", text="Append", icon='APPEND_BLEND')
layout.menu("INFO_MT_file_previews")
layout.separator()
layout.menu("INFO_MT_file_import", icon='IMPORT')
layout.menu("INFO_MT_file_export", icon='EXPORT')
layout.separator()
layout.menu("INFO_MT_file_external_data", icon='EXTERNAL_DATA')
layout.separator()
layout.operator_context = 'EXEC_AREA'
if bpy.data.is_dirty and context.user_preferences.view.use_quit_dialog:
layout.operator_context = 'INVOKE_SCREEN' # quit dialog
layout.operator("wm.quit_blender", text="Quit", icon='QUIT')
class INFO_MT_file_import(Menu):
bl_idname = "INFO_MT_file_import"
bl_label = "Import"
def draw(self, context):
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)")
if bpy.app.build_options.alembic:
self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
class INFO_MT_file_export(Menu):
bl_idname = "INFO_MT_file_export"
bl_label = "Export"
def draw(self, context):
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)")
if bpy.app.build_options.alembic:
self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
class INFO_MT_file_external_data(Menu):
bl_label = "External Data"
def draw(self, context):
layout = self.layout
icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT'
layout.operator("file.autopack_toggle", icon=icon)
layout.separator()
pack_all = layout.row()
pack_all.operator("file.pack_all")
pack_all.active = not bpy.data.use_autopack
unpack_all = layout.row()
unpack_all.operator("file.unpack_all")
unpack_all.active = not bpy.data.use_autopack
layout.separator()
layout.operator("file.make_paths_relative")
layout.operator("file.make_paths_absolute")
layout.operator("file.report_missing_files")
layout.operator("file.find_missing_files")
class INFO_MT_file_previews(Menu):
bl_label = "Data Previews"
def draw(self, context):
layout = self.layout
layout.operator("wm.previews_ensure")
layout.operator("wm.previews_batch_generate")
layout.separator()
layout.operator("wm.previews_clear")
layout.operator("wm.previews_batch_clear")
class INFO_MT_game(Menu):
bl_label = "Game"
def draw(self, context):
layout = self.layout
gs = context.scene.game_settings
layout.operator("view3d.game_start")
layout.separator()
layout.prop(gs, "show_debug_properties")
layout.prop(gs, "show_framerate_profile")
layout.prop(gs, "show_physics_visualization")
layout.prop(gs, "use_deprecation_warnings")
layout.prop(gs, "use_animation_record")
layout.separator()
layout.prop(gs, "use_auto_start")
class INFO_MT_render(Menu):
bl_label = "Render"
def draw(self, context):
layout = self.layout
rd = context.scene.render
layout.operator("render.render", text="Render Image", icon='RENDER_STILL').use_viewport = True
props = layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION')
props.animation = True
props.use_viewport = True
layout.operator("sound.mixdown", text="Render Audio", icon='PLAY_AUDIO')
layout.separator()
layout.prop_menu_enum(rd, "display_mode", text="Display Mode", icon='IMAGE_COL')
layout.prop(rd, "use_lock_interface", text="Lock Interface")
layout.separator()
props = layout.operator("render.opengl", text="OpenGL Render Image", icon='RENDER_STILL')
props.view_context = False
props = layout.operator("render.opengl", text="OpenGL Render Animation", icon='RENDER_ANIMATION')
props.view_context = False
props.animation = True
layout.menu("INFO_MT_opengl_render", icon='SETTINGS')
layout.separator()
layout.operator("render.view_show")
layout.operator("render.play_rendered_anim", icon='PLAY')
class INFO_MT_opengl_render(Menu):
bl_label = "OpenGL Render Options"
def draw(self, context):
layout = self.layout
rd = context.scene.render
layout.prop(rd, "use_antialiasing")
layout.prop(rd, "use_full_sample")
layout.prop_menu_enum(rd, "antialiasing_samples")
layout.prop_menu_enum(rd, "alpha_mode")
class INFO_MT_edit(Menu):
bl_label = "Edit"
def draw(self, context):
layout = self.layout
layout.operator("ed.undo")
layout.operator("ed.redo")
layout.separator()
layout.operator("ed.undo_history")
layout.separator()
layout.operator("screen.userpref_show", text="User Preferences...", icon='PREFERENCES')
class INFO_MT_window(Menu):
bl_label = "Window"
def draw(self, context):
import sys
layout = self.layout
layout.operator("wm.window_new")
layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
layout.separator()
layout.operator("screen.screenshot")
layout.operator("screen.screencast")
if sys.platform[:3] == "win":
layout.separator()
layout.operator("wm.console_toggle", icon='CONSOLE')
if context.scene.render.use_multiview:
layout.separator()
layout.operator("wm.set_stereo_3d", icon='CAMERA_STEREO')
class INFO_MT_help(Menu):
bl_label = "Help"
def draw(self, context):
layout = self.layout
layout.operator(
"wm.url_open", text="Manual", icon='HELP',
).url = "https://docs.blender.org/manual/en/dev/"
layout.operator(
"wm.url_open", text="Release Log", icon='URL',
).url = "http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/%d.%d" % bpy.app.version[:2]
layout.separator()
layout.operator(
"wm.url_open", text="Blender Website", icon='URL',
).url = "https://www.blender.org"
layout.operator(
"wm.url_open", text="Blender Store", icon='URL',
).url = "https://store.blender.org"
layout.operator(
"wm.url_open", text="Developer Community", icon='URL',
).url = "https://www.blender.org/get-involved/"
layout.operator(
"wm.url_open", text="User Community", icon='URL',
).url = "https://www.blender.org/support/user-community"
layout.separator()
layout.operator(
"wm.url_open", text="Report a Bug", icon='URL',
).url = "https://developer.blender.org/maniphest/task/edit/form/1"
layout.separator()
layout.operator(
"wm.url_open", text="Python API Reference", icon='URL',
).url = bpy.types.WM_OT_doc_view._prefix
layout.operator("wm.operator_cheat_sheet", icon='TEXT')
layout.operator("wm.sysinfo", icon='TEXT')
layout.separator()
layout.operator("wm.splash", icon='BLENDER')
classes = (
TOPBAR_HT_upper_bar,
TOPBAR_HT_lower_bar,
TOPBAR_PT_pivot_point,
TOPBAR_PT_snapping,
INFO_MT_editor_menus,
INFO_MT_file,
INFO_MT_file_import,
INFO_MT_file_export,
INFO_MT_file_external_data,
INFO_MT_file_previews,
INFO_MT_edit,
INFO_MT_game,
INFO_MT_render,
INFO_MT_opengl_render,
INFO_MT_window,
INFO_MT_help,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)