From 4c2ea47aec43298831885058445215bf25b0ec5e Mon Sep 17 00:00:00 2001 From: Sean Kim Date: Thu, 11 Sep 2025 23:53:43 +0200 Subject: [PATCH] UI: Add 'Developer Tools' section to user preferences This PR introduces a 'Developer Tools' section of the user preferences, enabled when the `Developer Extras` option is enabled. This menu will not be hidden when the release cycle is no longer in alpha, allowing developers to use this for common debug options instead of having to use `G.debug_value` and arbitrary values. This has the benefit of allowing for an ease of debugging in non-alpha builds as well as being able to use multiple options that would have been considered exclusive debug options at once. None of the existing values bound to a specific `G.debug_value` have been migrated yet. Pull Request: https://projects.blender.org/blender/blender/pulls/145215 --- scripts/startup/bl_ui/space_userpref.py | 215 +++++++++--------- source/blender/makesdna/DNA_userdef_types.h | 1 + source/blender/makesrna/intern/rna_userdef.cc | 10 +- 3 files changed, 122 insertions(+), 104 deletions(-) diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index ffed0378d41..697a6b03990 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -2830,114 +2830,45 @@ class USERPREF_PT_studiolight_light_editor(StudioLightPanel, Panel): # ----------------------------------------------------------------------------- # Experimental Panels -class ExperimentalPanel: +# Also used for "Developer Tools" which are stored in `preferences.experimental` too. +def _draw_experimental_items(layout, preferences, items, url_prefix="https://projects.blender.org"): + experimental = preferences.experimental + + layout.use_property_split = False + layout.use_property_decorate = False + + for prop_keywords, reference in items: + split = layout.split(factor=0.66) + col = split.split() + col.prop(experimental, **prop_keywords) + + if reference: + if type(reference) is tuple: + url_ext = reference[0] + text = reference[1] + else: + url_ext = reference + text = reference + + col = split.split() + col.operator("wm.url_open", text=text, icon='URL').url = url_prefix + url_ext + + +class USERPREF_PT_developer_tools(Panel): bl_space_type = 'PREFERENCES' bl_region_type = 'WINDOW' - bl_context = "experimental" - - url_prefix = "https://projects.blender.org/" + bl_context = "developer_tools" + bl_label = "Debug" @classmethod - def poll(cls, _context): - return bpy.app.version_cycle == "alpha" - - def _draw_items(self, context, items): - prefs = context.preferences - experimental = prefs.experimental - - layout = self.layout - layout.use_property_split = False - layout.use_property_decorate = False - - for prop_keywords, reference in items: - split = layout.split(factor=0.66) - col = split.split() - col.prop(experimental, **prop_keywords) - - if reference: - if type(reference) is tuple: - url_ext = reference[0] - text = reference[1] - else: - url_ext = reference - text = reference - - col = split.split() - col.operator("wm.url_open", text=text, icon='URL').url = self.url_prefix + url_ext - - -""" -# Example panel, leave it here so we always have a template to follow even -# after the features are gone from the experimental panel. - -class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): - bl_label = "Virtual Reality" + def poll(cls, context): + return context.preferences.view.show_developer_ui def draw(self, context): - self._draw_items( - context, ( - ({"property": "use_virtual_reality_scene_inspection"}, ("blender/blender/issues/71347", "#71347")), - ({"property": "use_virtual_reality_immersive_drawing"}, ("blender/blender/issues/71348", "#71348")), - ), - ) -""" - - -class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): - bl_label = "New Features" - - def draw(self, context): - self._draw_items( - context, ( - ({"property": "use_extended_asset_browser"}, - ("blender/blender/projects/10", "Pipeline, Assets & IO Project Page")), - ({"property": "use_new_volume_nodes"}, ("blender/blender/issues/103248", "#103248")), - ({"property": "use_shader_node_previews"}, ("blender/blender/issues/110353", "#110353")), - ({"property": "use_geometry_nodes_lists"}, ("blender/blender/issues/140918", "#140918")), - ), - ) - - -class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): - bl_label = "Prototypes" - - def draw(self, context): - self._draw_items( - context, ( - ({"property": "use_new_curves_tools"}, ("blender/blender/issues/68981", "#68981")), - ({"property": "use_sculpt_texture_paint"}, ("blender/blender/issues/96225", "#96225")), - ({"property": "write_legacy_blend_file_format"}, ("/blender/blender/issues/129309", "#129309")), - ), - ) - - -# Keep this as tweaks can be useful to restore. -""" -class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel): - bl_label = "Tweaks" - - def draw(self, context): - self._draw_items( - context, ( - ({"property": "use_select_nearest_on_first_click"}, ("blender/blender/issues/96752", "#96752")), - ), - ) - -""" - - -class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): - bl_label = "Debugging" - - @classmethod - def poll(cls, _context): - # Unlike the other experimental panels, the debugging one is always visible - # even in beta or release. - return True - - def draw(self, context): - self._draw_items( - context, ( + _draw_experimental_items( + self.layout, + context.preferences, + ( ({"property": "use_undo_legacy"}, ("blender/blender/issues/60695", "#60695")), ({"property": "override_auto_resync"}, ("blender/blender/issues/83811", "#83811")), ({"property": "use_all_linked_data_direct"}, None), @@ -2952,6 +2883,83 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): ) +class ExperimentalPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "experimental" + + @classmethod + def poll(cls, _context): + return bpy.app.version_cycle == "alpha" + + +""" +# Example panel, leave it here so we always have a template to follow even +# after the features are gone from the experimental panel. + +class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): + bl_label = "Virtual Reality" + + def draw(self, context): + _draw_experimental_items( + self.layout, + context.preferences, + ( + ({"property": "use_virtual_reality_scene_inspection"}, ("blender/blender/issues/71347", "#71347")), + ({"property": "use_virtual_reality_immersive_drawing"}, ("blender/blender/issues/71348", "#71348")), + ), + ) +""" + + +class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): + bl_label = "New Features" + + def draw(self, context): + _draw_experimental_items( + self.layout, + context.preferences, + ( + ({"property": "use_extended_asset_browser"}, + ("blender/blender/projects/10", "Pipeline, Assets & IO Project Page")), + ({"property": "use_new_volume_nodes"}, ("blender/blender/issues/103248", "#103248")), + ({"property": "use_shader_node_previews"}, ("blender/blender/issues/110353", "#110353")), + ({"property": "use_geometry_nodes_lists"}, ("blender/blender/issues/140918", "#140918")), + ), + ) + + +class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): + bl_label = "Prototypes" + + def draw(self, context): + _draw_experimental_items( + self.layout, + context.preferences, + ( + ({"property": "use_new_curves_tools"}, ("blender/blender/issues/68981", "#68981")), + ({"property": "use_sculpt_texture_paint"}, ("blender/blender/issues/96225", "#96225")), + ({"property": "write_legacy_blend_file_format"}, ("/blender/blender/issues/129309", "#129309")), + ), + ) + + +# Keep this as tweaks can be useful to restore. +""" +class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel): + bl_label = "Tweaks" + + def draw(self, context): + _draw_experimental_items( + self.layout, + context.preferences, + ( + ({"property": "use_select_nearest_on_first_click"}, ("blender/blender/issues/96752", "#96752")), + ), + ) + +""" + # ----------------------------------------------------------------------------- # Class Registration @@ -3067,7 +3075,8 @@ classes = ( USERPREF_PT_experimental_new_features, USERPREF_PT_experimental_prototypes, # USERPREF_PT_experimental_tweaks, - USERPREF_PT_experimental_debugging, + + USERPREF_PT_developer_tools, # UI lists USERPREF_UL_asset_libraries, diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 7bc18c437b2..d9e9c9d2d5f 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -659,6 +659,7 @@ typedef enum eUserPref_Section { USER_SECTION_FILE_PATHS = 15, USER_SECTION_EXPERIMENTAL = 16, USER_SECTION_EXTENSIONS = 17, + USER_SECTION_DEVELOPER_TOOLS = 18, } eUserPref_Section; /** #UserDef_SpaceData.flag (State of the user preferences UI). */ diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index 54e25302bad..1d3ecc65de2 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -67,6 +67,7 @@ const EnumPropertyItem rna_enum_preference_section_items[] = { {USER_SECTION_SAVE_LOAD, "SAVE_LOAD", 0, "Save & Load", ""}, {USER_SECTION_FILE_PATHS, "FILE_PATHS", 0, "File Paths", ""}, RNA_ENUM_ITEM_SEPR, + {USER_SECTION_DEVELOPER_TOOLS, "DEVELOPER_TOOLS", 0, "Developer Tools", ""}, {USER_SECTION_EXPERIMENTAL, "EXPERIMENTAL", 0, "Experimental", ""}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -191,6 +192,7 @@ static const EnumPropertyItem rna_enum_preferences_extension_repo_source_type_it # include "BKE_addon.h" # include "BKE_appdir.hh" # include "BKE_blender.hh" +# include "BKE_blender_version.h" # include "BKE_callbacks.hh" # include "BKE_global.hh" # include "BKE_idprop.hh" @@ -805,8 +807,9 @@ static const EnumPropertyItem *rna_UseDef_active_section_itemf(bContext * /*C*/, UserDef *userdef = static_cast(ptr->data); const bool use_developer_ui = (userdef->flag & USER_DEVELOPER_UI) != 0; + const bool is_alpha = BKE_blender_version_is_alpha(); - if (use_developer_ui) { + if (use_developer_ui && is_alpha) { *r_free = false; return rna_enum_preference_section_items; } @@ -818,6 +821,11 @@ static const EnumPropertyItem *rna_UseDef_active_section_itemf(bContext * /*C*/, it++) { if (it->value == USER_SECTION_EXPERIMENTAL) { + if (is_alpha == false) { + continue; + } + } + else if (it->value == USER_SECTION_DEVELOPER_TOOLS) { if (use_developer_ui == false) { continue; }