PyAPI: Support KeyMap.keymap_items.find_match(...)
There was no convenient way for an add-on to show it's "user" key-map items in the preferences, "user" meaning the key-map item which is edited in the key-map editor, not the original key-map item created in `wm.keyconfigs.addon`. Add a method to key-maps to lookup a user key-map item from an add-on key-map & key-map-item. Ref !134830
This commit is contained in:
@@ -446,6 +446,16 @@ static void rna_KeyMap_item_remove(wmKeyMap *km, ReportList *reports, PointerRNA
|
||||
kmi_ptr->invalidate();
|
||||
}
|
||||
|
||||
static PointerRNA rna_KeyMap_item_find_match(
|
||||
ID *id, wmKeyMap *km_user, ReportList *reports, wmKeyMap *km_addon, wmKeyMapItem *kmi_addon)
|
||||
{
|
||||
wmKeyMapItem *kmi_match = WM_keymap_item_find_match(km_user, km_addon, kmi_addon, reports);
|
||||
if (kmi_match) {
|
||||
return RNA_pointer_create_discrete(id, &RNA_KeyMapItem, kmi_match);
|
||||
}
|
||||
return PointerRNA_NULL;
|
||||
}
|
||||
|
||||
static PointerRNA rna_KeyMap_item_find_from_operator(ID *id,
|
||||
wmKeyMap *km,
|
||||
const char *idname,
|
||||
@@ -1354,6 +1364,21 @@ void RNA_api_keymapitems(StructRNA *srna)
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_RNAPTR);
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "find_match", "rna_KeyMap_item_find_match");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS);
|
||||
parm = RNA_def_pointer(func, "keymap", "KeyMap", "", "The matching keymap");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
parm = RNA_def_pointer(func, "item", "KeyMapItem", "", "The matching keymap item");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
parm = RNA_def_pointer(func,
|
||||
"result",
|
||||
"KeyMapItem",
|
||||
"",
|
||||
"The keymap item from this keymap which matches the keymap item from the "
|
||||
"arguments passed in");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_RNAPTR);
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "match_event", "rna_KeyMap_item_match_event");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT);
|
||||
parm = RNA_def_pointer(func, "event", "Event", "", "");
|
||||
|
||||
@@ -92,6 +92,16 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap);
|
||||
wmKeyMapItem *WM_keymap_item_find_id(wmKeyMap *keymap, int id);
|
||||
bool WM_keymap_item_compare(const wmKeyMapItem *k1, const wmKeyMapItem *k2);
|
||||
|
||||
/**
|
||||
* Return the user key-map item from `km_base` based on `km_match` & `kmi_match`,
|
||||
* currently the supported use case is looking up "User" key-map items from "Add-on" key-maps.
|
||||
* Other lookups may be supported.
|
||||
*/
|
||||
wmKeyMapItem *WM_keymap_item_find_match(wmKeyMap *km_base,
|
||||
wmKeyMap *km_match,
|
||||
wmKeyMapItem *kmi_match,
|
||||
ReportList *reports);
|
||||
|
||||
/* `wm_keymap_utils.cc`. */
|
||||
|
||||
/* Wrappers for #WM_keymap_add_item. */
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_workspace.hh"
|
||||
|
||||
@@ -501,6 +502,12 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool wm_keymap_is_match(const wmKeyMap *km_a, const wmKeyMap *km_b)
|
||||
{
|
||||
return ((km_a->spaceid == km_b->spaceid) && (km_a->regionid == km_b->regionid) &&
|
||||
STREQ(km_a->idname, km_b->idname));
|
||||
}
|
||||
|
||||
static void keymap_event_set(wmKeyMapItem *kmi, const KeyMapItem_Params *params)
|
||||
{
|
||||
kmi->type = params->type;
|
||||
@@ -2105,6 +2112,17 @@ void WM_keymap_restore_to_default(wmKeyMap *keymap, wmWindowManager *wm)
|
||||
}
|
||||
}
|
||||
|
||||
const char *WM_bool_as_string(bool test)
|
||||
{
|
||||
return test ? IFACE_("ON") : IFACE_("OFF");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Keymap Queries
|
||||
* \{ */
|
||||
|
||||
wmKeyMapItem *WM_keymap_item_find_id(wmKeyMap *keymap, int id)
|
||||
{
|
||||
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
|
||||
@@ -2116,9 +2134,114 @@ wmKeyMapItem *WM_keymap_item_find_id(wmKeyMap *keymap, int id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *WM_bool_as_string(bool test)
|
||||
wmKeyMapItem *WM_keymap_item_find_match(wmKeyMap *km_base,
|
||||
wmKeyMap *km_match,
|
||||
wmKeyMapItem *kmi_match,
|
||||
ReportList *reports)
|
||||
{
|
||||
return test ? IFACE_("ON") : IFACE_("OFF");
|
||||
/* NOTE: this is called by RNA, some of the reports in this function
|
||||
* would be asserts when called from C++. */
|
||||
|
||||
if (wm_keymap_update_flag != 0) {
|
||||
/* NOTE: this could be limited to the key-maps marked for updating.
|
||||
* However #KEYMAP_UPDATE is only cleared for `wm->userconf`
|
||||
* so only check the global flag for now.
|
||||
*
|
||||
* Use a warning not an error because scripts cannot prevent other scripts from manipulating
|
||||
* key-map items, so we won't want scripts to create exceptions in unrelated scripts.
|
||||
* Ideally we could detect which key-maps have been modified. */
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"KeyMap item result may be incorrect since an update is pending, call "
|
||||
"`context.window_manager.keyconfigs.update()` to ensure matches can be found.");
|
||||
}
|
||||
|
||||
if (km_base == km_match) {
|
||||
/* We could also return `kmi_match` (it's technically correct)
|
||||
* however this is almost certainly API misuse (as it's a no-op). */
|
||||
BKE_report(reports, RPT_ERROR, "KeyMaps are equal");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *idname = km_base->idname;
|
||||
const short spaceid = km_base->spaceid;
|
||||
const short regionid = km_base->regionid;
|
||||
|
||||
if (!wm_keymap_is_match(km_base, km_match)) {
|
||||
BKE_reportf(
|
||||
reports, RPT_ERROR, "KeyMap \"%s\" doesn't match \"%s\"", idname, km_match->idname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
|
||||
wmKeyConfig *kc_active = WM_keyconfig_active(wm);
|
||||
|
||||
/* NOTE: the key-maps could store this, it would simplify checks here. */
|
||||
enum {
|
||||
KM_TYPE_UNKNOWN = 0,
|
||||
KM_TYPE_USER,
|
||||
KM_TYPE_ADDON,
|
||||
KM_TYPE_ACTIVE,
|
||||
/* No support yet for preferences. */
|
||||
} base_type = KM_TYPE_UNKNOWN,
|
||||
match_type = KM_TYPE_UNKNOWN;
|
||||
|
||||
if (km_base->flag & KEYMAP_USER) {
|
||||
if (km_base == WM_keymap_list_find(&wm->userconf->keymaps, idname, spaceid, regionid)) {
|
||||
base_type = KM_TYPE_USER;
|
||||
}
|
||||
}
|
||||
|
||||
if ((km_match->flag & KEYMAP_USER) == 0) {
|
||||
if (km_match == WM_keymap_list_find(&wm->addonconf->keymaps, idname, spaceid, regionid)) {
|
||||
match_type = KM_TYPE_ADDON;
|
||||
}
|
||||
else if (km_match == WM_keymap_list_find(&kc_active->keymaps, idname, spaceid, regionid)) {
|
||||
match_type = KM_TYPE_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if (base_type == KM_TYPE_UNKNOWN) {
|
||||
BKE_reportf(reports, RPT_ERROR, "KeyMap \"%s\" (base) must be a user keymap", idname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (match_type == KM_TYPE_UNKNOWN) {
|
||||
BKE_reportf(
|
||||
reports, RPT_ERROR, "KeyMap \"%s\" (other) must be an add-on or active keymap", idname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int kmi_index = BLI_findindex(&km_match->items, kmi_match);
|
||||
if (kmi_index == -1) {
|
||||
BKE_reportf(reports, RPT_ERROR, "KeyMap \"%s\" item not part of the keymap", idname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int kmi_id;
|
||||
if (match_type == KM_TYPE_ADDON) {
|
||||
/* Perform the following lookup that calculates the ID that *would* be used
|
||||
* if the user key-map was re-created, see: #WM_keymap_item_restore_to_default.
|
||||
*
|
||||
* Find the index of the key-map item and add this to the `defaultmap`'s key-map index
|
||||
* since this is how the "user" key-map ID's are generated.
|
||||
*
|
||||
* This is needed so add-ons can show the user key-map items in preferences. */
|
||||
|
||||
wmKeyMap *defaultmap = wm_keymap_preset(wm, kc_active, km_base);
|
||||
if (defaultmap == nullptr) {
|
||||
/* This should practically never fail, it could be caused by failure
|
||||
* to refresh the user key-map after manipulating the add-on key-map. */
|
||||
return nullptr;
|
||||
}
|
||||
kmi_id = defaultmap->kmi_id + kmi_index + 1;
|
||||
}
|
||||
else {
|
||||
kmi_id = kmi_match->id;
|
||||
}
|
||||
|
||||
/* Returning null here isn't an error because it's possible there is no match. */
|
||||
return WM_keymap_item_find_id(km_base, kmi_id);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
Reference in New Issue
Block a user