Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/datafiles/userdef/userdef_default.c5
-rw-r--r--release/scripts/modules/rna_user_menus_ui.py294
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py4
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py4
-rw-r--r--release/scripts/startup/bl_operators/presets.py1
-rw-r--r--release/scripts/startup/bl_operators/userpref.py247
-rw-r--r--release/scripts/startup/bl_operators/wm.py31
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py33
-rw-r--r--source/blender/blenkernel/BKE_blender_user_menu.h9
-rw-r--r--source/blender/blenkernel/BKE_context.h1
-rw-r--r--source/blender/blenkernel/intern/blender.c12
-rw-r--r--source/blender/blenkernel/intern/blender_user_menu.c114
-rw-r--r--source/blender/blenkernel/intern/blendfile.c8
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenloader/intern/readfile.c53
-rw-r--r--source/blender/blenloader/intern/writefile.c62
-rw-r--r--source/blender/editors/include/ED_screen.h19
-rw-r--r--source/blender/editors/include/UI_interface.h1
-rw-r--r--source/blender/editors/interface/interface_context_menu.c7
-rw-r--r--source/blender/editors/interface/interface_templates.c77
-rw-r--r--source/blender/editors/screen/screen_user_menu.c277
-rw-r--r--source/blender/editors/space_userpref/space_userpref.c1
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h56
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_define.c2
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c6
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c937
-rw-r--r--source/blender/windowmanager/intern/wm_files.c2
m---------source/tools0
29 files changed, 2107 insertions, 162 deletions
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c
index df1be29f642..6dde3f3e6fa 100644
--- a/release/datafiles/userdef/userdef_default.c
+++ b/release/datafiles/userdef/userdef_default.c
@@ -105,6 +105,7 @@ const UserDef U_default = {
.autoexec_paths = {NULL},
.user_menus = {NULL},
+ .user_menus_group = {NULL},
.keyconfigstr = "blender",
.undosteps = 32,
@@ -234,5 +235,9 @@ const UserDef U_default = {
.runtime =
{
.is_dirty = 0,
+
+ .um_space_select = 1,
+ .um_context_select = 0,
+ .um_item_select = NULL,
},
};
diff --git a/release/scripts/modules/rna_user_menus_ui.py b/release/scripts/modules/rna_user_menus_ui.py
new file mode 100644
index 00000000000..7b058d0c5c5
--- /dev/null
+++ b/release/scripts/modules/rna_user_menus_ui.py
@@ -0,0 +1,294 @@
+# ##### 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>
+
+__all__ = (
+ "draw_user_menus",
+)
+
+import bpy
+from bpy.app.translations import pgettext_iface as iface_
+from bpy.app.translations import contexts as i18n_contexts
+
+def _indented_layout(layout, level):
+ indentpx = 16
+ if level == 0:
+ level = 0.0001 # Tweak so that a percentage of 0 won't split by half
+ indent = level * indentpx / bpy.context.region.width
+
+ split = layout.split(factor=indent)
+ col = split.column()
+ col = split.column()
+ return col
+
+def get_keymap(context, idname, ensure):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ if not idname:
+ idname = um.active_group.idname
+
+ for km in context.window_manager.keyconfigs.user.keymaps:
+ for kmi in km.keymap_items:
+ if kmi.idname == "wm.call_user_menu":
+ if kmi.properties.name == idname:
+ return kmi
+ if ensure:
+ km = context.window_manager.keyconfigs.user.keymaps['Window']
+ kmi = km.keymap_items.new("wm.call_user_menu",'NONE', 'ANY', shift=False, ctrl=False, alt=False)
+ kmi.properties.idname = idname
+ kmi.active = True
+ return kmi
+
+def draw_button(context, box, item, index):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ name = item.name
+ if name == "":
+ name = " "
+ item.is_selected = item == um.active_item
+ col = box.column(align=True)
+ row = col.row(align=True)
+ if item.type == "SEPARATOR":
+ name = "___________"
+ #icons = bpy.types.UILayout.bl_rna.functions["prop"].parameters["icon"].enum_items.keys()
+ #selected_icon = icons[item.icon]
+ row.prop(item, "is_selected", icon=item.icon, text=name, toggle=1)
+ if item.type == "SUBMENU":
+ sm = item.get_submenu()
+ if um.active_group.type == "PIE" and index >= 0:
+ row.operator("preferences.pie_menuitem_add", text="", icon='ADD').index = index
+ row.operator("preferences.menuitem_remove", text="", icon='REMOVE')
+ row.operator("preferences.menuitem_up", text="", icon='TRIA_UP')
+ row.operator("preferences.menuitem_down", text="", icon='TRIA_DOWN')
+ sub_box = col.box()
+ sub_box = sub_box.column(align=True)
+ draw_item(context, sub_box, sm.items_list)
+
+def draw_item(context, box, items):
+ for umi in items:
+ draw_button(context, box, umi, -1)
+
+def draw_item_box(context, row):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ box_line = row.box()
+ box_col = box_line.column(align=True)
+
+ has_item = um.has_item()
+ if not has_item:
+ box_col.label(text="none")
+ else:
+ draw_item(context, box_col, um.get_current_menu().menu_items)
+
+ row = row.split(factor=0.9, align=True)
+ col = row.column(align=True)
+
+ col.operator("preferences.menuitem_add", text="", icon='ADD').index = -1
+ col.operator("preferences.menuitem_remove", text="", icon='REMOVE')
+ col.operator("preferences.menuitem_up", text="", icon='TRIA_UP')
+ col.operator("preferences.menuitem_down", text="", icon='TRIA_DOWN')
+ row.separator()
+
+def draw_pie_item(context, col, items, label, index):
+ row = col.row()
+ row.label(text=label)
+ draw_button(context, row, items, index)
+
+def draw_pie(context, row):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ cm = um.get_current_menu()
+ if not cm:
+ return
+ col = row.column()
+ draw_pie_item(context, col, cm.menu_items[0], "Left : ", 0)
+ draw_pie_item(context, col, cm.menu_items[1], "Right : ", 1)
+ draw_pie_item(context, col, cm.menu_items[2], "Down : ", 2)
+ draw_pie_item(context, col, cm.menu_items[3], "Up : ", 3)
+ draw_pie_item(context, col, cm.menu_items[4], "Upper left : ", 4)
+ draw_pie_item(context, col, cm.menu_items[5], "Upper right : ", 5)
+ draw_pie_item(context, col, cm.menu_items[6], "Lower left : ", 6)
+ draw_pie_item(context, col, cm.menu_items[7], "Lower right : ", 7)
+ row.separator()
+
+
+def draw_item_editor(context, row):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ col = row.column()
+
+ has_item = um.has_item()
+ current = um.active_item
+ if not has_item:
+ col.label(text="No item in this list.")
+ col.label(text="Add one or choose another list to get started")
+ elif current:
+ col.prop(current, "type")
+ if (current.type != "SEPARATOR"):
+ rowsub = col.row(align=True)
+ rowsub.prop(current, "icon", icon=current.icon, text="")
+ col.prop(current, "name")
+ if (current.type == "OPERATOR"):
+ umi_op = current.get_operator()
+ col.prop(umi_op, "operator")
+ box = col.box()
+ box.template_user_menu_item_properties(umi_op)
+ if (current.type == "MENU"):
+ umi_pm = current.get_menu()
+ col.prop(umi_pm, "id_name", text="ID name")
+ if (current.type == "PROPERTY"):
+ umi_prop = current.get_property()
+ col.prop(umi_prop, "id_name")
+ col.prop(umi_prop, "context")
+ else:
+ col.label(text="No item selected.")
+
+def draw_user_menu_preference_expanded(context, layout, kmi, map_type):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ box = layout.box()
+ sub = box.row()
+
+ if kmi:
+ if map_type not in {'TEXTINPUT', 'TIMER'}:
+ sub = box.column()
+ subrow = sub.row(align=True)
+
+ if map_type == 'KEYBOARD':
+ subrow.prop(kmi, "type", text="", event=True)
+ subrow.prop(kmi, "value", text="")
+ subrow_repeat = subrow.row(align=True)
+ subrow_repeat.active = kmi.value in {'ANY', 'PRESS'}
+ subrow_repeat.prop(kmi, "repeat", text="Repeat")
+ elif map_type in {'MOUSE', 'NDOF'}:
+ subrow.prop(kmi, "type", text="")
+ subrow.prop(kmi, "value", text="")
+
+ subrow = sub.row()
+ subrow.scale_x = 0.75
+ subrow.prop(kmi, "any", toggle=True)
+ subrow.prop(kmi, "shift", toggle=True)
+ subrow.prop(kmi, "ctrl", toggle=True)
+ subrow.prop(kmi, "alt", toggle=True)
+ subrow.prop(kmi, "oskey", text="Cmd", toggle=True)
+ subrow.prop(kmi, "key_modifier", text="", event=True)
+ else:
+ sub.label(text="No key set")
+
+def draw_user_menu_preference(context, layout):
+ prefs = context.preferences
+ um = prefs.user_menus
+ umg = um.active_group
+ kmi = get_keymap(context, None, False)
+ map_type = None
+
+ box = layout.box()
+ row = box.row()
+
+ row.prop(um, "expanded", text="", emboss=False)
+
+ qf = um.menus[0]
+ if umg != qf:
+ row.prop(umg, "name")
+ pie_text = "List"
+ if umg.type == "PIE":
+ pie_text = "Pie"
+ row.prop(umg, "type", text=pie_text, expand=True)
+ if kmi:
+ row.prop(kmi, "map_type", text="")
+ map_type = kmi.map_type
+ if map_type == 'KEYBOARD':
+ row.prop(kmi, "type", text="", full_event=True)
+ elif map_type == 'MOUSE':
+ row.prop(kmi, "type", text="", full_event=True)
+ elif map_type == 'NDOF':
+ row.prop(kmi, "type", text="", full_event=True)
+ elif map_type == 'TWEAK':
+ subrow = row.row()
+ subrow.prop(kmi, "type", text="")
+ subrow.prop(kmi, "value", text="")
+ elif map_type == 'TIMER':
+ row.prop(kmi, "type", text="")
+ else:
+ row.label()
+
+ if um.expanded:
+ draw_user_menu_preference_expanded(context=context, layout=box, kmi=kmi, map_type=map_type)
+
+
+def menu_id(context, umg):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ i = 0
+ for item in um.menus:
+ if item == umg:
+ return i
+ i = i + 1
+ return -1
+
+
+def draw_user_menus(context, layout):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ if not um.active_group:
+ um.active_group = um.menus[0]
+
+ split = layout.split(factor=0.4)
+
+ row = split.row()
+
+ rowsub = row.row(align=True)
+ rowsub.menu("USERPREF_MT_menu_select", text=um.active_group.name)
+ rowsub.operator("preferences.usermenus_add", text="", icon='ADD')
+ rowsub.operator("preferences.usermenus_remove", text="", icon='REMOVE')
+
+ rowsub = split.row(align=True)
+ rowsub.prop(um, "space_selected", text="")
+
+ rowsub = split.row(align=True)
+ rowsub.prop(um, "context_selected", text="")
+
+ #rowsub = split.row(align=True)
+ #rowsub.operator("preferences.keyconfig_import", text="", icon='IMPORT')
+ #rowsub.operator("preferences.keyconfig_export", text="", icon='EXPORT')
+
+ row = layout.row()
+ row.separator()
+
+ draw_user_menu_preference(context=context, layout=row)
+
+ row = layout.row()
+ row.separator()
+
+ if um.active_group.type == "PIE":
+ draw_pie(context=context, row=row)
+ else:
+ draw_item_box(context=context, row=row)
+ draw_item_editor(context=context, row=row)
+
+ row.separator()
+
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index c3c6e77067e..4831f5565ab 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -157,6 +157,8 @@ def op_menu(menu, kmi_args):
def op_menu_pie(menu, kmi_args):
return ("wm.call_menu_pie", kmi_args, {"properties": [("name", menu)]})
+def op_user_menu(menu, kmi_args):
+ return ("wm.call_user_menu", kmi_args, {"properties": [("name", menu)]})
def op_panel(menu, kmi_args, kmi_data=()):
return ("wm.call_panel", kmi_args, {"properties": [("name", menu), *kmi_data]})
@@ -407,7 +409,7 @@ def km_window(params):
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
# Quick menu and toolbar
- op_menu("SCREEN_MT_user_menu", {"type": 'Q', "value": 'PRESS'}),
+ op_user_menu("QUICK_FAVORITES", {"type": 'Q', "value": 'PRESS'}),
# Fast editor switching
*(
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index bb921565374..5d52eed15dd 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -67,6 +67,8 @@ def op_menu(menu, kmi_args):
def op_menu_pie(menu, kmi_args):
return ("wm.call_menu_pie", kmi_args, {"properties": [("name", menu)]})
+def op_user_menu(menu, kmi_args):
+ return ("wm.call_user_menu", kmi_args, {"properties": [("name", menu)]})
def op_panel(menu, kmi_args, kmi_data=()):
return ("wm.call_panel", kmi_args, {"properties": [("name", menu), *kmi_data]})
@@ -210,7 +212,7 @@ def km_window(params):
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
# Quick menu and toolbar
- op_menu("SCREEN_MT_user_menu", {"type": 'TAB', "value": 'PRESS', "shift": True}),
+ op_user_menu("QUICK_FAVORITES", {"type": 'Q', "value": 'PRESS'}),
# NDOF settings
op_panel("USERPREF_PT_ndof_settings", {"type": 'NDOF_BUTTON_MENU', "value": 'PRESS'}),
diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py
index 2ea93a1aee9..357d08959fc 100644
--- a/release/scripts/startup/bl_operators/presets.py
+++ b/release/scripts/startup/bl_operators/presets.py
@@ -602,7 +602,6 @@ class AddPresetOperator(AddPresetBase, Operator):
prefix, suffix = operator.split("_OT_", 1)
return os.path.join("operator", "%s.%s" % (prefix.lower(), suffix))
-
class WM_MT_operator_presets(Menu):
bl_label = "Operator Presets"
diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index ceef2df63ff..5f10f06f655 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -51,6 +51,8 @@ def module_filesystem_remove(path_base, module_name):
# This duplicates shutil.copytree from Python 3.8, with the new dirs_exist_ok
# argument that we need. Once we upgrade to 3.8 we can remove this.
+
+
def _preferences_copytree(entries, src, dst):
import os
import shutil
@@ -85,11 +87,13 @@ def _preferences_copytree(entries, src, dst):
raise Error(errors)
return dst
+
def preferences_copytree(src, dst):
import os
with os.scandir(src) as entries:
return _preferences_copytree(entries=entries, src=src, dst=dst)
+
class PREFERENCES_OT_keyconfig_activate(Operator):
bl_idname = "preferences.keyconfig_activate"
bl_label = "Activate Keyconfig"
@@ -241,7 +245,8 @@ class PREFERENCES_OT_keyconfig_import(Operator):
config_name = basename(self.filepath)
- path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", "keyconfig"), create=True)
+ path = bpy.utils.user_resource(
+ 'SCRIPTS', os.path.join("presets", "keyconfig"), create=True)
path = os.path.join(path, config_name)
try:
@@ -460,7 +465,8 @@ class PREFERENCES_OT_addon_enable(Operator):
err_str = traceback.format_exc()
print(err_str)
- mod = addon_utils.enable(self.module, default_set=True, handle_error=err_cb)
+ mod = addon_utils.enable(
+ self.module, default_set=True, handle_error=err_cb)
if mod:
info = addon_utils.module_bl_info(mod)
@@ -544,7 +550,8 @@ class PREFERENCES_OT_theme_install(Operator):
xmlfile = self.filepath
- path_themes = bpy.utils.user_resource('SCRIPTS', "presets/interface_theme", create=True)
+ path_themes = bpy.utils.user_resource(
+ 'SCRIPTS', "presets/interface_theme", create=True)
if not path_themes:
self.report({'ERROR'}, "Failed to get themes path")
@@ -554,7 +561,8 @@ class PREFERENCES_OT_theme_install(Operator):
if not self.overwrite:
if os.path.exists(path_dest):
- self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
+ self.report(
+ {'WARNING'}, "File already installed to %r\n" % path_dest)
return {'CANCELLED'}
try:
@@ -638,7 +646,8 @@ class PREFERENCES_OT_addon_install(Operator):
if self.target == 'DEFAULT':
# don't use bpy.utils.script_paths("addons") because we may not be able to write to it.
- path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
+ path_addons = bpy.utils.user_resource(
+ 'SCRIPTS', "addons", create=True)
else:
path_addons = context.preferences.filepaths.script_directory
if path_addons:
@@ -661,7 +670,8 @@ class PREFERENCES_OT_addon_install(Operator):
pyfile_dir = os.path.dirname(pyfile)
for addon_path in addon_utils.paths():
if os.path.samefile(pyfile_dir, addon_path):
- self.report({'ERROR'}, "Source file is in the add-on search path: %r" % addon_path)
+ self.report(
+ {'ERROR'}, "Source file is in the add-on search path: %r" % addon_path)
return {'CANCELLED'}
del addon_path
del pyfile_dir
@@ -684,7 +694,8 @@ class PREFERENCES_OT_addon_install(Operator):
for f in file_to_extract.namelist():
path_dest = os.path.join(path_addons, os.path.basename(f))
if os.path.exists(path_dest):
- self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
+ self.report(
+ {'WARNING'}, "File already installed to %r\n" % path_dest)
return {'CANCELLED'}
try: # extract the file to "addons"
@@ -699,7 +710,8 @@ class PREFERENCES_OT_addon_install(Operator):
if self.overwrite:
module_filesystem_remove(path_addons, os.path.basename(pyfile))
elif os.path.exists(path_dest):
- self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
+ self.report(
+ {'WARNING'}, "File already installed to %r\n" % path_dest)
return {'CANCELLED'}
# if not compressed file just copy into the addon path
@@ -709,7 +721,8 @@ class PREFERENCES_OT_addon_install(Operator):
traceback.print_exc()
return {'CANCELLED'}
- addons_new = {mod.__name__ for mod in addon_utils.modules()} - addons_old
+ addons_new = {mod.__name__ for mod in addon_utils.modules()} - \
+ addons_old
addons_new.discard("modules")
# disable any addons we may have enabled previously and removed.
@@ -779,7 +792,8 @@ class PREFERENCES_OT_addon_remove(Operator):
path, isdir = PREFERENCES_OT_addon_remove.path_from_addon(self.module)
if path is None:
- self.report({'WARNING'}, "Add-on path %r could not be found" % path)
+ self.report(
+ {'WARNING'}, "Add-on path %r could not be found" % path)
return {'CANCELLED'}
# in case its enabled
@@ -925,9 +939,11 @@ class PREFERENCES_OT_app_template_install(Operator):
module_filesystem_remove(path_app_templates, f)
else:
for f in file_to_extract.namelist():
- path_dest = os.path.join(path_app_templates, os.path.basename(f))
+ path_dest = os.path.join(
+ path_app_templates, os.path.basename(f))
if os.path.exists(path_dest):
- self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
+ self.report(
+ {'WARNING'}, "File already installed to %r\n" % path_dest)
return {'CANCELLED'}
try: # extract the file to "bl_app_templates_user"
@@ -941,7 +957,8 @@ class PREFERENCES_OT_app_template_install(Operator):
self.report({'WARNING'}, "Expected a zip-file %r\n" % filepath)
return {'CANCELLED'}
- app_templates_new = set(os.listdir(path_app_templates)) - app_templates_old
+ app_templates_new = set(os.listdir(
+ path_app_templates)) - app_templates_old
# in case a new module path was created to install this addon.
bpy.utils.refresh_script_paths()
@@ -1001,14 +1018,17 @@ class PREFERENCES_OT_studiolight_install(Operator):
prefs = context.preferences
path_studiolights = os.path.join("studiolights", self.type.lower())
- path_studiolights = bpy.utils.user_resource('DATAFILES', path_studiolights, create=True)
+ path_studiolights = bpy.utils.user_resource(
+ 'DATAFILES', path_studiolights, create=True)
if not path_studiolights:
self.report({'ERROR'}, "Failed to create Studio Light path")
return {'CANCELLED'}
for e in self.files:
- shutil.copy(os.path.join(self.directory, e.name), path_studiolights)
- prefs.studio_lights.load(os.path.join(path_studiolights, e.name), self.type)
+ shutil.copy(os.path.join(self.directory, e.name),
+ path_studiolights)
+ prefs.studio_lights.load(os.path.join(
+ path_studiolights, e.name), self.type)
# print message
msg = (
@@ -1047,7 +1067,8 @@ class PREFERENCES_OT_studiolight_new(Operator):
wm = context.window_manager
filename = bpy.path.ensure_ext(self.filename, ".sl")
- path_studiolights = bpy.utils.user_resource('DATAFILES', os.path.join("studiolights", "studio"), create=True)
+ path_studiolights = bpy.utils.user_resource(
+ 'DATAFILES', os.path.join("studiolights", "studio"), create=True)
if not path_studiolights:
self.report({'ERROR'}, "Failed to get Studio Light path")
return {'CANCELLED'}
@@ -1060,7 +1081,8 @@ class PREFERENCES_OT_studiolight_new(Operator):
else:
for studio_light in prefs.studio_lights:
if studio_light.name == filename:
- bpy.ops.preferences.studiolight_uninstall(index=studio_light.index)
+ bpy.ops.preferences.studiolight_uninstall(
+ index=studio_light.index)
prefs.studio_lights.new(path=filepath_final)
@@ -1076,7 +1098,8 @@ class PREFERENCES_OT_studiolight_new(Operator):
def draw(self, _context):
layout = self.layout
if self.ask_overide:
- layout.label(text="Warning, file already exists. Overwrite existing file?")
+ layout.label(
+ text="Warning, file already exists. Overwrite existing file?")
else:
layout.prop(self, "filename")
@@ -1141,6 +1164,184 @@ class PREFERENCES_OT_studiolight_show(Operator):
bpy.ops.screen.userpref_show('INVOKE_DEFAULT')
return {'FINISHED'}
+# -----------------------------------------------------------------------------
+# User Menus Operators
+
+class PREFERENCES_OT_usermenus_select(Operator):
+ bl_idname = "preferences.usermenus_select"
+ bl_label = "select user menu to edit"
+
+ index: IntProperty()
+
+ def execute(self, _context):
+ prefs = _context.preferences
+ um = prefs.user_menus
+
+ i = 0
+ for umg in um.menus:
+ if i == self.index:
+ um.set_group(new_group=umg)
+ return {'FINISHED'}
+ i = i + 1
+ return {'CANCELLED'}
+
+class PREFERENCES_OT_usermenus_add(Operator):
+ bl_idname = "preferences.usermenus_add"
+ bl_label = "add an user menus group"
+
+ def execute(self, _context):
+ prefs = _context.preferences
+ um = prefs.user_menus
+ um.add_group()
+ return {'FINISHED'}
+
+class PREFERENCES_OT_usermenus_remove(Operator):
+ bl_idname = "preferences.usermenus_remove"
+ bl_label = "remove an user menus group"
+
+ @classmethod
+ def poll(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ if um.active_group.idname == "QUICK_FAVORITES":
+ return False
+ return True
+
+ def execute(self, _context):
+ prefs = _context.preferences
+ um = prefs.user_menus
+ um.remove_group()
+ return {'FINISHED'}
+
+class PREFERENCES_OT_menuitem_add(Operator):
+ """Add user menu item"""
+ bl_idname = "preferences.menuitem_add"
+ bl_label = "Add User Menu Item"
+
+ index: IntProperty()
+
+ def execute(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ um.item_add(index=self.index)
+ context.preferences.is_dirty = True
+ return {'FINISHED'}
+
+
+class PREFERENCES_OT_menuitem_remove(Operator):
+ """Remove user menu item"""
+ bl_idname = "preferences.menuitem_remove"
+ bl_label = "Remove User Menu Item"
+
+ @classmethod
+ def poll(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ can_remove = um.active_item
+ um_type = um.active_group.type
+
+ if um_type == "PIE" and can_remove:
+ if not can_remove.parent:
+ return False
+ return can_remove
+
+ def execute(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ um.item_remove()
+ context.preferences.is_dirty = True
+ return {'FINISHED'}
+
+
+class PREFERENCES_OT_menuitem_up(Operator):
+ """move up an user menu item"""
+ bl_idname = "preferences.menuitem_up"
+ bl_label = "Move Up An User Menu Item"
+
+ @classmethod
+ def poll(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ current = um.active_item
+ um_type = um.active_group.type
+ if not current:
+ return False
+
+ if um_type == "PIE":
+ if not current.parent:
+ return False
+ prev_item = current.prev
+ if prev_item:
+ return True
+ if current.parent.item.parent:
+ return True
+ return False
+ else:
+ prev_item = current.prev
+ if prev_item or current.parent:
+ return True
+ return False
+
+ def execute(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ um.item_move(up=True)
+ context.preferences.is_dirty = True
+ return {'FINISHED'}
+
+
+class PREFERENCES_OT_menuitem_down(Operator):
+ """move up an user menu item"""
+ bl_idname = "preferences.menuitem_down"
+ bl_label = "Move Up An User Menu Item"
+
+ @classmethod
+ def poll(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ um_type = um.active_group.type
+ current = um.active_item
+ if not current:
+ return False
+
+ if um_type == "PIE":
+ if not current.parent:
+ return False
+ next_item = current.next
+ if next_item:
+ return True
+ if current.parent.item.parent:
+ return True
+ return False
+ else:
+ next_item = current.next
+ if next_item or current.parent:
+ return True
+ return False
+
+ def execute(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ um.item_move(up=False)
+ context.preferences.is_dirty = True
+ return {'FINISHED'}
+
+class PREFERENCES_OT_pie_menuitem_add(Operator):
+ """Add user menu item"""
+ bl_idname = "preferences.pie_menuitem_add"
+ bl_label = "Add User Menu Item"
+
+ index: IntProperty()
+
+ def execute(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+
+ um.pie_item_add(index=self.index)
+ context.preferences.is_dirty = True
+ return {'FINISHED'}
classes = (
PREFERENCES_OT_addon_disable,
@@ -1167,4 +1368,12 @@ classes = (
PREFERENCES_OT_studiolight_uninstall,
PREFERENCES_OT_studiolight_copy_settings,
PREFERENCES_OT_studiolight_show,
+ PREFERENCES_OT_usermenus_select,
+ PREFERENCES_OT_usermenus_add,
+ PREFERENCES_OT_usermenus_remove,
+ PREFERENCES_OT_menuitem_add,
+ PREFERENCES_OT_menuitem_remove,
+ PREFERENCES_OT_menuitem_up,
+ PREFERENCES_OT_menuitem_down,
+ PREFERENCES_OT_pie_menuitem_add,
)
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 59abff12834..6c8d94c874c 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -2633,6 +2633,36 @@ class WM_OT_drop_blend_file(Operator):
col.operator("wm.link", text="Link...", icon='LINK_BLEND').filepath = self.filepath
col.operator("wm.append", text="Append...", icon='APPEND_BLEND').filepath = self.filepath
+class WM_OT_call_user_menu(Operator):
+ bl_idname = "wm.call_user_menu"
+ bl_label = "display user menu"
+
+ name: StringProperty()
+
+ def execute(self, context):
+ return {'FINISHED'}
+
+ def draw_menu(self, menu, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ umg = um.get_group(idname=self.name)
+
+ layout = menu.layout
+ if umg.type == "PIE":
+ layout = layout.menu_pie()
+ um.draw_menu(context=context, layout=layout, menu=umg)
+
+ def invoke(self, context, event):
+ prefs = context.preferences
+ um = prefs.user_menus
+ wm = context.window_manager
+ umg = um.get_group(idname=self.name)
+
+ if umg.type == "PIE":
+ wm.popup_menu_pie(draw_func=self.draw_menu, title=umg.name, event=event)
+ else:
+ wm.popup_menu(self.draw_menu, title=umg.name)
+ return {'FINISHED'}
classes = (
WM_OT_context_collection_boolean_set,
@@ -2677,4 +2707,5 @@ classes = (
WM_OT_batch_rename,
WM_MT_splash,
WM_MT_splash_about,
+ WM_OT_call_user_menu,
)
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 9548de20752..f015a8de295 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -1684,8 +1684,37 @@ class USERPREF_PT_keymap(KeymapPanel, Panel):
# Keymap Settings
draw_keymaps(context, layout)
- # print("runtime", time.time() - start)
+# -----------------------------------------------------------------------------
+# Custom Menu Editor Panels
+
+class UserMenusPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "user_menus"
+
+class USERPREF_MT_menu_select(Menu):
+ bl_label = "Menus group select"
+
+ def draw(self, context):
+ prefs = context.preferences
+ um = prefs.user_menus
+ layout = self.layout
+ index = 0
+ for umg in um.menus:
+ layout.operator("preferences.usermenus_select", text=umg.name).index = index
+ index = index + 1
+
+
+class USERPREF_PT_user_menus(UserMenusPanel, Panel):
+ bl_label = "user_menus"
+ bl_options = {'HIDE_HEADER'}
+
+ def draw(self, context):
+ from rna_user_menus_ui import draw_user_menus
+
+ layout = self.layout
+ draw_user_menus(context, layout)
# -----------------------------------------------------------------------------
# Add-On Panels
@@ -2278,6 +2307,8 @@ classes = (
USERPREF_PT_navigation_fly_walk_gravity,
USERPREF_PT_keymap,
+ USERPREF_MT_menu_select,
+ USERPREF_PT_user_menus,
USERPREF_PT_addons,
USERPREF_PT_studiolight_lights,
diff --git a/source/blender/blenkernel/BKE_blender_user_menu.h b/source/blender/blenkernel/BKE_blender_user_menu.h
index 8d00cde488e..dd42d45fd67 100644
--- a/source/blender/blenkernel/BKE_blender_user_menu.h
+++ b/source/blender/blenkernel/BKE_blender_user_menu.h
@@ -27,7 +27,15 @@ extern "C" {
struct ListBase;
struct bUserMenu;
struct bUserMenuItem;
+struct wmWindowManager;
+void BKE_blender_user_menu_free_list(struct ListBase *lb);
+void BKE_blender_user_menus_group_idname_update(struct bUserMenusGroup *umg);
+void BKE_blender_user_menus_group_idname_update_keymap(struct wmWindowManager *wm,
+ const char *old,
+ const char *new);
+struct bUserMenusGroup *BKE_blender_user_menus_group_new(const char *name);
+struct bUserMenusGroup *BKE_blender_user_menus_group_find(struct ListBase *lb, const char *idname);
struct bUserMenu *BKE_blender_user_menu_find(struct ListBase *lb,
char space_type,
const char *context);
@@ -38,6 +46,7 @@ struct bUserMenu *BKE_blender_user_menu_ensure(struct ListBase *lb,
struct bUserMenuItem *BKE_blender_user_menu_item_add(struct ListBase *lb, int type);
void BKE_blender_user_menu_item_free(struct bUserMenuItem *umi);
void BKE_blender_user_menu_item_free_list(struct ListBase *lb);
+struct bUserMenusGroup *BKU_blender_user_menu_default(void);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index eb26f1c3969..355b69b73ef 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -255,6 +255,7 @@ struct ViewLayer *CTX_data_view_layer(const bContext *C);
struct RenderEngineType *CTX_data_engine_type(const bContext *C);
struct ToolSettings *CTX_data_tool_settings(const bContext *C);
+const char **CTX_data_list_mode_string(void);
const char *CTX_data_mode_string(const bContext *C);
enum eContextObjectMode CTX_data_mode_enum_ex(const struct Object *obedit,
const struct Object *ob,
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index 1d5c8f76cc5..8a6d30834eb 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -238,10 +238,14 @@ static void userdef_free_keyconfig_prefs(UserDef *userdef)
static void userdef_free_user_menus(UserDef *userdef)
{
- for (bUserMenu *um = userdef->user_menus.first, *um_next; um; um = um_next) {
- um_next = um->next;
- BKE_blender_user_menu_item_free_list(&um->items);
- MEM_freeN(um);
+ for (bUserMenusGroup *umg = userdef->user_menus_group.first, *umg_next; umg; umg = umg_next) {
+ umg_next = umg->next;
+ for (bUserMenu *um = umg->menus.first, *um_next; um; um = um_next) {
+ um_next = um->next;
+ BKE_blender_user_menu_item_free_list(&um->items);
+ MEM_freeN(um);
+ }
+ MEM_freeN(umg);
}
}
diff --git a/source/blender/blenkernel/intern/blender_user_menu.c b/source/blender/blenkernel/intern/blender_user_menu.c
index edd89357fd5..17251e2c71d 100644
--- a/source/blender/blenkernel/intern/blender_user_menu.c
+++ b/source/blender/blenkernel/intern/blender_user_menu.c
@@ -26,13 +26,98 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+#include "BLI_string_utils.h"
#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "RNA_access.h"
#include "BKE_blender_user_menu.h"
#include "BKE_idprop.h"
/* -------------------------------------------------------------------- */
+/** \name Menu group
+ * \{ */
+
+void BKE_blender_user_menu_free_list(ListBase *lb)
+{
+ for (bUserMenu *um = lb->first, *um_next; um; um = um_next) {
+ um_next = um->next;
+ BKE_blender_user_menu_item_free_list(&um->items);
+ MEM_freeN(um);
+ }
+ BLI_listbase_clear(lb);
+}
+
+bUserMenusGroup *BKE_blender_user_menus_group_find(ListBase *lb, const char *idname)
+{
+ LISTBASE_FOREACH (bUserMenusGroup *, umg, lb) {
+ if ((STREQ(idname, umg->idname))) {
+ return umg;
+ }
+ }
+ return NULL;
+}
+
+void BKE_blender_user_menus_group_idname_update(bUserMenusGroup *umg)
+{
+ char name[64];
+
+ STRNCPY(name, umg->name);
+ for (int i = 0; name[i]; i++) {
+ if (name[i] == ' ')
+ name[i] = '_';
+ if (name[i] >= 'a' && name[i] <= 'z')
+ name[i] += 'A' - 'a';
+ }
+ STRNCPY(umg->idname, name);
+ BLI_uniquename(&U.user_menus_group,
+ umg,
+ umg->idname,
+ '_',
+ offsetof(bUserMenusGroup, idname),
+ sizeof(umg->idname));
+}
+
+void BKE_blender_user_menus_group_idname_update_keymap(wmWindowManager *wm,
+ const char *old,
+ const char *new)
+{
+ wmKeyConfig *kc;
+ wmKeyMap *km;
+
+ for (kc = wm->keyconfigs.first; kc; kc = kc->next) {
+ for (km = kc->keymaps.first; km; km = km->next) {
+ wmKeyMapItem *kmi;
+ for (kmi = km->items.first; kmi; kmi = kmi->next) {
+ if (STREQ(kmi->idname, "WM_OT_call_user_menu")) {
+ IDProperty *idp = IDP_GetPropertyFromGroup(kmi->properties, "name");
+ char *index = IDP_String(idp);
+ if (STREQ(index, old)) {
+ IDP_AssignString(idp, new, 64);
+ }
+ }
+ }
+ }
+ }
+}
+
+bUserMenusGroup *BKE_blender_user_menus_group_new(const char *name)
+{
+ bUserMenusGroup *umg = MEM_mallocN(sizeof(*umg), __func__);
+ STRNCPY(umg->name, name);
+ umg->type = 0;
+ umg->prev = NULL;
+ umg->next = NULL;
+ BLI_listbase_clear(&umg->menus);
+ BKE_blender_user_menus_group_idname_update(umg);
+ return umg;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Menu Type
* \{ */
@@ -50,8 +135,10 @@ bUserMenu *BKE_blender_user_menu_ensure(ListBase *lb, char space_type, const cha
{
bUserMenu *um = BKE_blender_user_menu_find(lb, space_type, context);
if (um == NULL) {
+
um = MEM_callocN(sizeof(bUserMenu), __func__);
um->space_type = space_type;
+ BLI_listbase_clear(&um->items);
STRNCPY(um->context, context);
BLI_addhead(lb, um);
}
@@ -80,6 +167,9 @@ bUserMenuItem *BKE_blender_user_menu_item_add(ListBase *lb, int type)
else if (type == USER_MENU_TYPE_PROP) {
size = sizeof(bUserMenuItem_Prop);
}
+ else if (type == USER_MENU_TYPE_SUBMENU) {
+ size = sizeof(bUserMenuItem_SubMenu);
+ }
else {
size = sizeof(bUserMenuItem);
BLI_assert(0);
@@ -87,7 +177,9 @@ bUserMenuItem *BKE_blender_user_menu_item_add(ListBase *lb, int type)
bUserMenuItem *umi = MEM_callocN(size, __func__);
umi->type = type;
- BLI_addtail(lb, umi);
+ umi->icon = 0;
+ if (lb)
+ BLI_addtail(lb, umi);
return umi;
}
@@ -99,6 +191,10 @@ void BKE_blender_user_menu_item_free(bUserMenuItem *umi)
IDP_FreeProperty(umi_op->prop);
}
}
+ if (umi->type == USER_MENU_TYPE_SUBMENU) {
+ bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)umi;
+ BKE_blender_user_menu_item_free_list(&umi_sm->items);
+ }
MEM_freeN(umi);
}
@@ -110,3 +206,19 @@ void BKE_blender_user_menu_item_free_list(ListBase *lb)
}
BLI_listbase_clear(lb);
}
+
+/* -------------------------------------------------------------------- */
+/** \name Default Menu
+ * \{ */
+
+bUserMenusGroup *BKU_blender_user_menu_default(void)
+{
+ bUserMenusGroup *umg = MEM_mallocN(sizeof(*umg), __func__);
+ STRNCPY(umg->name, "Quick Favorites");
+ STRNCPY(umg->idname, "QUICK_FAVORITES");
+ umg->type = 0;
+ umg->prev = NULL;
+ umg->next = NULL;
+ BLI_listbase_clear(&umg->menus);
+ return umg;
+} \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index ee60bf79611..0decce25e3a 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -57,6 +57,7 @@
#include "BKE_screen.h"
#include "BKE_studiolight.h"
#include "BKE_workspace.h"
+#include "BKE_blender_user_menu.h"
#include "BLO_readfile.h"
#include "BLO_writefile.h"
@@ -614,6 +615,13 @@ UserDef *BKE_blendfile_userdef_from_defaults(void)
BLI_addtail(&userdef->themes, btheme);
}
+ /* default user menus. */
+ {
+ bUserMenusGroup *umg = BKU_blender_user_menu_default();
+ BLI_addtail(&userdef->user_menus_group, umg);
+ userdef->runtime.umg_select = umg;
+ }
+
#ifdef WITH_PYTHON_SECURITY
/* use alternative setting for security nuts
* otherwise we'd need to patch the binary blob - startup.blend.c */
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index dce14c4c082..29a6f829c35 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1151,6 +1151,11 @@ const char *CTX_data_mode_string(const bContext *C)
return data_mode_strings[CTX_data_mode_enum(C)];
}
+const char **CTX_data_list_mode_string(void)
+{
+ return data_mode_strings;
+}
+
void CTX_data_scene_set(bContext *C, Scene *scene)
{
C->data.scene = scene;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 84cb898a426..241ae45ae5e 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -114,6 +114,7 @@
#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_armature.h"
+#include "BKE_blender_user_menu.h"
#include "BKE_brush.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
@@ -159,6 +160,8 @@
#include "BKE_volume.h"
#include "BKE_workspace.h"
+#include "BKE_blender_user_menu.h"
+
#include "DRW_engine.h"
#include "DEG_depsgraph.h"
@@ -8897,6 +8900,26 @@ static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi)
kmi->flag &= ~KMI_UPDATE;
}
+static void read_usermenuitems(BlendDataReader *reader,
+ ListBase *lb,
+ bUserMenuItem_SubMenu *parent)
+{
+ LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
+ umi->parent = parent;
+ if (umi->type == USER_MENU_TYPE_OPERATOR) {
+ bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
+ BLO_read_data_address(reader, &umi_op->prop);
+ IDP_DirectLinkGroup_OrFree(&umi_op->prop, reader);
+ }
+ if (umi->type == USER_MENU_TYPE_SUBMENU) {
+ bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)umi;
+ BLI_listbase_clear(&umi_sm->items);
+ BLO_read_list(reader, &umi_sm->items);
+ read_usermenuitems(reader, &umi_sm->items, umi_sm);
+ }
+ }
+}
+
static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
{
UserDef *user;
@@ -8918,6 +8941,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
BLO_read_list(reader, &user->user_menus);
BLO_read_list(reader, &user->addons);
BLO_read_list(reader, &user->autoexec_paths);
+ BLO_read_list(reader, &user->user_menus_group);
LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) {
keymap->modal_items = NULL;
@@ -8951,13 +8975,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
LISTBASE_FOREACH (bUserMenu *, um, &user->user_menus) {
BLO_read_list(reader, &um->items);
- LISTBASE_FOREACH (bUserMenuItem *, umi, &um->items) {
- if (umi->type == USER_MENU_TYPE_OPERATOR) {
- bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
- BLO_read_data_address(reader, &umi_op->prop);
- IDP_BlendDataRead(reader, &umi_op->prop);
- }
- }
+ read_usermenuitems(reader, &um->items, NULL);
}
LISTBASE_FOREACH (bAddon *, addon, &user->addons) {
@@ -8965,6 +8983,23 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
IDP_BlendDataRead(reader, &addon->prop);
}
+ LISTBASE_FOREACH (bUserMenusGroup *, umg, &user->user_menus_group) {
+ BLO_read_list(reader, &umg->menus);
+ BKE_blender_user_menus_group_idname_update(umg);
+ LISTBASE_FOREACH (bUserMenu *, um, &umg->menus) {
+ BLO_read_list(reader, &um->items);
+ read_usermenuitems(reader, &um->items, NULL);
+ }
+ }
+
+ if (user->user_menus.first != NULL && user->user_menus_group.first == NULL) {
+ bUserMenusGroup *umg = BKU_blender_user_menu_default();
+ BLI_addtail(&user->user_menus_group, umg);
+ user->runtime.umg_select = umg;
+ umg->menus = user->user_menus;
+ BLI_listbase_clear(&user->user_menus);
+ }
+
// XXX
user->uifonts.first = user->uifonts.last = NULL;
@@ -8976,6 +9011,10 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
/* Clear runtime data. */
user->runtime.is_dirty = false;
user->edit_studio_light = 0;
+ user->runtime.um_space_select = 1;
+ user->runtime.um_context_select = 1;
+ user->runtime.um_item_select = NULL;
+ user->runtime.umg_select = user->user_menus_group.first;
/* free fd->datamap again */
oldnewmap_clear(fd->datamap);
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index a9c92719a33..15c6e25985b 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -980,7 +980,37 @@ static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
}
}
+static void write_usermenuitems(BlendWriter *writer, const ListBase *lb)
+{
+ LISTBASE_FOREACH (const bUserMenuItem *, umi, lb) {
+ if (umi->type == USER_MENU_TYPE_OPERATOR) {
+ const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
+ BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
+ if (umi_op->prop) {
+ IDP_WriteProperty(umi_op->prop, writer);
+ }
+ }
+ else if (umi->type == USER_MENU_TYPE_MENU) {
+ const bUserMenuItem_Menu *umi_mt = (const bUserMenuItem_Menu *)umi;
+ BLO_write_struct(writer, bUserMenuItem_Menu, umi_mt);
+ }
+ else if (umi->type == USER_MENU_TYPE_PROP) {
+ const bUserMenuItem_Prop *umi_pr = (const bUserMenuItem_Prop *)umi;
+ BLO_write_struct(writer, bUserMenuItem_Prop, umi_pr);
+ }
+ else if (umi->type == USER_MENU_TYPE_SUBMENU) {
+ const bUserMenuItem_SubMenu *umi_sm = (const bUserMenuItem_SubMenu *)umi;
+ BLO_write_struct(writer, bUserMenuItem_SubMenu, umi_sm);
+ write_usermenuitems(writer, &umi_sm->items);
+ }
+ else {
+ BLO_write_struct(writer, bUserMenuItem, umi);
+ }
+ }
+}
+
static void write_userdef(BlendWriter *writer, const UserDef *userdef)
+
{
writestruct(writer->wd, USER, UserDef, 1, userdef);
@@ -1013,30 +1043,6 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
}
}
- LISTBASE_FOREACH (const bUserMenu *, um, &userdef->user_menus) {
- BLO_write_struct(writer, bUserMenu, um);
- LISTBASE_FOREACH (const bUserMenuItem *, umi, &um->items) {
- if (umi->type == USER_MENU_TYPE_OPERATOR) {
- const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
- BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
- if (umi_op->prop) {
- IDP_BlendWrite(writer, umi_op->prop);
- }
- }
- else if (umi->type == USER_MENU_TYPE_MENU) {
- const bUserMenuItem_Menu *umi_mt = (const bUserMenuItem_Menu *)umi;
- BLO_write_struct(writer, bUserMenuItem_Menu, umi_mt);
- }
- else if (umi->type == USER_MENU_TYPE_PROP) {
- const bUserMenuItem_Prop *umi_pr = (const bUserMenuItem_Prop *)umi;
- BLO_write_struct(writer, bUserMenuItem_Prop, umi_pr);
- }
- else {
- BLO_write_struct(writer, bUserMenuItem, umi);
- }
- }
- }
-
LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) {
BLO_write_struct(writer, bAddon, bext);
if (bext->prop) {
@@ -1048,6 +1054,14 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
BLO_write_struct(writer, bPathCompare, path_cmp);
}
+ LISTBASE_FOREACH (const bUserMenusGroup *, umg, &userdef->user_menus_group) {
+ BLO_write_struct(writer, bUserMenusGroup, umg);
+ LISTBASE_FOREACH (const bUserMenu *, um, &umg->menus) {
+ BLO_write_struct(writer, bUserMenu, um);
+ write_usermenuitems(writer, &um->items);
+ }
+ }
+
LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
BLO_write_struct(writer, uiStyle, style);
}
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index d9c7128c2ee..017253facd5 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -374,8 +374,12 @@ bool ED_operator_camera(struct bContext *C);
/* screen_user_menu.c */
-bUserMenu **ED_screen_user_menus_find(const struct bContext *C, uint *r_len);
-struct bUserMenu *ED_screen_user_menu_ensure(struct bContext *C);
+struct bUserMenusGroup *ED_screen_user_menus_group_find(int id);
+struct bUserMenu **ED_screen_user_menus_find_menu(const struct bContext *C,
+ uint *r_len,
+ struct bUserMenusGroup *umg);
+struct bUserMenu **ED_screen_user_menus_find(const struct bContext *C, uint *r_len, int id);
+struct bUserMenu *ED_screen_user_menu_ensure(struct bContext *C, int id);
struct bUserMenuItem_Op *ED_screen_user_menu_item_find_operator(struct ListBase *lb,
const struct wmOperatorType *ot,
@@ -396,7 +400,8 @@ void ED_screen_user_menu_item_add_operator(struct ListBase *lb,
void ED_screen_user_menu_item_add_menu(struct ListBase *lb,
const char *ui_name,
const struct MenuType *mt);
-void ED_screen_user_menu_item_add_prop(ListBase *lb,
+void ED_screen_user_menu_item_add_prop(struct bContext *C,
+ ListBase *lb,
const char *ui_name,
const char *context_data_path,
const char *prop_id,
@@ -404,6 +409,14 @@ void ED_screen_user_menu_item_add_prop(ListBase *lb,
void ED_screen_user_menu_item_remove(struct ListBase *lb, struct bUserMenuItem *umi);
void ED_screen_user_menu_register(void);
+bool screen_user_menu_draw_items(const struct bContext *C,
+ struct uiLayout *layout,
+ struct ListBase *lb,
+ char type);
+void screen_user_menu_draw_begin(struct bContext *C,
+ struct uiLayout *layout,
+ char type,
+ struct bUserMenusGroup *umg);
/* Cache display helpers */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 367f7965026..80e81445618 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2113,6 +2113,7 @@ void uiTemplateCacheFile(uiLayout *layout,
const struct bContext *C,
struct PointerRNA *ptr,
const char *propname);
+void uiTemplateUserMenuItemProperties(uiLayout *layout, PointerRNA *ptr);
/* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */
#define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list"
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 02a9c3742d7..3c3cb77fc85 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -437,7 +437,8 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
}
const char *prop_id = RNA_property_identifier(but->rnaprop);
/* Note, ignore 'drawstr', use property idname always. */
- ED_screen_user_menu_item_add_prop(&um->items, "", member_id_data_path, prop_id, but->rnaindex);
+ ED_screen_user_menu_item_add_prop(
+ C, &um->items, "", member_id_data_path, prop_id, but->rnaindex);
if (data_path) {
MEM_freeN((void *)data_path);
}
@@ -453,7 +454,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
static void popup_user_menu_add_or_replace_func(bContext *C, void *arg1, void *UNUSED(arg2))
{
uiBut *but = arg1;
- bUserMenu *um = ED_screen_user_menu_ensure(C);
+ bUserMenu *um = ED_screen_user_menu_ensure(C, 0);
U.runtime.is_dirty = true;
ui_but_user_menu_add(C, but, um);
}
@@ -983,7 +984,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
bool item_found = false;
uint um_array_len;
- bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
+ bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len, 0);
for (int um_index = 0; um_index < um_array_len; um_index++) {
bUserMenu *um = um_array[um_index];
if (um == NULL) {
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 8962755ea90..5f6fe35e98e 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -7390,3 +7390,80 @@ void uiTemplateFileSelectPath(uiLayout *layout, bContext *C, FileSelectParams *p
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Keymap Template
+ * \{ */
+
+static void template_user_menu_item_properties(uiLayout *layout,
+ const char *title,
+ PointerRNA *ptr)
+{
+ uiLayout *flow, *box, *row;
+
+ uiItemS(layout);
+
+ if (title) {
+ uiItemL(layout, title, ICON_NONE);
+ }
+
+ flow = uiLayoutColumnFlow(layout, 2, false);
+
+ int i = 0;
+ RNA_STRUCT_BEGIN_SKIP_RNA_TYPE (ptr, prop) {
+ const bool is_set = RNA_property_is_set(ptr, prop);
+ uiBut *but;
+
+ // recurse for nested properties
+ if (RNA_property_type(prop) == PROP_POINTER) {
+ PointerRNA propptr = RNA_property_pointer_get(ptr, prop);
+
+ if (propptr.data && RNA_struct_is_a(propptr.type, &RNA_OperatorProperties)) {
+ const char *name = RNA_property_ui_name(prop);
+ template_user_menu_item_properties(layout, name, &propptr);
+ continue;
+ }
+ }
+
+ box = uiLayoutBox(flow);
+ uiLayoutSetActive(box, is_set);
+ row = uiLayoutRow(box, false);
+
+ // property value
+ uiItemFullR(row, ptr, prop, -1, 0, 0, NULL, ICON_NONE);
+
+ if (is_set) {
+ // unset operator
+ uiBlock *block = uiLayoutGetBlock(row);
+ UI_block_emboss_set(block, UI_EMBOSS_NONE);
+ but = uiDefIconButO(block,
+ UI_BTYPE_BUT,
+ "UI_OT_unset_property_button",
+ WM_OP_EXEC_DEFAULT,
+ ICON_X,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ NULL);
+ but->rnapoin = *ptr;
+ but->rnaprop = prop;
+ UI_block_emboss_set(block, UI_EMBOSS);
+ }
+ }
+ RNA_STRUCT_END;
+}
+
+void uiTemplateUserMenuItemProperties(uiLayout *layout, PointerRNA *ptr)
+{
+ PointerRNA propptr = RNA_pointer_get(ptr, "prop");
+
+ if (propptr.data) {
+ uiBut *but = uiLayoutGetBlock(layout)->buttons.last;
+
+ WM_operator_properties_sanitize(&propptr, false);
+ template_user_menu_item_properties(layout, NULL, &propptr);
+ }
+}
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c
index 733e8b694a6..4ae15a8dce1 100644
--- a/source/blender/editors/screen/screen_user_menu.c
+++ b/source/blender/editors/screen/screen_user_menu.c
@@ -67,10 +67,23 @@ static const char *screen_menu_context_string(const bContext *C, const SpaceLink
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Menu Type
+/** \name Menu Group
* \{ */
-bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len)
+bUserMenusGroup *ED_screen_user_menus_group_find(int id)
+{
+ int index = 0;
+ LISTBASE_FOREACH (bUserMenusGroup *, umg, &U.user_menus_group) {
+ if (index == id)
+ return umg;
+ index++;
+ }
+ return NULL;
+}
+
+/** \} */
+
+bUserMenu **ED_screen_user_menus_find_menu(const bContext *C, uint *r_len, bUserMenusGroup *umg)
{
SpaceLink *sl = CTX_wm_space_data(C);
@@ -83,23 +96,33 @@ bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len)
const char *context = screen_menu_context_string(C, sl);
uint array_len = 3;
bUserMenu **um_array = MEM_calloc_arrayN(array_len, sizeof(*um_array), __func__);
- um_array[0] = BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context);
+ um_array[0] = BKE_blender_user_menu_find(&umg->menus, sl->spacetype, context);
um_array[1] = (sl->spacetype != SPACE_TOPBAR) ?
- BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context_mode) :
+ BKE_blender_user_menu_find(&umg->menus, SPACE_TOPBAR, context_mode) :
NULL;
um_array[2] = (sl->spacetype == SPACE_VIEW3D) ?
- BKE_blender_user_menu_find(&U.user_menus, SPACE_PROPERTIES, context_mode) :
+ BKE_blender_user_menu_find(&umg->menus, SPACE_PROPERTIES, context_mode) :
NULL;
-
*r_len = array_len;
return um_array;
}
-bUserMenu *ED_screen_user_menu_ensure(bContext *C)
+bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len, int id)
+{
+ bUserMenusGroup *umg = ED_screen_user_menus_group_find(id);
+ if (!umg)
+ return NULL;
+ return ED_screen_user_menus_find_menu(C, r_len, umg);
+}
+
+bUserMenu *ED_screen_user_menu_ensure(bContext *C, int id)
{
+ bUserMenusGroup *umg = ED_screen_user_menus_group_find(id);
+ if (!umg)
+ return NULL;
SpaceLink *sl = CTX_wm_space_data(C);
const char *context = screen_menu_context_string(C, sl);
- return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context);
+ return BKE_blender_user_menu_ensure(&umg->menus, sl->spacetype, context);
}
/** \} */
@@ -169,6 +192,9 @@ void ED_screen_user_menu_item_add_operator(ListBase *lb,
STRNCPY(umi_op->item.ui_name, ui_name);
}
STRNCPY(umi_op->op_idname, ot->idname);
+ umi_op->ptr = NULL;
+ umi_op->prop = NULL;
+ WM_operator_properties_alloc(&(umi_op->ptr), &(umi_op->prop), ot->idname);
umi_op->prop = prop ? IDP_CopyProperty(prop) : NULL;
}
@@ -182,7 +208,8 @@ void ED_screen_user_menu_item_add_menu(ListBase *lb, const char *ui_name, const
STRNCPY(umi_mt->mt_idname, mt->idname);
}
-void ED_screen_user_menu_item_add_prop(ListBase *lb,
+void ED_screen_user_menu_item_add_prop(bContext *C,
+ ListBase *lb,
const char *ui_name,
const char *context_data_path,
const char *prop_id,
@@ -194,6 +221,39 @@ void ED_screen_user_menu_item_add_prop(ListBase *lb,
STRNCPY(umi_pr->context_data_path, context_data_path);
STRNCPY(umi_pr->prop_id, prop_id);
umi_pr->prop_index = prop_index;
+
+ /* Copy current context name into ui_name if null */
+ if (ui_name && ui_name[0] != '\0')
+ return;
+ char *data_path = strchr(umi_pr->context_data_path, '.');
+ if (data_path) {
+ *data_path = '\0';
+ }
+
+ PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
+ if (ptr.type == NULL) {
+ PointerRNA ctx_ptr;
+ RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
+ if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
+ ptr.type = NULL;
+ }
+ }
+ if (data_path) {
+ *data_path = '.';
+ data_path += 1;
+ }
+
+ if (ptr.type != NULL) {
+ PropertyRNA *prop = NULL;
+ PointerRNA prop_ptr = ptr;
+ if ((data_path == NULL) || RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
+ prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
+ }
+ if (prop) {
+ const char *name = RNA_property_ui_name(prop);
+ BLI_strncpy(umi_pr->item.ui_name, name, FILE_MAX);
+ }
+ }
}
void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi)
@@ -208,97 +268,146 @@ void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi)
/** \name Menu Definition
* \{ */
-static void screen_user_menu_draw(const bContext *C, Menu *menu)
+static void screen_user_menu_draw_submenu(bContext *C, uiLayout *layout, void *arg)
+{
+ ListBase *lb = (ListBase *)arg;
+
+ screen_user_menu_draw_items(C, layout, lb, false);
+}
+
+bool screen_user_menu_draw_items(const bContext *C, uiLayout *layout, ListBase *lb, char type)
{
/* Enable when we have the ability to edit menus. */
- const bool show_missing = false;
char label[512];
-
- uint um_array_len;
- bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
+ const bool show_missing = type;
bool is_empty = true;
- for (int um_index = 0; um_index < um_array_len; um_index++) {
- bUserMenu *um = um_array[um_index];
- if (um == NULL) {
- continue;
- }
- LISTBASE_FOREACH (bUserMenuItem *, umi, &um->items) {
- const char *ui_name = umi->ui_name[0] ? umi->ui_name : NULL;
- if (umi->type == USER_MENU_TYPE_OPERATOR) {
- bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
- wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false);
- if (ot != NULL) {
- IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
- uiItemFullO_ptr(menu->layout, ot, ui_name, ICON_NONE, prop, umi_op->opcontext, 0, NULL);
- is_empty = false;
- }
- else {
- if (show_missing) {
- SNPRINTF(label, "Missing: %s", umi_op->op_idname);
- uiItemL(menu->layout, label, ICON_NONE);
- }
- }
+
+ int i = 0;
+
+ LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
+ if (type == 1 && i > 7)
+ return is_empty;
+ const char *ui_name = umi->ui_name[0] ? umi->ui_name : NULL;
+ if (umi->type == USER_MENU_TYPE_OPERATOR) {
+ bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
+ wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false);
+ if (ot != NULL) {
+ IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
+ uiItemFullO_ptr(layout, ot, ui_name, umi->icon, prop, umi_op->opcontext, 0, NULL);
+ is_empty = false;
}
- else if (umi->type == USER_MENU_TYPE_MENU) {
- bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
- MenuType *mt = WM_menutype_find(umi_mt->mt_idname, false);
- if (mt != NULL) {
- uiItemM_ptr(menu->layout, mt, ui_name, ICON_NONE);
- is_empty = false;
+ else {
+ if (show_missing) {
+ SNPRINTF(label, "Missing: %s", umi_op->op_idname);
+ uiItemL(layout, label, ICON_NONE);
}
- else {
- if (show_missing) {
- SNPRINTF(label, "Missing: %s", umi_mt->mt_idname);
- uiItemL(menu->layout, label, ICON_NONE);
- }
+ i--;
+ }
+ }
+ else if (umi->type == USER_MENU_TYPE_MENU) {
+ bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
+ MenuType *mt = WM_menutype_find(umi_mt->mt_idname, false);
+ if (mt != NULL) {
+ uiItemM_ptr(layout, mt, ui_name, umi->icon);
+ is_empty = false;
+ }
+ else {
+ if (show_missing) {
+ SNPRINTF(label, "Missing: %s", umi_mt->mt_idname);
+ uiItemL(layout, label, ICON_NONE);
}
+ i--;
}
- else if (umi->type == USER_MENU_TYPE_PROP) {
- bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
+ }
+ else if (umi->type == USER_MENU_TYPE_SUBMENU) {
+ bUserMenuItem_SubMenu *umi_mt = (bUserMenuItem_SubMenu *)umi;
- char *data_path = strchr(umi_pr->context_data_path, '.');
- if (data_path) {
- *data_path = '\0';
- }
- PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
- if (ptr.type == NULL) {
- PointerRNA ctx_ptr;
- RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
- if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
- ptr.type = NULL;
- }
- }
- if (data_path) {
- *data_path = '.';
- data_path += 1;
- }
+ uiItemMenuF(
+ layout, ui_name, umi->icon, &screen_user_menu_draw_submenu, (void *)&umi_mt->items);
+ is_empty = false;
+ }
+ else if (umi->type == USER_MENU_TYPE_PROP) {
+ bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
- bool ok = false;
- if (ptr.type != NULL) {
- PropertyRNA *prop = NULL;
- PointerRNA prop_ptr = ptr;
- if ((data_path == NULL) ||
- RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
- prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
- if (prop) {
- ok = true;
- uiItemFullR(
- menu->layout, &prop_ptr, prop, umi_pr->prop_index, 0, 0, ui_name, ICON_NONE);
- is_empty = false;
- }
- }
+ char *data_path = strchr(umi_pr->context_data_path, '.');
+ if (data_path) {
+ *data_path = '\0';
+ }
+ PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
+ if (ptr.type == NULL) {
+ PointerRNA ctx_ptr;
+ RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
+ if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
+ ptr.type = NULL;
}
- if (!ok) {
- if (show_missing) {
- SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id);
- uiItemL(menu->layout, label, ICON_NONE);
+ }
+ if (data_path) {
+ *data_path = '.';
+ data_path += 1;
+ }
+
+ bool ok = false;
+ if (ptr.type != NULL) {
+ PropertyRNA *prop = NULL;
+ PointerRNA prop_ptr = ptr;
+ if ((data_path == NULL) || RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
+ prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
+ if (prop) {
+ ok = true;
+ uiItemFullR(layout, &prop_ptr, prop, umi_pr->prop_index, 0, 0, ui_name, umi->icon);
+ is_empty = false;
}
}
}
- else if (umi->type == USER_MENU_TYPE_SEP) {
- uiItemS(menu->layout);
+ if (!ok) {
+ if (show_missing) {
+ SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id);
+ uiItemL(layout, label, ICON_NONE);
+ }
+ i--;
}
}
+ else if (umi->type == USER_MENU_TYPE_SEP) {
+ uiItemS(layout);
+ }
+ i++;
+ }
+ return is_empty;
+}
+
+void screen_user_menu_draw_begin(bContext *C, uiLayout *layout, char type, bUserMenusGroup *umg)
+{
+ uint um_array_len;
+ bUserMenu **um_array = ED_screen_user_menus_find_menu(C, &um_array_len, umg);
+ bool is_empty = true;
+ for (int um_index = 0; um_index < um_array_len; um_index++) {
+ bUserMenu *um = um_array[um_index];
+ if (um == NULL) {
+ continue;
+ }
+ is_empty = screen_user_menu_draw_items(C, layout, &um->items, true) && is_empty;
+ }
+ if (um_array) {
+ MEM_freeN(um_array);
+ }
+
+ if (is_empty && type == 0) {
+ uiItemL(layout, TIP_("No menu items found"), ICON_NONE);
+ uiItemL(layout, TIP_("Right click on buttons to add them to this menu"), ICON_NONE);
+ }
+}
+
+static void screen_user_menu_draw(const bContext *C, Menu *menu)
+{
+ uint um_array_len;
+ bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len, 0);
+ bool is_empty = true;
+ for (int um_index = 0; um_index < um_array_len; um_index++) {
+ bUserMenu *um = um_array[um_index];
+ if (um == NULL) {
+ continue;
+ }
+ is_empty = is_empty || screen_user_menu_draw_items(C, menu->layout, &um->items, true);
}
if (um_array) {
MEM_freeN(um_array);
diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c
index 3efdee9cec9..ffa8f8ebcaf 100644
--- a/source/blender/editors/space_userpref/space_userpref.c
+++ b/source/blender/editors/space_userpref/space_userpref.c
@@ -136,6 +136,7 @@ static void userpref_main_region_layout(const bContext *C, ARegion *region)
i = 0;
}
const char *id = items[i].identifier;
+
BLI_assert(strlen(id) < sizeof(id_lower));
STRNCPY(id_lower, id);
BLI_str_tolower_ascii(id_lower, strlen(id_lower));
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index ec46d2680ca..ee7af5dd56b 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -515,27 +515,42 @@ typedef struct bPathCompare {
char _pad0[7];
} bPathCompare;
+typedef struct bUserMenusGroup {
+ struct bUserMenusGroup *next, *prev;
+ /* bUserMenu */
+ char idname[64];
+ char name[64];
+ char type;
+ char _pad0[7];
+ struct ListBase menus;
+} bUserMenusGroup;
+
typedef struct bUserMenu {
struct bUserMenu *next, *prev;
char space_type;
char _pad0[7];
char context[64];
/* bUserMenuItem */
- ListBase items;
+ struct ListBase items;
} bUserMenu;
/** May be part of #bUserMenu or other list. */
typedef struct bUserMenuItem {
struct bUserMenuItem *next, *prev;
+ struct bUserMenuItem_SubMenu *parent;
+ int icon;
char ui_name[64];
char type;
- char _pad0[7];
+ // editor
+ char is_selected;
+ char _pad0[2];
} bUserMenuItem;
typedef struct bUserMenuItem_Op {
bUserMenuItem item;
char op_idname[64];
struct IDProperty *prop;
+ struct PointerRNA *ptr;
char opcontext;
char _pad0[7];
} bUserMenuItem_Op;
@@ -545,6 +560,12 @@ typedef struct bUserMenuItem_Menu {
char mt_idname[64];
} bUserMenuItem_Menu;
+typedef struct bUserMenuItem_SubMenu {
+ bUserMenuItem item;
+ struct ListBase items;
+ char name[64];
+} bUserMenuItem_SubMenu;
+
typedef struct bUserMenuItem_Prop {
bUserMenuItem item;
char context_data_path[256];
@@ -558,6 +579,7 @@ enum {
USER_MENU_TYPE_OPERATOR = 2,
USER_MENU_TYPE_MENU = 3,
USER_MENU_TYPE_PROP = 4,
+ USER_MENU_TYPE_SUBMENU = 5,
};
typedef struct SolidLight {
@@ -582,7 +604,14 @@ typedef struct WalkNavigation {
typedef struct UserDef_Runtime {
char is_dirty;
- char _pad0[7];
+
+ /* User menu editor runtime datas */
+ char um_space_select;
+ char um_context_select;
+ char um_expanded;
+ char _pad0[4];
+ struct bUserMenuItem *um_item_select;
+ struct bUserMenusGroup *umg_select;
} UserDef_Runtime;
/**
@@ -722,8 +751,10 @@ typedef struct UserDef {
struct ListBase user_keyconfig_prefs;
struct ListBase addons;
struct ListBase autoexec_paths;
- /** #bUserMenu. */
+ /** #bUserMenu : deprecated, keep for compatibility. */
struct ListBase user_menus;
+ /** #bUserMenuGroups. */
+ struct ListBase user_menus_group;
char keyconfigstr[64];
@@ -913,16 +944,17 @@ typedef enum eUserPref_Section {
USER_SECTION_ADDONS = 6,
USER_SECTION_LIGHT = 7,
USER_SECTION_KEYMAP = 8,
+ USER_SECTION_USER_MENUS = 9,
#ifdef WITH_USERDEF_WORKSPACES
- USER_SECTION_WORKSPACE_CONFIG = 9,
- USER_SECTION_WORKSPACE_ADDONS = 10,
- USER_SECTION_WORKSPACE_KEYMAPS = 11,
+ USER_SECTION_WORKSPACE_CONFIG = 10,
+ USER_SECTION_WORKSPACE_ADDONS = 11,
+ USER_SECTION_WORKSPACE_KEYMAPS = 12,
#endif
- USER_SECTION_VIEWPORT = 12,
- USER_SECTION_ANIMATION = 13,
- USER_SECTION_NAVIGATION = 14,
- USER_SECTION_FILE_PATHS = 15,
- USER_SECTION_EXPERIMENTAL = 16,
+ USER_SECTION_VIEWPORT = 13,
+ USER_SECTION_ANIMATION = 14,
+ USER_SECTION_NAVIGATION = 15,
+ USER_SECTION_FILE_PATHS = 16,
+ USER_SECTION_EXPERIMENTAL = 17,
} eUserPref_Section;
/** #UserDef_SpaceData.flag (State of the user preferences UI). */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 6acd9d16f80..7586cce2275 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -474,6 +474,7 @@ extern StructRNA RNA_PreferencesEdit;
extern StructRNA RNA_PreferencesFilePaths;
extern StructRNA RNA_PreferencesInput;
extern StructRNA RNA_PreferencesKeymap;
+extern StructRNA RNA_PreferencesUserMenus;
extern StructRNA RNA_PreferencesSystem;
extern StructRNA RNA_PreferencesView;
extern StructRNA RNA_Property;
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index e94dad59176..7c699de7035 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -1869,7 +1869,7 @@ void RNA_def_property_enum_items(PropertyRNA *prop, const EnumPropertyItem *item
{
StructRNA *srna = DefRNA.laststruct;
int i, defaultfound = 0;
-
+
switch (prop->type) {
case PROP_ENUM: {
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index 8dfa54b95da..8a85b924505 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -1639,6 +1639,12 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_property_ui_text(parm, "Item", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
api_ui_item_common_text(func);
+
+ /* User menu template */
+ func = RNA_def_function(
+ srna, "template_user_menu_item_properties", "uiTemplateUserMenuItemProperties");
+ parm = RNA_def_pointer(func, "item", "um_item_op", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 699e08302e7..7d49cadbeed 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -88,6 +88,7 @@ const EnumPropertyItem rna_enum_preference_section_items[] = {
{USER_SECTION_INPUT, "INPUT", 0, "Input", ""},
{USER_SECTION_NAVIGATION, "NAVIGATION", 0, "Navigation", ""},
{USER_SECTION_KEYMAP, "KEYMAP", 0, "Keymap", ""},
+ {USER_SECTION_USER_MENUS, "USER_MENUS", 0, "User Menus", ""},
{0, "", 0, NULL, NULL},
{USER_SECTION_SYSTEM, "SYSTEM", 0, "System", ""},
{USER_SECTION_SAVE_LOAD, "SAVE_LOAD", 0, "Save & Load", ""},
@@ -177,6 +178,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "DNA_screen_types.h"
# include "BKE_blender.h"
+# include "BKE_blender_user_menu.h"
# include "BKE_global.h"
# include "BKE_idprop.h"
# include "BKE_image.h"
@@ -186,6 +188,8 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "BKE_pbvh.h"
# include "BKE_screen.h"
+# include "ED_screen.h"
+
# include "DEG_depsgraph.h"
# include "GPU_extensions.h"
@@ -531,6 +535,11 @@ static PointerRNA rna_UserDef_keymap_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, &RNA_PreferencesKeymap, ptr->data);
}
+static PointerRNA rna_UserDef_user_menus_get(PointerRNA *ptr)
+{
+ return rna_pointer_inherit_refine(ptr, &RNA_PreferencesUserMenus, ptr->data);
+}
+
static PointerRNA rna_UserDef_filepaths_get(PointerRNA *ptr)
{
return rna_pointer_inherit_refine(ptr, &RNA_PreferencesFilePaths, ptr->data);
@@ -1068,6 +1077,549 @@ static void rna_UserDef_studiolight_light_ambient_get(PointerRNA *ptr, float *va
copy_v3_v3(values, sl->light_ambient);
}
+/* User Menus Functions */
+
+static bUserMenu *rna_UserDef_usermenus_get_current(UserDef *userdef, bool ensure)
+{
+ const char **contexts_list = CTX_data_list_mode_string();
+# if 0 /* UNUSED */
+ ListBase *umg_list = &userdef->user_menus_group;
+# endif
+
+ bUserMenusGroup *umg = userdef->runtime.umg_select;
+ bUserMenu *bum = NULL;
+
+ if (!umg)
+ return NULL;
+ if (userdef->runtime.um_space_select > 0) {
+ if (umg->type) {
+ bum = BKE_blender_user_menu_find(&umg->menus,
+ userdef->runtime.um_space_select,
+ contexts_list[userdef->runtime.um_context_select]);
+ if (!bum) {
+ bum = BKE_blender_user_menu_ensure(&umg->menus,
+ userdef->runtime.um_space_select,
+ contexts_list[userdef->runtime.um_context_select]);
+ for (int i = 0; i < 8; i++) {
+ BKE_blender_user_menu_item_add(&bum->items, USER_MENU_TYPE_SEP);
+ }
+ }
+ }
+ else {
+ if (ensure) {
+ bum = BKE_blender_user_menu_ensure(&umg->menus,
+ userdef->runtime.um_space_select,
+ contexts_list[userdef->runtime.um_context_select]);
+ }
+ else {
+ bum = BKE_blender_user_menu_find(&umg->menus,
+ userdef->runtime.um_space_select,
+ contexts_list[userdef->runtime.um_context_select]);
+ }
+ }
+ }
+ return bum;
+}
+
+/*static bUserMenuItem *rna_UserDef_usermenus_get_current_item(UserDef *userdef)
+{
+ int id = userdef->runtime.um_item_select - 1;
+ if (id < 0) return NULL;
+
+ bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, false);
+ if (bum) {
+
+ ListBase *lb = &bum->items;
+ int i = 0;
+ for (bUserMenuItem *umi = lb->first; umi; umi = umi->next, i++) {
+ if (i == id)
+ return umi;
+ }
+ }
+
+ return NULL;
+}*/
+
+static bUserMenusGroup *rna_UserDef_usermenus_get_group(UserDef *userdef, const char *idname)
+{
+ return BKE_blender_user_menus_group_find(&userdef->user_menus_group, idname);
+}
+
+static void rna_UserDef_usermenus_set_group(UserDef *userdef, bUserMenusGroup *umg)
+{
+ userdef->runtime.umg_select = umg;
+}
+
+static void rna_UserDef_usermenus_add_group(UserDef *userdef)
+{
+ bUserMenusGroup *umg = BKE_blender_user_menus_group_new("new menu");
+ BLI_addtail(&userdef->user_menus_group, umg);
+ userdef->runtime.umg_select = umg;
+}
+
+static void rna_UserDef_usermenus_remove_group(UserDef *userdef)
+{
+ bUserMenusGroup *umg = userdef->runtime.umg_select;
+
+ userdef->runtime.umg_select = umg->prev;
+ if (!userdef->runtime.umg_select) {
+ userdef->runtime.umg_select = umg->next;
+ if (!userdef->runtime.umg_select) {
+ return;
+ }
+ }
+
+ BLI_remlink(&userdef->user_menus_group, umg);
+ BKE_blender_user_menu_free_list(&umg->menus);
+ MEM_freeN(umg);
+}
+
+static bool rna_UserDef_usermenus_has_item(UserDef *userdef)
+{
+ bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, false);
+ if (!bum)
+ return false;
+
+ ListBase *lb = &bum->items;
+ bUserMenuItem *umi = lb->first;
+ if (!umi)
+ return false;
+ return true;
+}
+
+static int rna_UserDef_usermenus_items_length(UserDef *userdef)
+{
+ bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, false);
+ if (!bum)
+ return 0;
+
+ ListBase *lb = &bum->items;
+ int i = 0;
+ for (bUserMenuItem *umi = lb->first; umi; umi = umi->next, i++)
+ ;
+ return i;
+}
+
+static int rna_UserDef_usermenus_spacetypes_get(PointerRNA *ptr)
+{
+ UserDef *userdef = (UserDef *)ptr->data;
+ int id = userdef->runtime.um_space_select;
+ id = (id <= 0) ? USER_SECTION_EDITING : id;
+ return id;
+}
+
+static void rna_UserDef_usermenus_active_item_set(PointerRNA *ptr, bool value)
+{
+ bUserMenuItem *umi = (bUserMenuItem *)ptr->data;
+
+ if (value) {
+ U.runtime.um_item_select = umi;
+ }
+ umi->is_selected = value;
+}
+
+static const EnumPropertyItem *rna_UserDef_usermenus_spacetypes_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ int totitem = 0;
+ EnumPropertyItem *item = NULL;
+ int i;
+ const ListBase *spacetypes = BKE_spacetypes_list();
+
+ SpaceType *st = NULL;
+ for (i = 0, st = spacetypes->first; st; st = st->next, i++) {
+ int id = st->spaceid;
+ EnumPropertyItem new_item = {id, st->name, 0, st->name, st->name};
+ RNA_enum_item_add(&item, &totitem, &new_item);
+ }
+
+# ifndef NDEBUG
+ if (i == 0) {
+ EnumPropertyItem new_item = {i, "NO_SPACETYPE", 0, "No spacetype available", ""};
+ RNA_enum_item_add(&item, &totitem, &new_item);
+ }
+# endif
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+
+static const EnumPropertyItem *rna_UserDef_usermenus_contexts_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ static const char *contexts_list[] = {
+ "mesh edit", "curve edit", "surface edit", "text edit",
+ "armature edit", "mball edit", "lattice edit", "pose mode",
+ "sculpt mode", "weight paint", "vertex paint", "image paint",
+ "particle mode", "object mode", "greasepencil paint", "greasepencil edit",
+ "greasepencil sculpt", "greasepencil weight", "greasepencil vertex", NULL,
+ };
+
+ int totitem = 0;
+ EnumPropertyItem *item = NULL;
+ int i;
+
+ for (i = 0; contexts_list[i]; i++) {
+ EnumPropertyItem new_item = {i, contexts_list[i], 0, contexts_list[i], contexts_list[i]};
+ RNA_enum_item_add(&item, &totitem, &new_item);
+ }
+
+# ifndef NDEBUG
+ if (i == 0) {
+ EnumPropertyItem new_item = {i, "NO_CONTEXT", 0, "No context available", ""};
+ RNA_enum_item_add(&item, &totitem, &new_item);
+ }
+# endif
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+
+static const EnumPropertyItem *rna_UserDef_icons_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ int totitem = 0;
+ const EnumPropertyItem *list = rna_enum_icon_items;
+ EnumPropertyItem *item = NULL;
+
+ int i;
+
+ for (i = 0; list[i].identifier; i++) {
+ EnumPropertyItem new_item = {
+ list[i].value, list[i].identifier, list[i].value, list[i].name, list[i].description};
+ RNA_enum_item_add(&item, &totitem, &new_item);
+ }
+
+# ifndef NDEBUG
+ if (i == 0) {
+ EnumPropertyItem new_item = {i, "NONE", 0, "No icons", ""};
+ RNA_enum_item_add(&item, &totitem, &new_item);
+ }
+# endif
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+
+static void rna_UserDef_usermenus_item_add(UserDef *userdef, int index)
+{
+ bUserMenu *um = rna_UserDef_usermenus_get_current(userdef, true);
+ if (!um)
+ return;
+
+ ListBase *lb = &um->items;
+ bUserMenuItem_Op *umi = (bUserMenuItem_Op *)BKE_blender_user_menu_item_add(
+ NULL, USER_MENU_TYPE_OPERATOR);
+
+ if (index < 0)
+ BLI_addtail(lb, umi);
+ else {
+ int i = 0;
+ for (bUserMenuItem *insert = lb->first; insert; insert = insert->next, i++) {
+ if (i == index) {
+ BLI_insertlinkbefore(lb, insert, umi);
+ return;
+ }
+ }
+ BLI_addtail(lb, umi);
+ }
+
+ // basic operator setup
+ wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_add", true);
+ STRNCPY(umi->item.ui_name, "new item");
+ STRNCPY(umi->op_idname, ot->idname);
+ userdef->runtime.um_item_select = &umi->item;
+}
+
+static void rna_UserDef_usermenus_item_remove(UserDef *userdef)
+{
+ bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, true);
+ bUserMenuItem *umi = userdef->runtime.um_item_select;
+ if (!bum || !umi)
+ return;
+
+ userdef->runtime.um_item_select = umi->next;
+ if (!userdef->runtime.um_item_select)
+ userdef->runtime.um_item_select = umi->prev;
+ ListBase *lb = (umi->parent) ? &umi->parent->items : &bum->items;
+ ED_screen_user_menu_item_remove(lb, umi);
+}
+
+static void rna_UserDef_usermenus_item_move(UserDef *userdef, bool up)
+{
+ bUserMenu *bum = rna_UserDef_usermenus_get_current(userdef, true);
+ bUserMenuItem *umi = userdef->runtime.um_item_select;
+ if (!bum || !umi)
+ return;
+
+ ListBase *lb = (umi->parent) ? &umi->parent->items : &bum->items;
+ bUserMenuItem *umi_toward = (up) ? umi->prev : umi->next;
+ BLI_remlink(lb, umi);
+
+ if (!umi_toward) {
+
+ if (!umi->parent)
+ return;
+ if (!umi->parent->item.parent) {
+ if (up)
+ BLI_insertlinkbefore(&bum->items, umi->parent, umi);
+ else
+ BLI_insertlinkafter(&bum->items, umi->parent, umi);
+ umi->parent = NULL;
+ }
+ else {
+ if (up)
+ BLI_insertlinkbefore(&umi->parent->item.parent->items, umi->parent, umi);
+ else
+ BLI_insertlinkafter(&umi->parent->item.parent->items, umi->parent, umi);
+ umi->parent = umi->parent->item.parent;
+ }
+ }
+ else if (umi_toward->type == USER_MENU_TYPE_SUBMENU) {
+ bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)umi_toward;
+ if (up)
+ BLI_addtail(&umi_sm->items, umi);
+ else
+ BLI_addhead(&umi_sm->items, umi);
+ umi->parent = umi_sm;
+ }
+ else {
+
+ if (up)
+ BLI_insertlinkbefore(lb, umi->prev, umi);
+ else
+ BLI_insertlinkafter(lb, umi->next, umi);
+ }
+}
+
+static void rna_UserDef_usermenus_pie_item_add(UserDef *userdef, int index)
+{
+ bUserMenu *um = rna_UserDef_usermenus_get_current(userdef, true);
+ if (!um)
+ return;
+
+ bUserMenuItem_Op *umi = NULL;
+ ListBase *lb = &um->items;
+ bUserMenuItem *insert = lb->first;
+ int i = 0;
+ for (insert = lb->first; insert; insert = insert->next, i++) {
+ if (insert->type != USER_MENU_TYPE_SUBMENU)
+ continue;
+ bUserMenuItem_SubMenu *sm = (bUserMenuItem_SubMenu *)insert;
+ if (i == index) {
+ umi = (bUserMenuItem_Op *)BKE_blender_user_menu_item_add(&sm->items,
+ USER_MENU_TYPE_OPERATOR);
+ break;
+ }
+ }
+
+ if (!umi)
+ return;
+
+ // basic operator setup
+ wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_add", true);
+ STRNCPY(umi->item.ui_name, "new item");
+ STRNCPY(umi->op_idname, ot->idname);
+ umi->item.parent = (bUserMenuItem_SubMenu *)insert;
+ userdef->runtime.um_item_select = &umi->item;
+}
+
+static void rna_UserDef_usermenus_item_type_set(PointerRNA *ptr, int value)
+{
+ bUserMenuItem *umi = (bUserMenuItem *)ptr->data;
+ bUserMenu *bum = rna_UserDef_usermenus_get_current(&U, true);
+
+ if (!bum || !umi || umi->type == value)
+ return;
+
+ ListBase *lb = (!umi->parent) ? &bum->items : &umi->parent->items;
+ BLI_remlink(lb, umi);
+ bUserMenuItem *new_umi = BKE_blender_user_menu_item_add(NULL, value);
+ new_umi->parent = umi->parent;
+ if (value == USER_MENU_TYPE_SUBMENU) {
+ bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)new_umi;
+ BLI_listbase_clear(&umi_sm->items);
+ }
+ BLI_insertlinkafter(lb, umi->prev, new_umi);
+ STRNCPY(new_umi->ui_name, umi->ui_name);
+ BKE_blender_user_menu_item_free(umi);
+ U.runtime.um_item_select = new_umi;
+}
+
+static int rna_UserDef_usermenus_item_type_get(PointerRNA *ptr)
+{
+ bUserMenuItem *data = (bUserMenuItem *)(ptr->data);
+ if (data)
+ return (int)(data->type);
+ return 0;
+}
+
+static void rna_UserDef_usermenus_item_name_get(PointerRNA *ptr, char *value)
+{
+ bUserMenuItem *umi = (bUserMenuItem *)(ptr->data);
+
+ if (!*umi->ui_name) {
+ const char *name = " ";
+ if (umi->type == USER_MENU_TYPE_MENU) {
+ bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
+ MenuType *mt = WM_menutype_find(umi_mt->mt_idname, true);
+ name = (const char *)CTX_IFACE_(mt->translation_context, mt->label);
+ BLI_strncpy(umi->ui_name, name, FILE_MAX);
+ }
+ }
+ BLI_strncpy(value, umi->ui_name, FILE_MAX);
+}
+
+static void rna_UserDef_usermenus_pie_set(PointerRNA *ptr, int value)
+{
+ bUserMenusGroup *umg = (bUserMenusGroup *)ptr->data;
+
+ LISTBASE_FOREACH (bUserMenu *, um, &umg->menus) {
+ ListBase *lb = &um->items;
+ BKE_blender_user_menu_item_free_list(lb);
+ if (value)
+ for (int i = 0; i < 8; i++) {
+ BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_SEP);
+ }
+ }
+ umg->type = value;
+}
+
+static PointerRNA rna_UserDef_usermenus_item_op_prop_get(PointerRNA *ptr)
+{
+ bUserMenuItem_Op *umi_op = ptr->data;
+
+ if (umi_op->ptr) {
+ return *(umi_op->ptr);
+ }
+ return PointerRNA_NULL;
+}
+
+static void rna_UserDef_usermenus_group_idname_set(Main *bmain,
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ bUserMenusGroup *umg = (bUserMenusGroup *)ptr->data;
+# if 0
+ const char *name = umg->name;
+# endif
+ char old[64] = {'\0'};
+
+ STRNCPY(old, umg->idname);
+ BKE_blender_user_menus_group_idname_update(umg);
+ BKE_blender_user_menus_group_idname_update_keymap(bmain->wm.first, old, umg->idname);
+}
+
+static void rna_UserDef_usermenus_item_op_get(PointerRNA *ptr, char *value)
+{
+ bUserMenuItem_Op *umi_op = ptr->data;
+ WM_operator_py_idname(value, umi_op->op_idname);
+}
+
+static int rna_UserDef_usermenus_item_op_length(PointerRNA *ptr)
+{
+ bUserMenuItem_Op *umi_op = ptr->data;
+ char pyname[OP_MAX_TYPENAME];
+
+ WM_operator_py_idname(pyname, umi_op->op_idname);
+ return strlen(pyname);
+}
+
+static void rna_UserDef_usermenus_item_op_set(PointerRNA *ptr, const char *value)
+{
+ bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)ptr->data;
+ char idname_bl[OP_MAX_TYPENAME];
+
+ wmOperatorType *origin_ot = WM_operatortype_find(umi_op->op_idname, true);
+ wmOperatorType *ot = WM_operatortype_find(value, false);
+ if (origin_ot == ot || ot == NULL)
+ return;
+
+ WM_operator_bl_idname(idname_bl, value);
+ WM_operator_py_idname(umi_op->op_idname, value);
+
+ if (LIKELY(umi_op->ptr)) {
+ WM_operator_properties_free(umi_op->ptr);
+ MEM_freeN(umi_op->ptr);
+
+ umi_op->ptr = NULL;
+ }
+ umi_op->prop = NULL;
+
+ WM_operator_properties_alloc(&(umi_op->ptr), &(umi_op->prop), idname_bl);
+ WM_operator_properties_sanitize(umi_op->ptr, 1);
+}
+
+static void rna_UserDef_usermenu_draw(UserDef *UNUSED(userdef),
+ bContext *C,
+ uiLayout *layout,
+ bUserMenusGroup *umg)
+{
+ screen_user_menu_draw_begin(C, layout, true, umg);
+}
+
+/* UserMenu.menu_items */
+
+static void rna_UserDef_usermenu_items_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ bUserMenu *bum = (bUserMenu *)ptr->data;
+ rna_iterator_listbase_begin(iter, &bum->items, NULL);
+}
+
+static void rna_UserDef_usermenu_submenu_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ bUserMenuItem_SubMenu *umi_sm = (bUserMenuItem_SubMenu *)ptr->data;
+ rna_iterator_listbase_begin(iter, &umi_sm->items, NULL);
+}
+
+static bUserMenuItem_Op *rna_UserDef_usermenus_item_operator_get(bUserMenuItem *umi)
+{
+ if (umi->type == USER_MENU_TYPE_OPERATOR)
+ return (bUserMenuItem_Op *)umi;
+ return NULL;
+}
+
+static bUserMenuItem_Menu *rna_UserDef_usermenus_item_menu_get(bUserMenuItem *umi)
+{
+ if (umi->type == USER_MENU_TYPE_MENU)
+ return (bUserMenuItem_Menu *)umi;
+ return NULL;
+}
+
+static bUserMenuItem_Prop *rna_UserDef_usermenus_item_property_get(bUserMenuItem *umi)
+{
+ if (umi->type == USER_MENU_TYPE_PROP)
+ return (bUserMenuItem_Prop *)umi;
+ return NULL;
+}
+
+static bUserMenuItem_SubMenu *rna_UserDef_usermenus_item_submenu_get(bUserMenuItem *umi)
+{
+ if (umi->type == USER_MENU_TYPE_SUBMENU)
+ return (bUserMenuItem_SubMenu *)umi;
+ return NULL;
+}
+
+/* UserMenus.menus */
+
+static void rna_UserDef_usermenu_menus_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ UserDef *userdef = (UserDef *)ptr->data;
+ rna_iterator_listbase_begin(iter, &userdef->user_menus_group, NULL);
+}
+
int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char **UNUSED(r_info))
{
return GPU_mem_stats_supported() ? PROP_EDITABLE : 0;
@@ -5910,6 +6462,384 @@ static void rna_def_userdef_keymap(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Key Config", "The name of the active key configuration");
}
+static void rna_def_userdef_usermenus_items_subtypes(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ /* operator item */
+ srna = RNA_def_struct(brna, "um_item_op", NULL);
+ RNA_def_struct_sdna(srna, "bUserMenuItem_Op");
+ RNA_def_struct_ui_text(
+ srna, "User Menu operator item", "an item of the user menus that can store an operator");
+
+ prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "item");
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "operator", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "op_idname");
+ RNA_def_property_ui_text(prop, "operator", "the operator that will be executed");
+ RNA_def_property_string_funcs(prop,
+ "rna_UserDef_usermenus_item_op_get",
+ "rna_UserDef_usermenus_item_op_length",
+ "rna_UserDef_usermenus_item_op_set");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "prop", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "prop");
+ RNA_def_property_struct_type(prop, "OperatorProperties");
+ RNA_def_property_ui_text(prop, "properties", "properties of the operator");
+ RNA_def_property_pointer_funcs(prop, "rna_UserDef_usermenus_item_op_prop_get", NULL, NULL, NULL);
+
+ /* menu item */
+ srna = RNA_def_struct(brna, "um_item_menu", NULL);
+ RNA_def_struct_sdna(srna, "bUserMenuItem_Menu");
+ RNA_def_struct_ui_text(
+ srna, "User Menu menu item", "an item of the user menus that can store a submenu");
+
+ prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "item");
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "mt_idname");
+ RNA_def_property_ui_text(prop, "id name", "the menu id name");
+ RNA_def_struct_name_property(srna, prop);
+
+ /* prop item */
+ srna = RNA_def_struct(brna, "um_item_prop", NULL);
+ RNA_def_struct_sdna(srna, "bUserMenuItem_Prop");
+ RNA_def_struct_ui_text(
+ srna, "User Menu property item", "an item of the user menus that can store a property");
+
+ prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "item");
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "prop_id");
+ RNA_def_property_ui_text(prop, "id name", "the property id name");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "context", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "context_data_path");
+ RNA_def_property_ui_text(prop, "context", "the context data path of the property");
+ RNA_def_struct_name_property(srna, prop);
+
+ /* sub menu item */
+ srna = RNA_def_struct(brna, "um_item_submenu", NULL);
+ RNA_def_struct_sdna(srna, "bUserMenuItem_SubMenu");
+ RNA_def_struct_ui_text(
+ srna, "User Menu sub menu item", "an item of the user menus that can store a sub menu");
+
+ prop = RNA_def_property(srna, "item", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "item");
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_ui_text(prop, "name", "the sub menu name");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "items_list", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "sub menu items", "list of the items of the sub menu");
+ RNA_def_property_collection_funcs(prop,
+ "rna_UserDef_usermenu_submenu_begin",
+ "rna_iterator_listbase_next",
+ NULL,
+ "rna_iterator_listbase_get",
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+}
+
+static void rna_def_userdef_usermenu(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ /* user menu */
+ srna = RNA_def_struct(brna, "UserMenu", NULL);
+ RNA_def_struct_sdna(srna, "bUserMenu");
+ RNA_def_struct_ui_text(srna, "User Menu", "an user menu");
+
+ prop = RNA_def_property(srna, "spacetype", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "space_type");
+ RNA_def_property_ui_text(prop, "Space Type", "The Space type the user menu will be used in");
+
+ prop = RNA_def_property(srna, "context", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "context");
+ RNA_def_property_ui_text(prop, "Context", "The Context the user menu will be used in");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "menu_items", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "Items", "list of the items of the menu");
+ RNA_def_property_collection_funcs(prop,
+ "rna_UserDef_usermenu_items_begin",
+ "rna_iterator_listbase_next",
+ NULL,
+ "rna_iterator_listbase_get",
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ /* user menu item */
+ static const EnumPropertyItem um_item_type[] = {
+ {USER_MENU_TYPE_OPERATOR, "OPERATOR", 0, "Operator", "Operator"},
+ {USER_MENU_TYPE_PROP, "PROPERTY", 0, "Property", "Property"},
+ {USER_MENU_TYPE_SUBMENU, "SUBMENU", 0, "Menu", "Menu"},
+ {USER_MENU_TYPE_MENU, "MENU", 0, "Packed Menu", "Packed Menu"},
+ {USER_MENU_TYPE_SEP, "SEPARATOR", 0, "Separator", "Separator"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "UserMenuItem", NULL);
+ RNA_def_struct_sdna(srna, "bUserMenuItem");
+ RNA_def_struct_ui_text(srna, "User Menu Item", "User Menu item");
+
+ prop = RNA_def_property(srna, "next", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "next");
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "prev", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "prev");
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "parent");
+ RNA_def_property_struct_type(prop, "um_item_submenu");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "ui_name");
+ RNA_def_property_string_funcs(prop, "rna_UserDef_usermenus_item_name_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Name", "Name of the item");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ RNA_def_property_enum_items(prop, um_item_type);
+ RNA_def_property_enum_funcs(
+ prop, "rna_UserDef_usermenus_item_type_get", "rna_UserDef_usermenus_item_type_set", NULL);
+ RNA_def_property_ui_text(prop, "type", "the type of item");
+
+ prop = RNA_def_property(srna, "icon", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "icon");
+ RNA_def_property_enum_items(prop, rna_enum_icon_items);
+ RNA_def_property_enum_default(prop, ICON_NONE);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_UserDef_icons_itemf");
+ RNA_def_property_ui_text(prop, "Icon", "The item icon");
+
+ prop = RNA_def_property(srna, "is_selected", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "is_selected", 0);
+ RNA_def_property_ui_text(prop, "is selected", "is selected");
+ RNA_def_property_boolean_funcs(prop, NULL, "rna_UserDef_usermenus_active_item_set");
+
+ func = RNA_def_function(srna, "get_operator", "rna_UserDef_usermenus_item_operator_get");
+ RNA_def_function_ui_description(func, "return the operator item");
+ parm = RNA_def_pointer(func, "item_op", "um_item_op", "", "the item operator");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "get_menu", "rna_UserDef_usermenus_item_menu_get");
+ RNA_def_function_ui_description(func, "return the operator menu");
+ parm = RNA_def_pointer(func, "item_menu", "um_item_menu", "", "the item menu");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "get_property", "rna_UserDef_usermenus_item_property_get");
+ RNA_def_function_ui_description(func, "return the operator property");
+ parm = RNA_def_pointer(func, "item_prop", "um_item_prop", "", "the item property");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "get_submenu", "rna_UserDef_usermenus_item_submenu_get");
+ RNA_def_function_ui_description(func, "return the submenu");
+ parm = RNA_def_pointer(func, "item_submenu", "um_item_submenu", "", "the item submenu");
+ RNA_def_function_return(func, parm);
+
+ rna_def_userdef_usermenus_items_subtypes(brna);
+}
+
+static void rna_def_userdef_usermenusgroup(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem um_menu_type[] = {
+ {0, "LIST", 0, "List", "List"},
+ {1, "PIE", 0, "Pie", "Pie"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* user menus group */
+ srna = RNA_def_struct(brna, "UserMenusGroup", NULL);
+ RNA_def_struct_sdna(srna, "bUserMenusGroup");
+ RNA_def_struct_ui_text(srna, "User Menus Group", "A whole displayble user menu");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_ui_text(prop, "Name", "Name of the user menu group");
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_update(prop, 0, "rna_UserDef_usermenus_group_idname_set");
+
+ prop = RNA_def_property(srna, "idname", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "idname");
+ RNA_def_property_ui_text(prop, "ID Name", "ID Name of the user menu group");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ RNA_def_property_enum_items(prop, um_menu_type);
+ RNA_def_property_ui_text(prop, "menu type", "change menu type between list and pie");
+ RNA_def_property_enum_funcs(prop, NULL, "rna_UserDef_usermenus_pie_set", NULL);
+
+ prop = RNA_def_property(srna, "menus", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "UserMenu");
+ RNA_def_property_ui_text(prop, "User Menu", "list of user sub menus contained in the group");
+}
+
+static void rna_def_userdef_usermenus_editor(BlenderRNA *brna)
+{
+ PropertyRNA *prop;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ static const EnumPropertyItem um_space_default[] = {
+ {0, "NULL", 0, "None", "No spacetypes found"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem um_context_default[] = {
+ {0, "NULL", 0, "None", "No context found"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ StructRNA *srna = RNA_def_struct(brna, "PreferencesUserMenus", NULL);
+ RNA_def_struct_sdna(srna, "UserDef");
+ RNA_def_struct_nested(brna, srna, "Preferences");
+ RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
+ RNA_def_struct_ui_text(srna, "User Menus", "User Menus editor");
+
+ prop = RNA_def_property(srna, "space_selected", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "runtime.um_space_select");
+ RNA_def_property_enum_items(prop, um_space_default);
+ RNA_def_property_enum_funcs(prop,
+ "rna_UserDef_usermenus_spacetypes_get",
+ NULL,
+ "rna_UserDef_usermenus_spacetypes_itemf");
+ RNA_def_property_ui_text(prop, "space type selected", "the space type selected");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "context_selected", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "runtime.um_context_select");
+ RNA_def_property_enum_items(prop, um_context_default);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_UserDef_usermenus_contexts_itemf");
+ RNA_def_property_ui_text(prop, "context selected", "the context selected");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "active_item", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "runtime.um_item_select");
+ RNA_def_property_struct_type(prop, "UserMenuItem");
+ RNA_def_property_ui_text(prop, "item", "");
+
+ prop = RNA_def_property(srna, "active_group", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_sdna(prop, NULL, "runtime.umg_select");
+ RNA_def_property_struct_type(prop, "UserMenusGroup");
+ RNA_def_property_ui_text(prop, "active user menus group", "active user menus group");
+
+ prop = RNA_def_property(srna, "menus", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "UserMenusGroup");
+ RNA_def_property_ui_text(prop, "Menus", "list of the menus");
+ RNA_def_property_collection_funcs(prop,
+ "rna_UserDef_usermenu_menus_begin",
+ "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end",
+ "rna_iterator_listbase_get",
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ prop = RNA_def_property(srna, "expanded", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "runtime.um_expanded", 0);
+ RNA_def_property_ui_text(prop, "Items Expanded", "Expanded in the user interface");
+ RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
+
+ rna_def_userdef_usermenu(brna);
+ rna_def_userdef_usermenusgroup(brna);
+
+ // functions
+ func = RNA_def_function(srna, "get_current_menu", "rna_UserDef_usermenus_get_current");
+ RNA_def_function_ui_description(func, "get active user menu");
+ parm = RNA_def_boolean(func, "ensure", false, "ensure", "create the menu if don't exist");
+ parm = RNA_def_pointer(func, "current_menu", "UserMenu", "", "the menu");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "get_group", "rna_UserDef_usermenus_get_group");
+ RNA_def_function_ui_description(func, "get user menus group by idname");
+ parm = RNA_def_string(func, "idname", NULL, 0, "ID name", "ID name of the group");
+ parm = RNA_def_pointer(func, "menu", "UserMenusGroup", "", "the menu group");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "set_group", "rna_UserDef_usermenus_set_group");
+ RNA_def_function_ui_description(func, "set the group menu to edit");
+ parm = RNA_def_pointer(func, "new_group", "UserMenusGroup", "", "the group menu");
+
+ func = RNA_def_function(srna, "add_group", "rna_UserDef_usermenus_add_group");
+ RNA_def_function_ui_description(func, "add a group");
+
+ func = RNA_def_function(srna, "remove_group", "rna_UserDef_usermenus_remove_group");
+ RNA_def_function_ui_description(func, "remove a group");
+
+ func = RNA_def_function(srna, "items_len", "rna_UserDef_usermenus_items_length");
+ RNA_def_function_ui_description(func, "Refresh custom menu editor");
+ parm = RNA_def_int(func, "len", 0, 0, 1000, "", "the list len", 0, 1000);
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "has_item", "rna_UserDef_usermenus_has_item");
+ RNA_def_function_ui_description(func, "the current list has items");
+ parm = RNA_def_boolean(func, "has_item", false, "", "is there items in the current list ?");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "item_add", "rna_UserDef_usermenus_item_add");
+ RNA_def_function_ui_description(func, "add an item to a menu");
+ parm = RNA_def_int(func, "index", 0, -1, 1000, "index", "index to insert the item", -1, 1000);
+
+ func = RNA_def_function(srna, "item_remove", "rna_UserDef_usermenus_item_remove");
+ RNA_def_function_ui_description(func, "remove an item from a user menu");
+
+ func = RNA_def_function(srna, "item_move", "rna_UserDef_usermenus_item_move");
+ RNA_def_function_ui_description(func, "up an item");
+ parm = RNA_def_boolean(func, "up", false, "", "go up ?");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+
+ func = RNA_def_function(srna, "pie_item_add", "rna_UserDef_usermenus_pie_item_add");
+ RNA_def_function_ui_description(func, "add an item to a menu");
+ parm = RNA_def_int(func, "index", 0, -1, 1000, "index", "index to insert the item", -1, 1000);
+
+ func = RNA_def_function(srna, "draw_menu", "rna_UserDef_usermenu_draw");
+ RNA_def_function_ui_description(func, "draw items of usermenu");
+ parm = RNA_def_pointer(func, "context", "Context", "", "context");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "layout", "UILayout", "", "layout");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "menu", "UserMenusGroup", "", "menu");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+}
+
static void rna_def_userdef_filepaths(BlenderRNA *brna)
{
PropertyRNA *prop;
@@ -6248,6 +7178,12 @@ void RNA_def_userdef(BlenderRNA *brna)
RNA_def_property_pointer_funcs(prop, "rna_UserDef_keymap_get", NULL, NULL, NULL);
RNA_def_property_ui_text(prop, "Keymap", "Shortcut setup for keyboards and other input devices");
+ prop = RNA_def_property(srna, "user_menus", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "PreferencesUserMenus");
+ RNA_def_property_pointer_funcs(prop, "rna_UserDef_user_menus_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "User Menus", "User Menus Editor");
+
prop = RNA_def_property(srna, "filepaths", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "PreferencesFilePaths");
@@ -6315,6 +7251,7 @@ void RNA_def_userdef(BlenderRNA *brna)
rna_def_userdef_edit(brna);
rna_def_userdef_input(brna);
rna_def_userdef_keymap(brna);
+ rna_def_userdef_usermenus_editor(brna);
rna_def_userdef_filepaths(brna);
rna_def_userdef_system(brna);
rna_def_userdef_addon(brna);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index f53a3d6bf35..707eeef02a9 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -1841,6 +1841,8 @@ static void rna_struct_update_when_changed(bContext *C,
PointerRNA *ptr_b)
{
CollectionPropertyIterator iter;
+ if (!ptr_a->data || !ptr_b->data)
+ return;
PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type);
BLI_assert(ptr_a->type == ptr_b->type);
RNA_property_collection_begin(ptr_a, iterprop, &iter);
diff --git a/source/tools b/source/tools
-Subproject 896c5f78952adb2d091d28c65086d46992dabda
+Subproject 5cf2fc3e5dc28025394b57d8743401295528f31