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
path: root/source
diff options
context:
space:
mode:
authorCampbell Barton <campbell@blender.org>2022-05-20 07:30:17 +0300
committerCampbell Barton <campbell@blender.org>2022-05-26 05:16:35 +0300
commit3f3d82cfe9cefe4bfd9da3d283dec4a1923ec22d (patch)
tree532de0110686d29f63c550c4784f8d2acc86e110 /source
parent11480763b62e6ca72ae869391b8e5495e57410a6 (diff)
UI support for showing candidates for string properties
Currently strings are used for cases where a list of identifiers would be useful to show. Add support for string properties to reference a callback to populate candidates to show when editing a string. The user isn't prevented from typing in text not found in this list, it's just useful as a reference. Support for expanding the following strings has been added: - Operator, menu & panel identifiers in the keymap editor. - WM operators that reference data-paths expand using the Python-consoles auto-complete functionality. - Names of keying sets for insert/delete keyframe operators. Details: - `bpy.props.StringProperty` takes an option `search` callback. - A new string callback has been added, set via `RNA_def_property_string_search_func` or `RNA_def_property_string_search_func_runtime`. - Addresses usability issue highlighted by T89560, where setting keying set identifiers as strings isn't practical. - Showing additional right-aligned text in the search results is supported but disabled by default as the text is too cramped in most string search popups where the feature would make sense. It could be enabled as part of other layout tweaks. Reviewed By: brecht Ref D14986
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_global.h1
-rw-r--r--source/blender/editors/animation/keyframing.c4
-rw-r--r--source/blender/editors/animation/keyingsets.c66
-rw-r--r--source/blender/editors/include/ED_keyframing.h13
-rw-r--r--source/blender/editors/interface/interface_layout.c39
-rw-r--r--source/blender/editors/interface/interface_utils.cc166
-rw-r--r--source/blender/makesrna/RNA_access.h15
-rw-r--r--source/blender/makesrna/RNA_define.h6
-rw-r--r--source/blender/makesrna/RNA_types.h49
-rw-r--r--source/blender/makesrna/intern/makesrna.c38
-rw-r--r--source/blender/makesrna/intern/rna_access.c28
-rw-r--r--source/blender/makesrna/intern/rna_define.c39
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h9
-rw-r--r--source/blender/makesrna/intern/rna_wm.c3
-rw-r--r--source/blender/python/intern/bpy_props.c230
-rw-r--r--source/blender/windowmanager/WM_api.h22
-rw-r--r--source/blender/windowmanager/intern/wm_menu_type.c18
-rw-r--r--source/blender/windowmanager/intern/wm_operator_type.c21
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c23
-rw-r--r--source/blender/windowmanager/intern/wm_panel_type.c18
20 files changed, 743 insertions, 65 deletions
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 06feb07aef2..96b6f7a53b0 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -81,6 +81,7 @@ typedef struct Global {
* * 1 - 30: EEVEE debug/stats values (01/2018).
* * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021).
* * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014).
+ * * 102: Enable extra items in string search UI (05/2022).
* * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019).
* * 777: Enable UI node panel's sockets polling (11/2011).
* * 799: Enable some mysterious new depsgraph behavior (05/2015).
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 14a3b958ea6..941125b9ad5 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -2051,6 +2051,8 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot)
/* keyingset to use (idname) */
prop = RNA_def_string(
ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use");
+ RNA_def_property_string_search_func_runtime(
+ prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION);
RNA_def_property_flag(prop, PROP_HIDDEN);
ot->prop = prop;
}
@@ -2246,6 +2248,8 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot)
/* keyingset to use (idname) */
prop = RNA_def_string(
ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use");
+ RNA_def_property_string_search_func_runtime(
+ prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION);
RNA_def_property_flag(prop, PROP_HIDDEN);
ot->prop = prop;
}
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index 6fcdd21bad8..97b81277008 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -708,6 +708,72 @@ KeyingSet *ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *tra
return ANIM_builtin_keyingset_get_named(NULL, transformKSName);
}
+static void anim_keyingset_visit_for_search_impl(const bContext *C,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data,
+ const bool use_poll)
+{
+ /* Poll requires context. */
+ if (use_poll && (C == NULL)) {
+ return;
+ }
+
+ Scene *scene = C ? CTX_data_scene(C) : NULL;
+ KeyingSet *ks;
+
+ /* Active Keying Set. */
+ if (!use_poll || (scene && scene->active_keyingset)) {
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = "__ACTIVE__";
+ visit_params.info = "Active Keying Set";
+ visit_fn(visit_user_data, &visit_params);
+ }
+
+ /* User-defined Keying Sets. */
+ if (scene && scene->keyingsets.first) {
+ for (ks = scene->keyingsets.first; ks; ks = ks->next) {
+ if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) {
+ continue;
+ }
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = ks->idname;
+ visit_params.info = ks->name;
+ visit_fn(visit_user_data, &visit_params);
+ }
+ }
+
+ /* Builtin Keying Sets. */
+ for (ks = builtin_keyingsets.first; ks; ks = ks->next) {
+ if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) {
+ continue;
+ }
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = ks->idname;
+ visit_params.info = ks->name;
+ visit_fn(visit_user_data, &visit_params);
+ }
+}
+
+void ANIM_keyingset_visit_for_search(const bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ const char *UNUSED(edit_text),
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, false);
+}
+
+void ANIM_keyingset_visit_for_search_no_poll(const bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ const char *UNUSED(edit_text),
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, true);
+}
+
/* Menu of All Keying Sets ----------------------------- */
const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C,
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 6a730225da9..8c0147612fb 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -351,6 +351,19 @@ int ANIM_scene_get_keyingset_index(struct Scene *scene, struct KeyingSet *ks);
struct KeyingSet *ANIM_get_keyingset_for_autokeying(const struct Scene *scene,
const char *transformKSName);
+void ANIM_keyingset_visit_for_search(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
+
+void ANIM_keyingset_visit_for_search_no_poll(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
/**
* Dynamically populate an enum of Keying Sets.
*/
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 933d7efb4d6..3465373c85d 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2338,7 +2338,14 @@ void uiItemFullR(uiLayout *layout,
/* property with separate label */
else if (ELEM(type, PROP_ENUM, PROP_STRING, PROP_POINTER)) {
but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag);
- but = ui_but_add_search(but, ptr, prop, NULL, NULL, false);
+ bool results_are_suggestions = false;
+ if (type == PROP_STRING) {
+ const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag(prop);
+ if (search_flag & PROP_STRING_SEARCH_SUGGESTION) {
+ results_are_suggestions = true;
+ }
+ }
+ but = ui_but_add_search(but, ptr, prop, NULL, NULL, results_are_suggestions);
if (layout->redalert) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
@@ -2711,11 +2718,16 @@ uiBut *ui_but_add_search(uiBut *but,
PropertyRNA *prop,
PointerRNA *searchptr,
PropertyRNA *searchprop,
- bool results_are_suggestions)
+ const bool results_are_suggestions)
{
/* for ID's we do automatic lookup */
+ bool has_search_fn = false;
+
PointerRNA sptr;
if (!searchprop) {
+ if (RNA_property_type(prop) == PROP_STRING) {
+ has_search_fn = (RNA_property_string_search_flag(prop) != 0);
+ }
if (RNA_property_type(prop) == PROP_POINTER) {
StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
search_id_collection(ptype, &sptr, &searchprop);
@@ -2724,14 +2736,18 @@ uiBut *ui_but_add_search(uiBut *but,
}
/* turn button into search button */
- if (searchprop) {
+ if (has_search_fn || searchprop) {
uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__);
uiButSearch *search_but;
but = ui_but_change_type(but, UI_BTYPE_SEARCH_MENU);
search_but = (uiButSearch *)but;
- search_but->rnasearchpoin = *searchptr;
- search_but->rnasearchprop = searchprop;
+
+ if (searchptr) {
+ search_but->rnasearchpoin = *searchptr;
+ search_but->rnasearchprop = searchprop;
+ }
+
but->hardmax = MAX2(but->hardmax, 256.0f);
but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT;
if (RNA_property_is_unlink(prop)) {
@@ -2740,8 +2756,17 @@ uiBut *ui_but_add_search(uiBut *but,
coll_search->target_ptr = *ptr;
coll_search->target_prop = prop;
- coll_search->search_ptr = *searchptr;
- coll_search->search_prop = searchprop;
+
+ if (searchptr) {
+ coll_search->search_ptr = *searchptr;
+ coll_search->search_prop = searchprop;
+ }
+ else {
+ /* Rely on `has_search_fn`. */
+ coll_search->search_ptr = PointerRNA_NULL;
+ coll_search->search_prop = NULL;
+ }
+
coll_search->search_but = but;
coll_search->butstore_block = but->block;
coll_search->butstore = UI_butstore_create(coll_search->butstore_block);
diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc
index 993ccdf92f7..b7ca2d9aa11 100644
--- a/source/blender/editors/interface/interface_utils.cc
+++ b/source/blender/editors/interface/interface_utils.cc
@@ -24,6 +24,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_report.h"
@@ -516,71 +517,136 @@ void ui_rna_collection_search_update_fn(
StringSearch *search = skip_filter ? nullptr : BLI_string_search_new();
- /* build a temporary list of relevant items first */
- int item_index = 0;
- RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
+ if (data->search_prop != nullptr) {
+ /* build a temporary list of relevant items first */
+ int item_index = 0;
+ RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
- if (flag & PROP_ID_SELF_CHECK) {
- if (itemptr.data == data->target_ptr.owner_id) {
- continue;
+ if (flag & PROP_ID_SELF_CHECK) {
+ if (itemptr.data == data->target_ptr.owner_id) {
+ continue;
+ }
}
- }
- /* use filter */
- if (is_ptr_target) {
- if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
- continue;
+ /* use filter */
+ if (is_ptr_target) {
+ if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
+ continue;
+ }
}
- }
- int name_prefix_offset = 0;
- int iconid = ICON_NONE;
- bool has_sep_char = false;
- const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
+ int name_prefix_offset = 0;
+ int iconid = ICON_NONE;
+ bool has_sep_char = false;
+ const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
- if (is_id) {
- iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false);
- if (!ELEM(iconid, 0, ICON_BLANK1)) {
- has_id_icon = true;
- }
+ if (is_id) {
+ iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false);
+ if (!ELEM(iconid, 0, ICON_BLANK1)) {
+ has_id_icon = true;
+ }
- if (requires_exact_data_name) {
- name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
+ if (requires_exact_data_name) {
+ name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
+ }
+ else {
+ const ID *id = static_cast<ID *>(itemptr.data);
+ BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset);
+ BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
+ "Name string buffer should be big enough to hold full UI ID name");
+ name = name_buf;
+ has_sep_char = ID_IS_LINKED(id);
+ }
}
else {
- const ID *id = static_cast<ID *>(itemptr.data);
- BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset);
- BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
- "Name string buffer should be big enough to hold full UI ID name");
- name = name_buf;
- has_sep_char = ID_IS_LINKED(id);
+ name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
}
- }
- else {
- name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
- }
- if (name) {
- CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__);
- cis->data = itemptr.data;
- cis->name = BLI_strdup(name);
- cis->index = item_index;
- cis->iconid = iconid;
- cis->is_id = is_id;
- cis->name_prefix_offset = name_prefix_offset;
- cis->has_sep_char = has_sep_char;
- if (!skip_filter) {
- BLI_string_search_add(search, name, cis, 0);
+ if (name) {
+ CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__);
+ cis->data = itemptr.data;
+ cis->name = BLI_strdup(name);
+ cis->index = item_index;
+ cis->iconid = iconid;
+ cis->is_id = is_id;
+ cis->name_prefix_offset = name_prefix_offset;
+ cis->has_sep_char = has_sep_char;
+ if (!skip_filter) {
+ BLI_string_search_add(search, name, cis, 0);
+ }
+ BLI_addtail(items_list, cis);
+ if (name != name_buf) {
+ MEM_freeN(name);
+ }
}
- BLI_addtail(items_list, cis);
- if (name != name_buf) {
- MEM_freeN(name);
+
+ item_index++;
+ }
+ RNA_PROP_END;
+ }
+ else {
+ BLI_assert(RNA_property_type(data->target_prop) == PROP_STRING);
+ const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag(
+ data->target_prop);
+ BLI_assert(search_flag & PROP_STRING_SEARCH_SUPPORTED);
+
+ struct SearchVisitUserData {
+ StringSearch *search;
+ bool skip_filter;
+ int item_index;
+ ListBase *items_list;
+ const char *func_id;
+ } user_data = {nullptr};
+
+ user_data.search = search;
+ user_data.skip_filter = skip_filter;
+ user_data.items_list = items_list;
+ user_data.func_id = __func__;
+
+ RNA_property_string_search(
+ C,
+ &data->target_ptr,
+ data->target_prop,
+ str,
+ [](void *user_data, const StringPropertySearchVisitParams *visit_params) {
+ const bool show_extra_info = (G.debug_value == 102);
+
+ SearchVisitUserData *search_data = (struct SearchVisitUserData *)user_data;
+ CollItemSearch *cis = MEM_cnew<CollItemSearch>(search_data->func_id);
+ cis->data = nullptr;
+ if (visit_params->info && show_extra_info) {
+ cis->name = BLI_sprintfN(
+ "%s" UI_SEP_CHAR_S "%s", visit_params->text, visit_params->info);
+ }
+ else {
+ cis->name = BLI_strdup(visit_params->text);
+ }
+ cis->index = search_data->item_index;
+ cis->iconid = ICON_NONE;
+ cis->is_id = false;
+ cis->name_prefix_offset = 0;
+ cis->has_sep_char = visit_params->info != nullptr;
+ if (!search_data->skip_filter) {
+ BLI_string_search_add(search_data->search, visit_params->text, cis, 0);
+ }
+ BLI_addtail(search_data->items_list, cis);
+ search_data->item_index++;
+ },
+ (void *)&user_data);
+
+ if (search_flag & PROP_STRING_SEARCH_SORT) {
+ BLI_listbase_sort(items_list, [](const void *a_, const void *b_) -> int {
+ const CollItemSearch *cis_a = (const CollItemSearch *)a_;
+ const CollItemSearch *cis_b = (const CollItemSearch *)b_;
+ return BLI_strcasecmp_natural(cis_a->name, cis_b->name);
+ });
+ int i = 0;
+ LISTBASE_FOREACH (CollItemSearch *, cis, items_list) {
+ cis->index = i;
+ i++;
}
}
-
- item_index++;
}
- RNA_PROP_END;
if (skip_filter) {
LISTBASE_FOREACH (CollItemSearch *, cis, items_list) {
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index f0a60929431..4267ce47d81 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -358,6 +358,21 @@ char *RNA_property_string_get_alloc(
PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len);
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value);
void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len);
+
+eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop);
+/**
+ * Search candidates for string `prop` by calling `visit_fn` with each string.
+ * Typically these strings are collected in `visit_user_data` in a format defined by the caller.
+ *
+ * See #PropStringSearchFunc for details.
+ */
+void RNA_property_string_search(const struct bContext *C,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
+
/**
* \return the length without `\0` terminator.
*/
diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h
index 13a5ec66a16..0389d1b3b16 100644
--- a/source/blender/makesrna/RNA_define.h
+++ b/source/blender/makesrna/RNA_define.h
@@ -446,6 +446,9 @@ void RNA_def_property_string_funcs(PropertyRNA *prop,
const char *get,
const char *length,
const char *set);
+void RNA_def_property_string_search_func(PropertyRNA *prop,
+ const char *search,
+ eStringPropertySearchFlag search_flag);
void RNA_def_property_pointer_funcs(
PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll);
void RNA_def_property_collection_funcs(PropertyRNA *prop,
@@ -490,6 +493,9 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
StringPropertyGetFunc getfunc,
StringPropertyLengthFunc lengthfunc,
StringPropertySetFunc setfunc);
+void RNA_def_property_string_search_func_runtime(PropertyRNA *prop,
+ StringPropertySearchFunc search_fn,
+ eStringPropertySearchFlag search_flag);
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index 3ebcae5f947..75b514cdb13 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -515,6 +515,55 @@ typedef int (*StringPropertyLengthFunc)(struct PointerRNA *ptr, struct PropertyR
typedef void (*StringPropertySetFunc)(struct PointerRNA *ptr,
struct PropertyRNA *prop,
const char *value);
+
+typedef struct StringPropertySearchVisitParams {
+ /** Text being searched for (never NULL). */
+ const char *text;
+ /** Additional information to display (optional, may be NULL). */
+ const char *info;
+} StringPropertySearchVisitParams;
+
+typedef enum eStringPropertySearchFlag {
+ /**
+ * Used so the result of #RNA_property_string_search_flag can be used to check
+ * if search is supported.
+ */
+ PROP_STRING_SEARCH_SUPPORTED = (1 << 0),
+ /** Items resulting from the search must be sorted. */
+ PROP_STRING_SEARCH_SORT = (1 << 1),
+ /**
+ * Allow members besides the ones listed to be entered.
+ *
+ * \warning disabling this options causes the search callback to run on redraw and should
+ * only be enabled this doesn't cause performance issues.
+ */
+ PROP_STRING_SEARCH_SUGGESTION = (1 << 2),
+} eStringPropertySearchFlag;
+
+/**
+ * Visit string search candidates, `text` may be freed once this callback has finished,
+ * so references to it should not be held.
+ */
+typedef void (*StringPropertySearchVisitFunc)(void *visit_user_data,
+ const StringPropertySearchVisitParams *params);
+/**
+ * \param C: context, may be NULL (in this case all available items should be shown).
+ * \param ptr: RNA pointer.
+ * \param prop: RNA property. This must have it's #StringPropertyRNA.search callback set,
+ * to check this use `RNA_property_string_search_flag(prop) & PROP_STRING_SEARCH_SUPPORTED`.
+ * \param edit_text: Optionally use the string being edited by the user as a basis
+ * for the search results (auto-complete Python attributes for e.g.).
+ * \param visit_fn: This function is called with every search candidate and is typically
+ * responsible for storing the search results.
+ * \param visit_user_data: Caller defined data, passed to `visit_fn`.
+ */
+typedef void (*StringPropertySearchFunc)(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
+
typedef int (*EnumPropertyGetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop);
typedef void (*EnumPropertySetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop, int value);
/* same as PropEnumItemFunc */
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index c3ca57b38bf..400944d60d4 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -1039,6 +1039,38 @@ static void rna_clamp_value(FILE *f, PropertyRNA *prop, int array)
}
}
+static char *rna_def_property_search_func(FILE *f,
+ StructRNA *srna,
+ PropertyRNA *prop,
+ PropertyDefRNA *UNUSED(dp),
+ const char *manualfunc)
+{
+ char *func;
+
+ if (prop->flag & PROP_IDPROPERTY && manualfunc == NULL) {
+ return NULL;
+ }
+ if (!manualfunc) {
+ return NULL;
+ }
+
+ func = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "search");
+
+ fprintf(f,
+ "void %s("
+ "const bContext *C, "
+ "PointerRNA *ptr, "
+ "PropertyRNA *prop, "
+ "const char *edit_text, "
+ "StringPropertySearchVisitFunc visit_fn, "
+ "void *visit_user_data)\n",
+ func);
+ fprintf(f, "{\n");
+ fprintf(f, "\n %s(C, ptr, prop, edit_text, visit_fn, visit_user_data);\n", manualfunc);
+ fprintf(f, "}\n\n");
+ return func;
+}
+
static char *rna_def_property_set_func(
FILE *f, StructRNA *srna, PropertyRNA *prop, PropertyDefRNA *dp, const char *manualfunc)
{
@@ -1895,6 +1927,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp)
sprop->length = (void *)rna_def_property_length_func(
f, srna, prop, dp, (const char *)sprop->length);
sprop->set = (void *)rna_def_property_set_func(f, srna, prop, dp, (const char *)sprop->set);
+ sprop->search = (void *)rna_def_property_search_func(
+ f, srna, prop, dp, (const char *)sprop->search);
break;
}
case PROP_POINTER: {
@@ -4081,13 +4115,15 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
case PROP_STRING: {
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
fprintf(f,
- "\t%s, %s, %s, %s, %s, %s, %d, ",
+ "\t%s, %s, %s, %s, %s, %s, %s, %d, %d, ",
rna_function_string(sprop->get),
rna_function_string(sprop->length),
rna_function_string(sprop->set),
rna_function_string(sprop->get_ex),
rna_function_string(sprop->length_ex),
rna_function_string(sprop->set_ex),
+ rna_function_string(sprop->search),
+ (int)sprop->search_flag,
sprop->maxlength);
rna_print_c_string(f, sprop->defaultvalue);
fprintf(f, "\n");
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index ccb7e1c69bc..c8cb0b7ffb8 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -3369,6 +3369,34 @@ int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *pro
return strlen(sprop->defaultvalue);
}
+eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop)
+{
+ StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
+ if (prop->magic != RNA_MAGIC) {
+ return false;
+ }
+ BLI_assert(RNA_property_type(prop) == PROP_STRING);
+ if (sprop->search) {
+ BLI_assert(sprop->search_flag & PROP_STRING_SEARCH_SUPPORTED);
+ }
+ else {
+ BLI_assert(sprop->search_flag == 0);
+ }
+ return sprop->search_flag;
+}
+
+void RNA_property_string_search(const bContext *C,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ BLI_assert(RNA_property_string_search_flag(prop) & PROP_STRING_SEARCH_SUPPORTED);
+ StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
+ sprop->search(C, ptr, prop, edit_text, visit_fn, visit_user_data);
+}
+
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
{
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index 1e950b883ab..dfb551fcb05 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -3316,6 +3316,33 @@ void RNA_def_property_string_funcs(PropertyRNA *prop,
}
}
+void RNA_def_property_string_search_func(PropertyRNA *prop,
+ const char *search,
+ const eStringPropertySearchFlag search_flag)
+{
+ StructRNA *srna = DefRNA.laststruct;
+
+ if (!DefRNA.preprocess) {
+ CLOG_ERROR(&LOG, "only during preprocessing.");
+ return;
+ }
+
+ switch (prop->type) {
+ case PROP_STRING: {
+ StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
+ sprop->search = (StringPropertySearchFunc)search;
+ if (search != NULL) {
+ sprop->search_flag = search_flag | PROP_STRING_SEARCH_SUPPORTED;
+ }
+ break;
+ }
+ default:
+ CLOG_ERROR(&LOG, "\"%s.%s\", type is not string.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ break;
+ }
+}
+
void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
StringPropertyGetFunc getfunc,
StringPropertyLengthFunc lengthfunc,
@@ -3343,6 +3370,18 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
}
}
+void RNA_def_property_string_search_func_runtime(PropertyRNA *prop,
+ StringPropertySearchFunc search_fn,
+ const eStringPropertySearchFlag search_flag)
+{
+ StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
+
+ sprop->search = search_fn;
+ if (search_fn != NULL) {
+ sprop->search_flag = search_flag | PROP_STRING_SEARCH_SUPPORTED;
+ }
+}
+
void RNA_def_property_pointer_funcs(
PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll)
{
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index a5b719cf753..4f88959b5ba 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -440,6 +440,15 @@ typedef struct StringPropertyRNA {
PropStringLengthFuncEx length_ex;
PropStringSetFuncEx set_ex;
+ /**
+ * Optional callback to list candidates for a string.
+ * This is only for use as suggestions in UI, other values may be assigned.
+ *
+ * \note The callback type is public, hence the difference in naming convention.
+ */
+ StringPropertySearchFunc search;
+ eStringPropertySearchFlag search_flag;
+
int maxlength; /* includes string terminator! */
const char *defaultvalue;
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index ac1b7e53a9b..c80d0e2da39 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -2599,6 +2599,9 @@ static void rna_def_keyconfig(BlenderRNA *brna)
"rna_wmKeyMapItem_idname_get",
"rna_wmKeyMapItem_idname_length",
"rna_wmKeyMapItem_idname_set");
+ RNA_def_property_string_search_func(prop,
+ "WM_operatortype_idname_visit_for_search",
+ PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION);
RNA_def_struct_name_property(srna, prop);
RNA_def_property_update(prop, 0, "rna_KeyMapItem_update");
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index a6aa1f46b0c..f4ebc68b5ef 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -191,6 +191,17 @@ static const EnumPropertyItem property_subtype_array_items[] = {
"'XYZ', 'XYZ_LENGTH', 'COLOR_GAMMA', 'COORDINATES', 'LAYER', 'LAYER_MEMBER', 'NONE'].\n" \
" :type subtype: string\n"
+static const EnumPropertyItem property_string_search_options_items[] = {
+ {PROP_STRING_SEARCH_SORT, "SORT", 0, "Sort Search Results", ""},
+ {PROP_STRING_SEARCH_SUGGESTION,
+ "SUGGESTION",
+ 0,
+ "Suggestion",
+ "Search results are suggestions (other values may be entered)"},
+
+ {0, NULL, 0, NULL, NULL},
+};
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -257,6 +268,11 @@ struct BPyPropStore {
/** Wrap: #RNA_def_property_poll_runtime */
PyObject *poll_fn;
} pointer_data;
+ /** #PROP_STRING type. */
+ struct {
+ /** Wrap: #RNA_def_property_string_search_func_runtime */
+ PyObject *search_fn;
+ } string_data;
};
} py_data;
};
@@ -1672,6 +1688,163 @@ static void bpy_prop_string_set_fn(struct PointerRNA *ptr,
}
}
+static bool bpy_prop_string_visit_fn_call(PyObject *py_func,
+ PyObject *item,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ const char *text;
+ const char *info = NULL;
+
+ if (PyTuple_CheckExact(item)) {
+ /* Positional only. */
+ static const char *_keywords[] = {
+ "",
+ "",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {
+ "s" /* `text` */
+ "s" /* `info` */
+ ":search",
+ _keywords,
+ 0,
+ };
+ if (!_PyArg_ParseTupleAndKeywordsFast(item, NULL, &_parser, &text, &info)) {
+ PyC_Err_PrintWithFunc(py_func);
+ return false;
+ }
+ }
+ else {
+ text = PyUnicode_AsUTF8(item);
+ if (UNLIKELY(text == NULL)) {
+ PyErr_Clear();
+ PyErr_Format(PyExc_TypeError,
+ "expected sequence of strings or tuple pairs of strings, not %.200s",
+ Py_TYPE(item)->tp_name);
+ PyC_Err_PrintWithFunc(py_func);
+ return false;
+ }
+ }
+
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = text;
+ visit_params.info = info;
+ visit_fn(visit_user_data, &visit_params);
+ return true;
+}
+
+static void bpy_prop_string_visit_for_search_fn(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ struct BPyPropStore *prop_store = RNA_property_py_data_get(prop);
+ PyObject *py_func;
+ PyObject *args;
+ PyObject *self;
+ PyObject *ret;
+ PyGILState_STATE gilstate;
+ PyObject *py_edit_text;
+
+ BLI_assert(prop_store != NULL);
+
+ if (C) {
+ bpy_context_set((struct bContext *)C, &gilstate);
+ }
+ else {
+ gilstate = PyGILState_Ensure();
+ }
+
+ py_func = prop_store->py_data.string_data.search_fn;
+
+ args = PyTuple_New(3);
+ self = pyrna_struct_as_instance(ptr);
+ PyTuple_SET_ITEM(args, 0, self);
+
+ Py_INCREF(bpy_context_module);
+ PyTuple_SET_ITEM(args, 1, (PyObject *)bpy_context_module);
+
+ py_edit_text = PyUnicode_FromString(edit_text);
+ PyTuple_SET_ITEM(args, 2, py_edit_text);
+
+ ret = PyObject_CallObject(py_func, args);
+
+ Py_DECREF(args);
+
+ if (ret == NULL) {
+ PyC_Err_PrintWithFunc(py_func);
+ }
+ else {
+ if (PyIter_Check(ret)) {
+ /* Iterators / generator types. */
+ PyObject *it;
+ PyObject *(*iternext)(PyObject *);
+ it = PyObject_GetIter(ret);
+ if (it == NULL) {
+ PyC_Err_PrintWithFunc(py_func);
+ }
+ else {
+ iternext = *Py_TYPE(it)->tp_iternext;
+ for (;;) {
+ PyObject *py_text = iternext(it);
+ if (py_text == NULL) {
+ break;
+ }
+ const bool ok = bpy_prop_string_visit_fn_call(
+ py_func, py_text, visit_fn, visit_user_data);
+ Py_DECREF(py_text);
+ if (!ok) {
+ break;
+ }
+ }
+ Py_DECREF(it);
+ if (PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ PyErr_Clear();
+ }
+ else {
+ PyC_Err_PrintWithFunc(py_func);
+ }
+ }
+ }
+ }
+ else {
+ /* Sequence (typically list/tuple). */
+ PyObject *ret_fast = PySequence_Fast(
+ ret,
+ "StringProperty(...): "
+ "return value from search callback was not a sequence, iterator or generator");
+ if (ret_fast == NULL) {
+ PyC_Err_PrintWithFunc(py_func);
+ }
+ else {
+ const Py_ssize_t ret_num = PySequence_Fast_GET_SIZE(ret_fast);
+ PyObject **ret_fast_items = PySequence_Fast_ITEMS(ret_fast);
+ for (Py_ssize_t i = 0; i < ret_num; i++) {
+ const bool ok = bpy_prop_string_visit_fn_call(
+ py_func, ret_fast_items[i], visit_fn, visit_user_data);
+ if (!ok) {
+ break;
+ }
+ }
+ Py_DECREF(ret_fast);
+ }
+ }
+
+ Py_DECREF(ret);
+ }
+
+ if (C) {
+ bpy_context_clear((struct bContext *)C, &gilstate);
+ }
+ else {
+ PyGILState_Release(gilstate);
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -2352,11 +2525,14 @@ static void bpy_prop_callback_assign_float_array(struct PropertyRNA *prop,
static void bpy_prop_callback_assign_string(struct PropertyRNA *prop,
PyObject *get_fn,
- PyObject *set_fn)
+ PyObject *set_fn,
+ PyObject *search_fn,
+ const eStringPropertySearchFlag search_flag)
{
StringPropertyGetFunc rna_get_fn = NULL;
StringPropertyLengthFunc rna_length_fn = NULL;
StringPropertySetFunc rna_set_fn = NULL;
+ StringPropertySearchFunc rna_search_fn = NULL;
if (get_fn && get_fn != Py_None) {
struct BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
@@ -2372,8 +2548,17 @@ static void bpy_prop_callback_assign_string(struct PropertyRNA *prop,
rna_set_fn = bpy_prop_string_set_fn;
ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn);
}
+ if (search_fn) {
+ struct BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop);
+
+ rna_search_fn = bpy_prop_string_visit_for_search_fn;
+ ASSIGN_PYOBJECT_INCREF(prop_store->py_data.string_data.search_fn, search_fn);
+ }
RNA_def_property_string_funcs_runtime(prop, rna_get_fn, rna_length_fn, rna_set_fn);
+ if (rna_search_fn) {
+ RNA_def_property_string_search_func_runtime(prop, rna_search_fn, search_flag);
+ }
}
static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
@@ -2628,6 +2813,24 @@ static int bpy_prop_arg_parse_tag_defines(PyObject *o, void *p)
" This function must take 2 values (self, value) and return None.\n" \
" :type set: function\n"
+#define BPY_PROPDEF_SEARCH_DOC \
+ " :arg search: Function to be called to show candidates for this string (shown in the UI).\n" \
+ " This function must take 3 values (self, context, edit_text)\n" \
+ " and return a sequence, iterator or generator where each item must be:\n" \
+ "\n" \
+ " - A single string (representing a candidate to display).\n" \
+ " - A tuple-pair of strings, where the first is a candidate and the second\n" \
+ " is additional information about the candidate.\n" \
+ " :type search: function\n" \
+ " :arg search_options: Set of strings in:\n" \
+ "\n" \
+ " - 'SORT' sorts the resulting items.\n" \
+ " - 'SUGGESTION' lets the user enter values not found in search candidates.\n" \
+ " **WARNING** disabling this flag causes the search callback to run on redraw,\n" \
+ " so only disable this flag if it's not likely to cause performance issues.\n" \
+ "\n" \
+ " :type search_options: set\n"
+
#define BPY_PROPDEF_POINTER_TYPE_DOC \
" :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \
" :type type: class\n"
@@ -3721,7 +3924,9 @@ PyDoc_STRVAR(BPy_StringProperty_doc,
"subtype='NONE', "
"update=None, "
"get=None, "
- "set=None)\n"
+ "set=None, "
+ "search=None, "
+ "search_options={'SUGGESTION'})\n"
"\n"
" Returns a new string property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC
@@ -3730,7 +3935,7 @@ PyDoc_STRVAR(BPy_StringProperty_doc,
" :arg maxlen: maximum length of the string.\n"
" :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC
- BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
+ BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC BPY_PROPDEF_SEARCH_DOC);
static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -3767,6 +3972,11 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
PyObject *update_fn = NULL;
PyObject *get_fn = NULL;
PyObject *set_fn = NULL;
+ PyObject *search_fn = NULL;
+ static struct BPy_EnumProperty_Parse search_options_enum = {
+ .items = property_string_search_options_items,
+ .value = PROP_STRING_SEARCH_SUGGESTION,
+ };
static const char *_keywords[] = {
"attr",
@@ -3781,6 +3991,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
"update",
"get",
"set",
+ "search",
+ "search_options",
NULL,
};
static _PyArg_Parser _parser = {
@@ -3797,6 +4009,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
"O" /* `update` */
"O" /* `get` */
"O" /* `set` */
+ "O" /* `search` */
+ "O&" /* `search_options` */
":StringProperty",
_keywords,
0,
@@ -3820,7 +4034,10 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
&subtype_enum,
&update_fn,
&get_fn,
- &set_fn)) {
+ &set_fn,
+ &search_fn,
+ pyrna_enum_bitfield_parse_set,
+ &search_options_enum)) {
return NULL;
}
@@ -3833,6 +4050,9 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
if (bpy_prop_callback_check(set_fn, "set", 2) == -1) {
return NULL;
}
+ if (bpy_prop_callback_check(set_fn, "search", 3) == -1) {
+ return NULL;
+ }
if (id_data.prop_free_handle != NULL) {
RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle);
@@ -3858,7 +4078,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
bpy_prop_assign_flag_override(prop, override_enum.value);
}
bpy_prop_callback_assign_update(prop, update_fn);
- bpy_prop_callback_assign_string(prop, get_fn, set_fn);
+ bpy_prop_callback_assign_string(prop, get_fn, set_fn, search_fn, search_options_enum.value);
RNA_def_property_duplicate_pointers(srna, prop);
Py_RETURN_NONE;
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 1ed3174ee6f..dda60975a96 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -974,6 +974,14 @@ bool WM_operatortype_remove(const char *idname);
* Remove memory of all previously executed tools.
*/
void WM_operatortype_last_properties_clear_all(void);
+
+void WM_operatortype_idname_visit_for_search(const struct bContext *C,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
+
/**
* Tag all operator-properties of \a ot defined after calling this, until
* the next #WM_operatortype_props_advanced_end call (if available), with
@@ -1077,6 +1085,13 @@ void WM_menutype_freelink(struct MenuType *mt);
void WM_menutype_free(void);
bool WM_menutype_poll(struct bContext *C, struct MenuType *mt);
+void WM_menutype_idname_visit_for_search(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
+
/* wm_panel_type.c */
/**
@@ -1088,6 +1103,13 @@ struct PanelType *WM_paneltype_find(const char *idname, bool quiet);
bool WM_paneltype_add(struct PanelType *pt);
void WM_paneltype_remove(struct PanelType *pt);
+void WM_paneltype_idname_visit_for_search(const struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ const char *edit_text,
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data);
+
/* wm_gesture_ops.c */
int WM_gesture_box_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
diff --git a/source/blender/windowmanager/intern/wm_menu_type.c b/source/blender/windowmanager/intern/wm_menu_type.c
index 9e50ebb1ce5..b4cf5a79cfa 100644
--- a/source/blender/windowmanager/intern/wm_menu_type.c
+++ b/source/blender/windowmanager/intern/wm_menu_type.c
@@ -99,3 +99,21 @@ bool WM_menutype_poll(bContext *C, MenuType *mt)
}
return true;
}
+
+void WM_menutype_idname_visit_for_search(const bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ const char *UNUSED(edit_text),
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, menutypes_hash) {
+ MenuType *mt = BLI_ghashIterator_getValue(&gh_iter);
+
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = mt->idname;
+ visit_params.info = mt->label;
+ visit_fn(visit_user_data, &visit_params);
+ }
+}
diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c
index 8aa8469f0bc..d1c27504628 100644
--- a/source/blender/windowmanager/intern/wm_operator_type.c
+++ b/source/blender/windowmanager/intern/wm_operator_type.c
@@ -246,6 +246,27 @@ void WM_operatortype_last_properties_clear_all(void)
}
}
+void WM_operatortype_idname_visit_for_search(const bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ const char *UNUSED(edit_text),
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, global_ops_hash) {
+ wmOperatorType *ot = BLI_ghashIterator_getValue(&gh_iter);
+
+ char idname_py[OP_MAX_TYPENAME];
+ WM_operator_py_idname(idname_py, ot->idname);
+
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = idname_py;
+ visit_params.info = ot->name;
+ visit_fn(visit_user_data, &visit_params);
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index c3f2f9d15ca..307d3282659 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -1870,7 +1870,14 @@ static void WM_OT_call_menu(wmOperatorType *ot)
ot->flag = OPTYPE_INTERNAL;
- RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
+ PropertyRNA *prop;
+
+ prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
+ RNA_def_property_string_search_func_runtime(
+ prop,
+ WM_menutype_idname_visit_for_search,
+ /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */
+ (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION));
}
static int wm_call_pie_menu_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -1902,7 +1909,14 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot)
ot->flag = OPTYPE_INTERNAL;
- RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu");
+ PropertyRNA *prop;
+
+ prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu");
+ RNA_def_property_string_search_func_runtime(
+ prop,
+ WM_menutype_idname_visit_for_search,
+ /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */
+ (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION));
}
static int wm_call_panel_exec(bContext *C, wmOperator *op)
@@ -1938,6 +1952,11 @@ static void WM_OT_call_panel(wmOperatorType *ot)
PropertyRNA *prop;
prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
+ RNA_def_property_string_search_func_runtime(
+ prop,
+ WM_paneltype_idname_visit_for_search,
+ /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */
+ (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION));
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "keep_open", true, "Keep Open", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
diff --git a/source/blender/windowmanager/intern/wm_panel_type.c b/source/blender/windowmanager/intern/wm_panel_type.c
index bfcc86c38e7..860b53c1071 100644
--- a/source/blender/windowmanager/intern/wm_panel_type.c
+++ b/source/blender/windowmanager/intern/wm_panel_type.c
@@ -65,3 +65,21 @@ void WM_paneltype_clear(void)
{
BLI_ghash_free(g_paneltypes_hash, NULL, NULL);
}
+
+void WM_paneltype_idname_visit_for_search(const bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ const char *UNUSED(edit_text),
+ StringPropertySearchVisitFunc visit_fn,
+ void *visit_user_data)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, g_paneltypes_hash) {
+ PanelType *pt = BLI_ghashIterator_getValue(&gh_iter);
+
+ StringPropertySearchVisitParams visit_params = {NULL};
+ visit_params.text = pt->idname;
+ visit_params.info = pt->label;
+ visit_fn(visit_user_data, &visit_params);
+ }
+}