blender/scripts/startup/bl_ui/space_project.py
Pratik Borhade 73915eb274 UI: Always draw project categories
Draw all the categories tab even if project doesn't exist. Basically,
remove poll function that decides this.

See PR description for images

Pull Request: https://projects.blender.org/blender/blender/pulls/163183
2026-08-28 10:22:31 +02:00

307 lines
9.1 KiB
Python

# SPDX-FileCopyrightText: 2026 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Header, Menu, Panel
from bpy.app.translations import (
pgettext_iface as iface_,
contexts as i18n_contexts,
)
from .space_userpref import CenterAlignMixIn
MAIN_SECTION_NAME = "General"
# -------------------------------------------------------------
# Header
class PROJECT_HT_header(Header):
bl_space_type = 'PROJECT'
def draw(self, context):
layout = self.layout
layout.template_header()
PROJECT_MT_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
class PROJECT_MT_editor_menus(Menu):
bl_idname = "PROJECT_MT_editor_menus"
bl_label = ""
def draw(self, context):
layout = self.layout
layout.menu("PROJECT_MT_view")
layout.menu("PROJECT_MT_save_load", text="Project", text_ctxt=i18n_contexts.editor_preferences)
class PROJECT_MT_view(Menu):
bl_label = "View"
def draw(self, context):
layout = self.layout
project_space = context.space_data
layout.prop(project_space, "show_region_ui")
layout.separator()
layout.menu("INFO_MT_area")
class PROJECT_MT_save_load(Menu):
bl_label = "Save & Load"
def draw(self, context):
layout = self.layout
prefs = context.preferences
layout.prop(prefs, "use_project_auto_save", text="Auto-Save Project")
layout.operator("project.save_project", text="Save Project")
class PROJECT_MT_add_variable(Menu):
bl_label = "Add Variable"
def draw(self, context):
layout = self.layout
layout.operator("project.add_variable", text="String", icon='NONE').variable_type = 'STRING'
layout.operator("project.add_variable", text="Filepath", icon='NONE').variable_type = 'FILEPATH'
layout.separator()
layout.operator("project.add_variable", text="Integer", icon='NONE').variable_type = 'INTEGER'
layout.operator("project.add_variable", text="Float", icon='NONE').variable_type = 'FLOAT'
# -------------------------------------------------------------
# Execution Area
#
# Shown when header is hidden.
class PROJECT_PT_save_project(Panel):
bl_label = "Save Project"
bl_space_type = 'PROJECT'
bl_region_type = 'EXECUTE'
bl_options = {'HIDE_HEADER'}
def draw(self, context):
layout = self.layout.row()
layout.operator_context = 'EXEC_AREA'
layout.menu("PROJECT_MT_save_load", text="", icon='COLLAPSEMENU')
# Save button.
if not context.preferences.use_project_auto_save and bpy.data.project is not None:
# Show '*' to let users know the project has been modified.
# It is shown to the left so that it is visible when the sidebar is narrow,
# and for consistency with unsaved files in the title bar.
layout.operator(
"project.save_project",
text=("* " if bpy.data.project.is_dirty else "") + iface_("Save Project"),
icon='FILE_TICK',
translate=False,
)
# -------------------------------------------------------------
# Navigation Bar
class PROJECT_PT_navigation_bar(Panel):
bl_label = "Project Navigation"
bl_space_type = 'PROJECT'
bl_region_type = 'UI'
bl_category = "Navigation"
bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
return True
def draw(self, context):
layout = self.layout
space_data = context.space_data
col = layout.column()
if bpy.data.project is None:
# If there's no project, we need to make sure the UI for creating a
# new project is visible. That UI is in the main section, so we
# ensure it's the active section.
space_data.active_section = MAIN_SECTION_NAME
col.enabled = False
col.scale_x = 1.3
col.scale_y = 1.3
col.prop(space_data, "active_section", expand=True)
# -------------------------------------------------------------
# Main Area
class PROJECT_PT_main(Panel, CenterAlignMixIn):
bl_label = "Project"
bl_translation_context = i18n_contexts.editor_preferences
bl_space_type = 'PROJECT'
bl_region_type = 'WINDOW'
bl_category = MAIN_SECTION_NAME
@classmethod
def poll(cls, context):
return bpy.data.project is not None
def draw_centered(self, context, layout):
project = bpy.data.project
col = layout.column()
col.prop(project, "name")
col.prop(project, "root_path")
class PROJECT_PT_main_unset(Panel, CenterAlignMixIn):
bl_label = "No Project"
bl_space_type = 'PROJECT'
bl_region_type = 'WINDOW'
bl_options = {'HIDE_HEADER'}
bl_category = MAIN_SECTION_NAME
@classmethod
def poll(cls, context):
return not PROJECT_PT_main.poll(context)
def draw_centered(self, context, layout):
col = layout.column()
col.separator(factor=2.0)
if context.blend_data.filepath == "":
row = col.row()
row.alignment = 'CENTER'
row.label(
text="No active project.",
icon='STATUS_INFO',
)
col.separator()
row = col.row()
row.label_multiline(
text="Save the current file, and make sure to place it in a folder that will be part of the project",
alignment='CENTER')
row = col.row()
row.label_multiline(
text="Alternatively, open a file inside of a project directory to see its settings.",
alignment='CENTER')
col.separator()
row = col.row()
row.alignment = 'CENTER'
row.operator("wm.save_as_mainfile", text="Save File...", icon='FILE_TICK')
row.operator("project.open_blend_in_project", icon='FILE_FOLDER')
else:
row = col.row()
row.alignment = 'CENTER'
row.label(
text="No active project.",
icon='STATUS_INFO',
)
col.separator()
row = col.row()
row.label_multiline(
text="Set up a new project by choosing any parent directory of the current file.",
alignment='CENTER')
row = col.row()
row.label_multiline(
text="Alternatively, open a file inside of a project directory to see its settings.",
alignment='CENTER')
col.separator()
row = col.row()
row.alignment = 'CENTER'
row.operator("project.new_project", text="New Project...", icon='ADD')
row.operator("project.open_blend_in_project", icon='FILE_FOLDER')
class PROJECT_UL_variables(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
col = layout.column()
col.prop(item, "name", text="", expand=False, emboss=False)
col = layout.column()
col.alignment = 'RIGHT'
col.active = False
match item.type:
case 'INTEGER':
col.label(text=str(item.value))
case 'FLOAT':
col.label(text="{:.3f}".format(item.value))
case 'STRING':
col.label(text=str(item.value))
class PROJECT_PT_variables(Panel):
bl_label = "Variables"
bl_space_type = 'PROJECT'
bl_region_type = 'WINDOW'
bl_category = "Variables"
def draw(self, context):
project = bpy.data.project
layout = self.layout
row = layout.row()
row.template_list(
listtype_name="PROJECT_UL_variables",
list_id="Variables",
dataptr=project,
propname="variables",
item_dyntip_propname="description",
active_dataptr=project,
active_propname="active_variable_index",
)
col = row.column(align=True)
col.menu("PROJECT_MT_add_variable", text="", icon='ADD')
col.operator("project.remove_variable", text="", icon='REMOVE')
col.separator()
col.operator("project.move_variable", text="", icon='TRIA_UP').direction = 'UP'
col.operator("project.move_variable", text="", icon='TRIA_DOWN').direction = 'DOWN'
col = layout.column()
col.use_property_split = True
col.alignment = 'LEFT'
col.separator(factor=1)
if project.active_variable_index >= 0 and project.active_variable_index < len(project.variables):
var = project.variables[project.active_variable_index]
col.prop(var, "type")
if var.type == 'STRING':
col.prop(var, "subtype")
col.prop(var, "name")
col.prop(var, "value")
col.prop(var, "description")
# -------------------------------------------------------------
# Register
classes = (
PROJECT_HT_header,
PROJECT_MT_editor_menus,
PROJECT_MT_view,
PROJECT_MT_save_load,
PROJECT_MT_add_variable,
PROJECT_PT_navigation_bar,
PROJECT_PT_save_project,
PROJECT_PT_main_unset,
PROJECT_PT_main,
PROJECT_PT_variables,
PROJECT_UL_variables,
)