diff --git a/scripts/modules/bpy/utils/__init__.py b/scripts/modules/bpy/utils/__init__.py index 17beb101993..94b7394971b 100644 --- a/scripts/modules/bpy/utils/__init__.py +++ b/scripts/modules/bpy/utils/__init__.py @@ -292,7 +292,7 @@ def load_scripts(*, reload_scripts=False, refresh_scripts=False, extensions=True # however reloading scripts re-enable all add-ons immediately (which may inspect key-maps). # For this reason it's important to update key-maps which will have been tagged to update. # Without this, add-on register functions accessing key-map properties can crash, see: #111702. - _bpy.context.window_manager.keyconfigs.update() + _bpy.context.window_manager.keyconfigs.update(keep_properties=True) from bpy_restrict_state import RestrictBlend @@ -308,6 +308,11 @@ def load_scripts(*, reload_scripts=False, refresh_scripts=False, extensions=True for mod in modules_from_path(path, loaded_modules): test_register(mod) + if reload_scripts: + # Update key-maps for key-map items referencing operators defined in "startup". + # Without this, key-map items wont be set properly, see: #113309. + _bpy.context.window_manager.keyconfigs.update() + if extensions: load_scripts_extensions(reload_scripts=reload_scripts) diff --git a/source/blender/makesrna/intern/rna_wm_api.cc b/source/blender/makesrna/intern/rna_wm_api.cc index 8b17a550c8d..eb6b375d8a7 100644 --- a/source/blender/makesrna/intern/rna_wm_api.cc +++ b/source/blender/makesrna/intern/rna_wm_api.cc @@ -539,9 +539,9 @@ static PointerRNA rna_KeyConfig_find_item_from_operator(wmWindowManager *wm, return kmi_ptr; } -static void rna_KeyConfig_update(wmWindowManager *wm) +static void rna_KeyConfig_update(wmWindowManager *wm, bool keep_properties) { - WM_keyconfig_update(wm); + WM_keyconfig_update_ex(wm, keep_properties); } /* popup menu wrapper */ @@ -1335,7 +1335,13 @@ void RNA_api_keyconfigs(StructRNA *srna) RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_RNAPTR); RNA_def_function_return(func, parm); - RNA_def_function(srna, "update", "rna_KeyConfig_update"); /* WM_keyconfig_update */ + func = RNA_def_function(srna, "update", "rna_KeyConfig_update"); /* WM_keyconfig_update */ + RNA_def_boolean( + func, + "keep_properties", + false, + "Keep Properties", + "Operator properties are kept to allow the operators to be registered again in the future"); } #endif diff --git a/source/blender/windowmanager/WM_keymap.hh b/source/blender/windowmanager/WM_keymap.hh index fc14d25f87f..c8621fc43a6 100644 --- a/source/blender/windowmanager/WM_keymap.hh +++ b/source/blender/windowmanager/WM_keymap.hh @@ -27,6 +27,12 @@ void WM_keyconfig_free(wmKeyConfig *keyconf); void WM_keyconfig_set_active(wmWindowManager *wm, const char *idname); +/** + * \param keep_properties: When true, the properties for operators which cannot be found are kept. + * This is needed for operator reloading that validates key-map items for operators that may have + * their operators loaded back in the future, see: #113309. + */ +void WM_keyconfig_update_ex(wmWindowManager *wm, bool keep_properties); void WM_keyconfig_update(wmWindowManager *wm); void WM_keyconfig_update_tag(wmKeyMap *keymap, wmKeyMapItem *kmi); void WM_keyconfig_update_operatortype(); diff --git a/source/blender/windowmanager/intern/wm_keymap.cc b/source/blender/windowmanager/intern/wm_keymap.cc index bef374f124d..f8eeb6ef1d7 100644 --- a/source/blender/windowmanager/intern/wm_keymap.cc +++ b/source/blender/windowmanager/intern/wm_keymap.cc @@ -94,6 +94,17 @@ static void wm_keymap_item_free_data(wmKeyMapItem *kmi) } } +static void wm_keymap_item_clear_runtime(wmKeyMapItem *kmi) +{ + IDProperty *properties = kmi->properties; + kmi->properties = nullptr; + if (kmi->ptr) { + kmi->ptr->data = nullptr; + } + wm_keymap_item_free_data(kmi); + kmi->properties = properties; +} + static void wm_keymap_item_properties_set(wmKeyMapItem *kmi) { WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname); @@ -107,7 +118,7 @@ static void wm_keymap_item_properties_set(wmKeyMapItem *kmi) * Similar to #wm_keymap_item_properties_set * but checks for the #wmOperatorType having changed, see #38042. */ -static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi) +static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi, const bool keep_properties) { if (kmi->idname[0] == 0) { BLI_assert(kmi->ptr == nullptr); @@ -137,25 +148,31 @@ static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi) } } else { - /* zombie keymap item */ - wm_keymap_item_free_data(kmi); + /* Zombie key-map item. */ + if (keep_properties) { + wm_keymap_item_clear_runtime(kmi); + } + else { + wm_keymap_item_free_data(kmi); + } } } } -static void wm_keymap_item_properties_update_ot_from_list(ListBase *km_lb) +static void wm_keymap_item_properties_update_ot_from_list(ListBase *km_lb, + const bool keep_properties) { LISTBASE_FOREACH (wmKeyMap *, km, km_lb) { LISTBASE_FOREACH (wmKeyMapItem *, kmi, &km->items) { - wm_keymap_item_properties_update_ot(kmi); + wm_keymap_item_properties_update_ot(kmi, keep_properties); } LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &km->diff_items) { if (kmdi->add_item) { - wm_keymap_item_properties_update_ot(kmdi->add_item); + wm_keymap_item_properties_update_ot(kmdi->add_item, keep_properties); } if (kmdi->remove_item) { - wm_keymap_item_properties_update_ot(kmdi->remove_item); + wm_keymap_item_properties_update_ot(kmdi->remove_item, keep_properties); } } } @@ -1873,6 +1890,11 @@ static wmKeyMap *wm_keymap_preset(wmWindowManager *wm, wmKeyMap *km) } void WM_keyconfig_update(wmWindowManager *wm) +{ + WM_keyconfig_update_ex(wm, false); +} + +void WM_keyconfig_update_ex(wmWindowManager *wm, bool keep_properties) { bool compat_update = false; @@ -1883,9 +1905,9 @@ void WM_keyconfig_update(wmWindowManager *wm) if (wm_keymap_update_flag & WM_KEYMAP_UPDATE_OPERATORTYPE) { /* One or more operator-types have been removed, this won't happen often * but when it does we have to check _every_ key-map item. */ - wm_keymap_item_properties_update_ot_from_list(&U.user_keymaps); + wm_keymap_item_properties_update_ot_from_list(&U.user_keymaps, keep_properties); LISTBASE_FOREACH (wmKeyConfig *, kc, &wm->keyconfigs) { - wm_keymap_item_properties_update_ot_from_list(&kc->keymaps); + wm_keymap_item_properties_update_ot_from_list(&kc->keymaps, keep_properties); } wm_keymap_update_flag &= ~WM_KEYMAP_UPDATE_OPERATORTYPE; }