diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_topbar.py | 3 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_userpref.py | 68 | ||||
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 11 | ||||
-rw-r--r-- | source/blender/editors/interface/interface.c | 20 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 12 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 4 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_layout.c | 2 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_region_search.c | 61 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 653 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_utils.c | 2 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_select.c | 4 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_tools.c | 4 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_userdef_types.h | 4 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_ui_api.c | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_userdef.c | 4 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_operators.c | 45 |
16 files changed, 827 insertions, 71 deletions
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 1f52323f540..7ffb61fef5a 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -207,7 +207,8 @@ class TOPBAR_MT_editor_menus(Menu): def draw(self, context): layout = self.layout - if context.area.show_menus: + # Allow calling this menu directly (this might not be a header area). + if getattr(context.area, "show_menus"): layout.menu("TOPBAR_MT_app", text="", icon='BLENDER') else: layout.menu("TOPBAR_MT_app", text="Blender") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index e6ee779d89b..165761254d0 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2134,6 +2134,21 @@ class ExperimentalPanel: url_prefix = "https://developer.blender.org/" + def _draw_items(self, context, items): + prefs = context.preferences + experimental = prefs.experimental + + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + for prop_keywords, task in items: + split = layout.split(factor=0.66) + col = split.split() + col.prop(experimental, **prop_keywords) + col = split.split() + col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task + """ # Example panel, leave it here so we always have a template to follow even # after the features are gone from the experimental panel. @@ -2142,46 +2157,34 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): bl_label = "Virtual Reality" def draw(self, context): - prefs = context.preferences - experimental = prefs.experimental + self._draw_items( + context, ( + ({"property": "use_virtual_reality_scene_inspection"}, "T71347"), + ({"property": "use_virtual_reality_immersive_drawing"}, "T71348"), + ) + ) +""" - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False +class USERPREF_PT_experimental_ui(ExperimentalPanel, Panel): + bl_label = "UI" - task = "T71347" - split = layout.split(factor=0.66) - col = split.split() - col.prop(experimental, "use_virtual_reality_scene_inspection", text="Scene Inspection") - col = split.split() - col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task - - task = "T71348" - split = layout.split(factor=0.66) - col = split.column() - col.prop(experimental, "use_virtual_reality_immersive_drawing", text="Continuous Immersive Drawing") - col = split.column() - col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task -""" + def draw(self, context): + self._draw_items( + context, ( + ({"property": "use_menu_search"}, "T74157"), + ), + ) class USERPREF_PT_experimental_system(ExperimentalPanel, Panel): bl_label = "System" def draw(self, context): - prefs = context.preferences - experimental = prefs.experimental - - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - task = "T60695" - split = layout.split(factor=0.66) - col = split.split() - col.prop(experimental, "use_undo_speedup") - col = split.split() - col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task + self._draw_items( + context, ( + ({"property": "use_undo_speedup"}, "T60695"), + ), + ) # ----------------------------------------------------------------------------- @@ -2274,6 +2277,7 @@ classes = ( # Popovers. USERPREF_PT_ndof_settings, + USERPREF_PT_experimental_ui, USERPREF_PT_experimental_system, # Add dynamically generated editor theme panels last, diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 3e416cd8057..bb2edef8adc 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -506,6 +506,9 @@ typedef void (*uiButSearchFunc)(const struct bContext *C, void *arg, const char *str, uiSearchItems *items); + +typedef void (*uiButSearchArgFreeFunc)(void *arg); + /* Must return allocated string. */ typedef char *(*uiButToolTipFunc)(struct bContext *C, void *argN, const char *tip); typedef int (*uiButPushedStateFunc)(struct bContext *C, void *arg); @@ -1565,13 +1568,13 @@ eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout, const bool compact); /* use inside searchfunc to add items */ -bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid); +bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, int state); /* bfunc gets search item *poin as arg2, or if NULL the old string */ void UI_but_func_search_set(uiBut *but, uiButSearchCreateFunc cfunc, uiButSearchFunc sfunc, void *arg, - bool free_arg, + uiButSearchArgFreeFunc search_arg_free_func, uiButHandleFunc bfunc, void *active); /* height in pixels, it's using hardcoded values still */ @@ -2035,6 +2038,10 @@ void uiTemplateImageInfo(uiLayout *layout, void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C); void UI_but_func_operator_search(uiBut *but); void uiTemplateOperatorSearch(uiLayout *layout); + +void UI_but_func_menu_search(uiBut *but); +void uiTemplateMenuSearch(uiLayout *layout); + eAutoPropButsReturn uiTemplateOperatorPropertyButs(const struct bContext *C, uiLayout *layout, struct wmOperator *op, diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 2a4ca48a005..41b7683dff7 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -3207,8 +3207,9 @@ static void ui_but_free(const bContext *C, uiBut *but) MEM_freeN(but->hold_argN); } - if (but->free_search_arg) { - MEM_SAFE_FREE(but->search_arg); + if (but->search_arg_free_func) { + but->search_arg_free_func(but->search_arg); + but->search_arg = NULL; } if (but->active) { @@ -6336,7 +6337,7 @@ void UI_but_func_search_set(uiBut *but, uiButSearchCreateFunc search_create_func, uiButSearchFunc search_func, void *arg, - bool free_arg, + uiButSearchArgFreeFunc search_arg_free_func, uiButHandleFunc bfunc, void *active) { @@ -6346,14 +6347,16 @@ void UI_but_func_search_set(uiBut *but, search_create_func = ui_searchbox_create_generic; } - if (but->free_search_arg) { - MEM_SAFE_FREE(but->search_arg); + if (but->search_arg_free_func != NULL) { + but->search_arg_free_func(but->search_arg); + but->search_arg = NULL; } but->search_create_func = search_create_func; but->search_func = search_func; + but->search_arg = arg; - but->free_search_arg = free_arg; + but->search_arg_free_func = search_arg_free_func; if (bfunc) { #ifdef DEBUG @@ -6404,8 +6407,7 @@ static void operator_enum_search_cb(const struct bContext *C, /* note: need to give the index rather than the * identifier because the enum can be freed */ if (BLI_strcasestr(item->name, str)) { - if (false == - UI_search_item_add(items, item->name, POINTER_FROM_INT(item->value), item->icon)) { + if (!UI_search_item_add(items, item->name, POINTER_FROM_INT(item->value), item->icon, 0)) { break; } } @@ -6462,7 +6464,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block, ui_searchbox_create_generic, operator_enum_search_cb, but, - false, + NULL, operator_enum_call_cb, NULL); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 238223fddfd..2dfa29f5646 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -421,6 +421,9 @@ typedef struct uiAfterFunc { PointerRNA rnapoin; PropertyRNA *rnaprop; + void *search_arg; + uiButSearchArgFreeFunc search_arg_free_func; + bContextStore *context; char undostr[BKE_UNDO_STR_MAX]; @@ -755,6 +758,11 @@ static void ui_apply_but_func(bContext *C, uiBut *but) after->rnapoin = but->rnapoin; after->rnaprop = but->rnaprop; + after->search_arg_free_func = but->search_arg_free_func; + after->search_arg = but->search_arg; + but->search_arg_free_func = NULL; + but->search_arg = NULL; + if (but->context) { after->context = CTX_store_copy(but->context); } @@ -921,6 +929,10 @@ static void ui_apply_but_funcs_after(bContext *C) MEM_freeN(after.rename_orig); } + if (after.search_arg_free_func) { + after.search_arg_free_func(after.search_arg); + } + ui_afterfunc_update_preferences_dirty(&after); if (after.undostr[0]) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index e2b4e8f2958..4a9c8a1ff54 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -203,8 +203,8 @@ struct uiBut { uiButSearchCreateFunc search_create_func; uiButSearchFunc search_func; - bool free_search_arg; void *search_arg; + uiButSearchArgFreeFunc search_arg_free_func; uiButHandleRenameFunc rename_func; void *rename_arg1; @@ -649,6 +649,8 @@ ColorPicker *ui_block_colorpicker_create(struct uiBlock *block); /* Searchbox for string button */ ARegion *ui_searchbox_create_generic(struct bContext *C, struct ARegion *butregion, uiBut *but); ARegion *ui_searchbox_create_operator(struct bContext *C, struct ARegion *butregion, uiBut *but); +ARegion *ui_searchbox_create_menu(struct bContext *C, struct ARegion *butregion, uiBut *but); + bool ui_searchbox_inside(struct ARegion *region, int x, int y); int ui_searchbox_find_index(struct ARegion *region, const char *name); void ui_searchbox_update(struct bContext *C, struct ARegion *region, uiBut *but, const bool reset); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 79b3c1a0a83..fed35ccff59 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -2610,7 +2610,7 @@ void ui_but_add_search( ui_searchbox_create_generic, ui_rna_collection_search_cb, coll_search, - true, + MEM_freeN, NULL, NULL); } diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index dab42e47e0b..48779fd86dc 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -73,6 +73,7 @@ struct uiSearchItems { char **names; void **pointers; int *icons; + int *states; AutoComplete *autocpl; void *active; @@ -95,9 +96,18 @@ typedef struct uiSearchboxData { #define SEARCH_ITEMS 10 -/* exported for use by search callbacks */ -/* returns zero if nothing to add */ -bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid) +/** + * Public function exported for functions that use #UI_BTYPE_SEARCH_MENU. + * + * \param items: Stores the items. + * \param name: Text to display for the item. + * \param poin: Opaque pointer (for use by the caller). + * \param iconid: The icon, #ICON_NONE for no icon. + * \param state: The buttons state flag, compatible with #uiBut.flag, + * typically #UI_BUT_DISABLED / #UI_BUT_INACTIVE. + * \return false if there is nothing to add. + */ +bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, int state) { /* hijack for autocomplete */ if (items->autocpl) { @@ -135,6 +145,13 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int items->icons[items->totitem] = iconid; } + /* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set + * which will cause problems, add others as needed. */ + BLI_assert((state & ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT)) == 0); + if (items->states) { + items->states[items->totitem] = state; + } + items->totitem++; return true; @@ -421,17 +438,16 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) if (data->preview) { /* draw items */ for (a = 0; a < data->items.totitem; a++) { + const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + /* ensure icon is up-to-date */ ui_icon_ensure_deferred(C, data->items.icons[a], data->preview); ui_searchbox_butrect(&rect, data, a); /* widget itself */ - ui_draw_preview_item(&data->fstyle, - &rect, - data->items.names[a], - data->items.icons[a], - (a == data->active) ? UI_ACTIVE : 0); + ui_draw_preview_item( + &data->fstyle, &rect, data->items.names[a], data->items.icons[a], state); } /* indicate more */ @@ -451,6 +467,8 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) else { /* draw items */ for (a = 0; a < data->items.totitem; a++) { + const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + ui_searchbox_butrect(&rect, data, a); /* widget itself */ @@ -458,7 +476,7 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) &rect, data->items.names[a], data->items.icons[a], - (a == data->active) ? UI_ACTIVE : 0, + state, data->use_sep); } /* indicate more */ @@ -490,6 +508,7 @@ static void ui_searchbox_region_free_cb(ARegion *region) MEM_freeN(data->items.names); MEM_freeN(data->items.pointers); MEM_freeN(data->items.icons); + MEM_freeN(data->items.states); MEM_freeN(data); region->regiondata = NULL; @@ -657,6 +676,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but data->items.names = MEM_callocN(data->items.maxitem * sizeof(void *), "search names"); data->items.pointers = MEM_callocN(data->items.maxitem * sizeof(void *), "search pointers"); data->items.icons = MEM_callocN(data->items.maxitem * sizeof(int), "search icons"); + data->items.states = MEM_callocN(data->items.maxitem * sizeof(int), "search flags"); for (i = 0; i < data->items.maxitem; i++) { data->items.names[i] = MEM_callocN(but->hardmax + 1, "search pointers"); } @@ -718,9 +738,9 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* widget itself */ /* NOTE: i18n messages extracting tool does the same, please keep it in sync. */ { - wmOperatorType *ot = data->items.pointers[a]; + const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; - int state = (a == data->active) ? UI_ACTIVE : 0; + wmOperatorType *ot = data->items.pointers[a]; char text_pre[128]; char *text_pre_p = strstr(ot->idname, "_OT_"); if (text_pre_p == NULL) { @@ -780,6 +800,25 @@ void ui_searchbox_free(bContext *C, ARegion *region) ui_region_temp_remove(C, CTX_wm_screen(C), region); } +static void ui_searchbox_region_draw_cb__menu(const bContext *UNUSED(C), ARegion *UNUSED(region)) +{ + /* Currently unused. */ +} + +ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiBut *but) +{ + ARegion *region; + + UI_but_drawflag_enable(but, UI_BUT_HAS_SHORTCUT); + region = ui_searchbox_create_generic(C, butregion, but); + + if (false) { + region->type->draw = ui_searchbox_region_draw_cb__menu; + } + + return region; +} + /* sets red alert if button holds a string it can't find */ /* XXX weak: search_func adds all partial matches... */ void ui_but_search_refresh(uiBut *but) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 42e0aab8418..d7377a0e56e 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -38,13 +38,17 @@ #include "DNA_texture_types.h" #include "BLI_alloca.h" +#include "BLI_dynstr.h" #include "BLI_fnmatch.h" #include "BLI_ghash.h" +#include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_memarena.h" #include "BLI_path_util.h" #include "BLI_rect.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_timecode.h" #include "BLI_utildefines.h" @@ -99,6 +103,9 @@ #include "PIL_time.h" +/* For key-map item access. */ +#include "wm_event_system.h" + // #define USE_OP_RESET_BUT // we may want to make this optional, disable for now. /* defines for templateID/TemplateSearch */ @@ -281,7 +288,7 @@ static uiBlock *template_common_search_menu(const bContext *C, ""); } UI_but_func_search_set( - but, ui_searchbox_create_generic, search_func, search_arg, false, handle_func, active_item); + but, ui_searchbox_create_generic, search_func, search_arg, NULL, handle_func, active_item); UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); UI_block_direction_set(block, UI_DIR_DOWN); @@ -363,7 +370,7 @@ static bool id_search_add(const bContext *C, int iconid = ui_id_icon_get(C, id, template_ui->preview); - if (false == UI_search_item_add(items, name_ui, id, iconid)) { + if (!UI_search_item_add(items, name_ui, id, iconid, 0)) { return false; } } @@ -6643,7 +6650,7 @@ static void operator_search_cb(const bContext *C, } } - if (false == UI_search_item_add(items, name, ot, 0)) { + if (!UI_search_item_add(items, name, ot, ICON_NONE, 0)) { break; } } @@ -6674,6 +6681,646 @@ void uiTemplateOperatorSearch(uiLayout *layout) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Menu Search Template + * \{ */ + +struct MenuSearch_Parent { + struct MenuSearch_Parent *parent; + MenuType *parent_mt; + /* Set while writing menu items only. */ + struct MenuSearch_Parent *temp_child; + const char *drawstr; +}; + +struct MenuSearch_Item { + struct MenuSearch_Item *next, *prev; + const char *drawstr; + const char *drawwstr_full; + /** Support a single level sub-menu nesting (for operator buttons that expand). */ + const char *drawstr_submenu; + int icon; + int state; + + struct MenuSearch_Parent *menu_parent; + MenuType *mt; + + enum { + MENU_SEARCH_TYPE_OP = 1, + MENU_SEARCH_TYPE_RNA = 2, + } type; + + union { + /* Operator menu item. */ + struct { + wmOperatorType *type; + PointerRNA *opptr; + short opcontext; + bContextStore *context; + } op; + + /* Property (only for check-boxe/boolean). */ + struct { + PointerRNA ptr; + PropertyRNA *prop; + int index; + /** Only for enum buttons. */ + int enum_value; + } rna; + }; +}; + +struct MenuSearch_Data { + /** MenuSearch_Item */ + ListBase items; + /** Use for all small allocations. */ + MemArena *memarena; +}; + +static int menu_item_sort_by_drawstr_full(const void *menu_item_a_v, const void *menu_item_b_v) +{ + const struct MenuSearch_Item *menu_item_a = menu_item_a_v; + const struct MenuSearch_Item *menu_item_b = menu_item_b_v; + return strcmp(menu_item_a->drawwstr_full, menu_item_b->drawwstr_full); +} + +static const char *strdup_memarena(MemArena *memarena, const char *str) +{ + const uint str_size = strlen(str) + 1; + char *str_dst = BLI_memarena_alloc(memarena, str_size); + memcpy(str_dst, str, str_size); + return str_dst; +} + +static const char *strdup_memarena_from_dynstr(MemArena *memarena, DynStr *dyn_str) +{ + const uint str_size = BLI_dynstr_get_len(dyn_str) + 1; + char *str_dst = BLI_memarena_alloc(memarena, str_size); + BLI_dynstr_get_cstring_ex(dyn_str, str_dst); + return str_dst; +} + +static bool menu_items_from_ui_create_item_from_button(struct MenuSearch_Data *data, + MemArena *memarena, + struct MenuType *mt, + const char *drawstr_submenu, + uiBut *but) +{ + struct MenuSearch_Item *item = NULL; + if (but->optype != NULL) { + item = BLI_memarena_calloc(memarena, sizeof(*item)); + item->type = MENU_SEARCH_TYPE_OP; + + item->op.type = but->optype; + item->op.opcontext = but->opcontext; + item->op.context = but->context; + item->op.opptr = but->opptr; + but->opptr = NULL; + } + else if (but->rnaprop != NULL) { + const int prop_type = RNA_property_type(but->rnaprop); + if (!ELEM(prop_type, PROP_BOOLEAN, PROP_ENUM)) { + /* Note that these buttons are not prevented, + * but aren't typically used in menus. */ + printf("Button '%s' in menu '%s' is a menu item with unsupported RNA type %d\n", + but->drawstr, + mt->idname, + prop_type); + } + else { + item = BLI_memarena_calloc(memarena, sizeof(*item)); + item->type = MENU_SEARCH_TYPE_RNA; + + item->rna.ptr = but->rnapoin; + item->rna.prop = but->rnaprop; + item->rna.index = but->rnaindex; + + if (prop_type == PROP_ENUM) { + item->rna.enum_value = (int)but->hardmax; + } + } + } + + if (item != NULL) { + /* Handle shared settings. */ + item->drawstr = strdup_memarena(memarena, but->drawstr); + item->icon = but->icon; + item->state = (but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT)); + item->mt = mt; + item->drawstr_submenu = drawstr_submenu ? strdup_memarena(memarena, drawstr_submenu) : NULL; + BLI_addtail(&data->items, item); + return true; + } + + return false; +} + +/** + * Populate \a menu_stack with menus from inspecting active key-maps for this context. + */ +static void menu_types_add_from_keymap_items(bContext *C, + wmWindow *win, + ScrArea *sa, + ARegion *region, + LinkNode **menuid_stack_p, + GHash *menu_to_kmi, + GSet *menu_tagged) +{ + wmWindowManager *wm = CTX_wm_manager(C); + ListBase *handlers[] = { + ®ion->handlers, + &sa->handlers, + &win->handlers, + }; + + for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { + LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers[handler_index]) { + /* During this loop, ui handlers for nested menus can tag multiple handlers free. */ + if (handler_base->flag & WM_HANDLER_DO_FREE) { + continue; + } + if (handler_base->type != WM_HANDLER_TYPE_KEYMAP) { + continue; + } + + else if (handler_base->poll == NULL || handler_base->poll(region, win->eventstate)) { + wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; + wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); + if (keymap && WM_keymap_poll(C, keymap)) { + LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { + if (kmi->flag & KMI_INACTIVE) { + continue; + } + if (STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie")) { + char menu_idname[MAX_NAME]; + RNA_string_get(kmi->ptr, "name", menu_idname); + MenuType *mt = WM_menutype_find(menu_idname, false); + + if (mt && BLI_gset_add(menu_tagged, mt)) { + /* Unlikely, but possible this will be included twice. */ + BLI_linklist_prepend(menuid_stack_p, mt); + + void **kmi_p; + if (!BLI_ghash_ensure_p(menu_to_kmi, mt, &kmi_p)) { + *kmi_p = kmi; + } + } + } + } + } + } + } + } +} + +/** + * Create #MenuSearch_Data by inspecting the current context, this uses two methods: + * + * - Look-up pre-defined editor-menus. + * - Look-up key-map items which call menus. + */ +static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C, + wmWindow *win, + ScrArea *sa, + ARegion *region) +{ + MemArena *memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + /** Map (#MenuType to #MenuSearch_Parent) */ + GHash *menu_parent_map = BLI_ghash_ptr_new(__func__); + GHash *menu_display_name_map = BLI_ghash_ptr_new(__func__); + const uiStyle *style = UI_style_get_dpi(); + + /* Convert into non-ui structure. */ + struct MenuSearch_Data *data = MEM_callocN(sizeof(*data), __func__); + + DynStr *dyn_str = BLI_dynstr_new_memarena(); + + /* Use a stack of menus to handle and discover new menus in passes. */ + LinkNode *menu_stack = NULL; + + /* Tag menu types not to add, either because they have already been added + * or they have been blacklisted. + * Set of #MenuType. */ + GSet *menu_tagged = BLI_gset_ptr_new(__func__); + /** Map (#MenuType -> #wmKeyMapItem). */ + GHash *menu_to_kmi = BLI_ghash_ptr_new(__func__); + + /* Blacklist menus we don't want to show. */ + { + const char *idname_array[] = { + /* While we could include this, it's just showing filenames to load. */ + "TOPBAR_MT_file_open_recent", + }; + for (int i = 0; i < ARRAY_SIZE(idname_array); i++) { + MenuType *mt = WM_menutype_find(idname_array[i], false); + if (mt != NULL) { + BLI_gset_add(menu_tagged, mt); + } + } + } + + /* Populate menus from the editors, + * note that we could create a fake header, draw the header and extract the menus + * from the buttons, however this is quite involved and can be avoided as by convention + * each space-type has a single root-menu that headers use. */ + { + const char *idname_array[] = { + "TOPBAR_MT_editor_menus", + /* Optional second menu for the space-type. */ + NULL, + }; + int idname_array_len = 1; + +#define SPACE_MENU_MAP(space_type, menu_id) \ + case space_type: \ + idname_array[idname_array_len++] = menu_id; \ + break +#define SPACE_MENU_NOP(space_type) \ + case space_type: \ + break + + if (sa != NULL) { + switch (sa->spacetype) { + SPACE_MENU_MAP(SPACE_VIEW3D, "VIEW3D_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_GRAPH, "GRAPH_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_OUTLINER, "OUTLINER_MT_editor_menus"); + SPACE_MENU_NOP(SPACE_PROPERTIES); + SPACE_MENU_MAP(SPACE_FILE, "FILE_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_IMAGE, "IMAGE_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_INFO, "INFO_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_SEQ, "SEQUENCER_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_TEXT, "TEXT_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_ACTION, "ACTION_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_NLA, "NLA_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_NODE, "NODE_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_CONSOLE, "CONSOLE_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_USERPREF, "USERPREF_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_CLIP, + (((const SpaceClip *)sa->spacedata.first)->mode == SC_MODE_TRACKING) ? + "CLIP_MT_tracking_editor_menus" : + "CLIP_MT_masking_editor_menus"); + SPACE_MENU_NOP(SPACE_TOPBAR); + SPACE_MENU_NOP(SPACE_STATUSBAR); + default: + printf("Unknown space type '%d'\n", sa->spacetype); + } + } + for (int i = 0; i < idname_array_len; i++) { + MenuType *mt = WM_menutype_find(idname_array[i], false); + if (mt != NULL) { + BLI_linklist_prepend(&menu_stack, mt); + BLI_gset_add(menu_tagged, mt); + } + } + } +#undef SPACE_MENU_MAP +#undef SPACE_MENU_NOP + + bool has_keymap_menu_items = false; + + GHashIterator iter; + + while (menu_stack != NULL) { + MenuType *mt = BLI_linklist_pop(&menu_stack); + if (!WM_menutype_poll(C, mt)) { + continue; + } + + uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS); + uiLayout *layout = UI_block_layout( + block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); + + UI_block_flag_enable(block, UI_BLOCK_SHOW_SHORTCUT_ALWAYS); + + uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN); + UI_menutype_draw(C, mt, layout); + + UI_block_end(C, block); + + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + MenuType *mt_from_but = NULL; + /* Support menu titles with dynamic from initial labels + * (used by edit-mesh context menu). */ + if (but->type == UI_BTYPE_LABEL) { + + /* Check if the label is the title. */ + uiBut *but_test = but->prev; + while (but_test && but_test->type == UI_BTYPE_SEPR) { + but_test = but_test->prev; + } + + if (but_test == NULL) { + BLI_ghash_insert( + menu_display_name_map, mt, (void *)strdup_memarena(memarena, but->drawstr)); + } + } + else if (menu_items_from_ui_create_item_from_button(data, memarena, mt, NULL, but)) { + /* pass */ + } + else if ((mt_from_but = UI_but_menutype_get(but))) { + + if (BLI_gset_add(menu_tagged, mt_from_but)) { + BLI_linklist_prepend(&menu_stack, mt_from_but); + } + + if (!BLI_ghash_haskey(menu_parent_map, mt_from_but)) { + struct MenuSearch_Parent *menu_parent = BLI_memarena_calloc(memarena, + sizeof(*menu_parent)); + /* Use brackets for menu key shortcuts, + * converting "Text|Some-Shortcut" to "Text (Some-Shortcut)". + * This is needed so we don't right align sub-menu contents + * we only want to do that for the last menu item, not the path that leads to it. + */ + const char *drawstr_sep = but->flag & UI_BUT_HAS_SEP_CHAR ? + strrchr(but->drawstr, UI_SEP_CHAR) : + NULL; + bool drawstr_is_empty = false; + if (drawstr_sep != NULL) { + BLI_assert(BLI_dynstr_get_len(dyn_str) == 0); + /* Detect empty string, fallback to menu name. */ + const char *drawstr = but->drawstr; + int drawstr_len = drawstr_sep - but->drawstr; + if (UNLIKELY(drawstr_len == 0)) { + drawstr = CTX_IFACE_(mt_from_but->translation_context, mt_from_but->label); + drawstr_len = strlen(drawstr); + if (drawstr[0] == '\0') { + drawstr_is_empty = true; + } + } + BLI_dynstr_nappend(dyn_str, drawstr, drawstr_len); + BLI_dynstr_appendf(dyn_str, " (%s)", drawstr_sep + 1); + menu_parent->drawstr = strdup_memarena_from_dynstr(memarena, dyn_str); + BLI_dynstr_clear(dyn_str); + } + else { + const char *drawstr = but->drawstr; + if (UNLIKELY(drawstr[0] == '\0')) { + drawstr = CTX_IFACE_(mt_from_but->translation_context, mt_from_but->label); + if (drawstr[0] == '\0') { + drawstr_is_empty = true; + } + } + menu_parent->drawstr = strdup_memarena(memarena, drawstr); + } + menu_parent->parent_mt = mt; + BLI_ghash_insert(menu_parent_map, mt_from_but, menu_parent); + + if (drawstr_is_empty) { + printf("Warning: '%s' menu has empty 'bl_label'.\n", mt_from_but->idname); + } + } + } + else if (but->menu_create_func != NULL) { + /* A non 'MenuType' menu button. */ + + /* Only expand one level deep, this is mainly for expanding operator menus. */ + const char *drawstr_submenu = but->drawstr; + + /* +1 to avoid overlap with the current 'block'. */ + uiBlock *sub_block = UI_block_begin(C, region, __func__ + 1, UI_EMBOSS); + uiLayout *sub_layout = UI_block_layout( + sub_block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); + + UI_block_flag_enable(sub_block, UI_BLOCK_SHOW_SHORTCUT_ALWAYS); + + uiLayoutSetOperatorContext(sub_layout, WM_OP_INVOKE_REGION_WIN); + + but->menu_create_func(C, sub_layout, but->poin); + + UI_block_end(C, sub_block); + + LISTBASE_FOREACH (uiBut *, sub_but, &sub_block->buttons) { + menu_items_from_ui_create_item_from_button(data, memarena, mt, drawstr_submenu, sub_but); + } + + BLI_remlink(®ion->uiblocks, sub_block); + UI_block_free(NULL, sub_block); + } + } + BLI_remlink(®ion->uiblocks, block); + UI_block_free(NULL, block); + + /* Add key-map items as a second pass, + * so all menus are accessed from the header & top-bar before key shortcuts are expanded. */ + if ((menu_stack == NULL) && (has_keymap_menu_items == false)) { + has_keymap_menu_items = true; + menu_types_add_from_keymap_items(C, win, sa, region, &menu_stack, menu_to_kmi, menu_tagged); + } + } + + LISTBASE_FOREACH (struct MenuSearch_Item *, item, &data->items) { + item->menu_parent = BLI_ghash_lookup(menu_parent_map, item->mt); + } + + GHASH_ITER (iter, menu_parent_map) { + struct MenuSearch_Parent *menu_parent = BLI_ghashIterator_getValue(&iter); + menu_parent->parent = BLI_ghash_lookup(menu_parent_map, menu_parent->parent_mt); + } + + /* NOTE: currently this builds the full path for each menu item, + * that could be moved into the parent menu. */ + + /* Unicode arrow. */ +#define MENU_SEP "\xe2\x86\x92" + + /* Set names as full paths. */ + LISTBASE_FOREACH (struct MenuSearch_Item *, item, &data->items) { + if (item->menu_parent != NULL) { + struct MenuSearch_Parent *menu_parent = item->menu_parent; + menu_parent->temp_child = NULL; + while (menu_parent && menu_parent->parent) { + menu_parent->parent->temp_child = menu_parent; + menu_parent = menu_parent->parent; + } + BLI_assert(BLI_dynstr_get_len(dyn_str) == 0); + while (menu_parent) { + BLI_dynstr_append(dyn_str, menu_parent->drawstr); + BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + menu_parent = menu_parent->temp_child; + } + } + else { + BLI_assert(BLI_dynstr_get_len(dyn_str) == 0); + const char *drawstr = BLI_ghash_lookup(menu_display_name_map, item->mt); + if (drawstr == NULL) { + drawstr = CTX_IFACE_(item->mt->translation_context, item->mt->label); + } + BLI_dynstr_append(dyn_str, drawstr); + + wmKeyMapItem *kmi = BLI_ghash_lookup(menu_to_kmi, item->mt); + if (kmi != NULL) { + char kmi_str[128]; + WM_keymap_item_to_string(kmi, false, kmi_str, sizeof(kmi_str)); + BLI_dynstr_appendf(dyn_str, " (%s)", kmi_str); + } + + BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + } + + /* Optional nested menu. */ + if (item->drawstr_submenu != NULL) { + BLI_dynstr_append(dyn_str, item->drawstr_submenu); + BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + } + + BLI_dynstr_append(dyn_str, item->drawstr); + + item->drawwstr_full = strdup_memarena_from_dynstr(memarena, dyn_str); + BLI_dynstr_clear(dyn_str); + } + BLI_dynstr_free(dyn_str); +#undef MENU_SEP + + /* Finally sort menu items. + * + * Note: we might want to keep the in-menu order, for now sort all. */ + BLI_listbase_sort(&data->items, menu_item_sort_by_drawstr_full); + + BLI_ghash_free(menu_parent_map, NULL, NULL); + BLI_ghash_free(menu_display_name_map, NULL, NULL); + + BLI_ghash_free(menu_to_kmi, NULL, NULL); + + BLI_gset_free(menu_tagged, NULL); + + data->memarena = memarena; + + return data; +} + +static void menu_items_from_ui_destroy(void *data_v) +{ + struct MenuSearch_Data *data = data_v; + LISTBASE_FOREACH (struct MenuSearch_Item *, item, &data->items) { + switch (item->type) { + case MENU_SEARCH_TYPE_OP: { + if (item->op.opptr != NULL) { + WM_operator_properties_free(item->op.opptr); + MEM_freeN(item->op.opptr); + } + } + case MENU_SEARCH_TYPE_RNA: { + break; + } + } + } + + BLI_memarena_free(data->memarena); + + MEM_freeN(data); +} + +static void menu_call_fn(bContext *C, void *UNUSED(arg1), void *arg2) +{ + struct MenuSearch_Item *item = arg2; + if (item == NULL) { + return; + } + if (item->state & UI_BUT_DISABLED) { + return; + } + + switch (item->type) { + case MENU_SEARCH_TYPE_OP: { + CTX_store_set(C, item->op.context); + WM_operator_name_call_ptr(C, item->op.type, item->op.opcontext, item->op.opptr); + CTX_store_set(C, NULL); + break; + } + case MENU_SEARCH_TYPE_RNA: { + PointerRNA *ptr = &item->rna.ptr; + PropertyRNA *prop = item->rna.prop; + int index = item->rna.index; + const int prop_type = RNA_property_type(prop); + bool changed = false; + + if (prop_type == PROP_BOOLEAN) { + const bool is_array = RNA_property_array_check(prop); + if (is_array) { + const bool value = RNA_property_boolean_get_index(ptr, prop, index); + RNA_property_boolean_set_index(ptr, prop, index, !value); + } + else { + const bool value = RNA_property_boolean_get(ptr, prop); + RNA_property_boolean_set(ptr, prop, !value); + } + changed = true; + } + else if (prop_type == PROP_ENUM) { + RNA_property_enum_set(ptr, prop, item->rna.enum_value); + changed = true; + } + + if (changed) { + RNA_property_update(C, ptr, prop); + } + break; + } + } +} + +static void menu_search_cb(const bContext *UNUSED(C), + void *arg, + const char *str, + uiSearchItems *items) +{ + struct MenuSearch_Data *data = arg; + const size_t str_len = strlen(str); + const int words_max = (str_len / 2) + 1; + int(*words)[2] = BLI_array_alloca(words, words_max); + + const int words_len = BLI_string_find_split_words(str, str_len, ' ', words, words_max); + + for (struct MenuSearch_Item *item = data->items.first; item; item = item->next) { + int index; + + /* match name against all search words */ + for (index = 0; index < words_len; index++) { + if (!has_word_prefix(item->drawwstr_full, str + words[index][0], words[index][1])) { + break; + } + } + + if (index == words_len) { + if (!UI_search_item_add(items, item->drawwstr_full, item, item->icon, item->state)) { + break; + } + } + } +} + +void UI_but_func_menu_search(uiBut *but) +{ + bContext *C = but->block->evil_C; + wmWindow *win = CTX_wm_window(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + struct MenuSearch_Data *data = menu_items_from_ui_create(C, win, sa, region); + UI_but_func_search_set(but, + ui_searchbox_create_menu, + menu_search_cb, + data, + menu_items_from_ui_destroy, + menu_call_fn, + NULL); +} + +void uiTemplateMenuSearch(uiLayout *layout) +{ + uiBlock *block; + uiBut *but; + static char search[256] = ""; + + block = uiLayoutGetBlock(layout); + UI_block_layout_set_current(block, layout); + + but = uiDefSearchBut( + block, search, 0, ICON_VIEWZOOM, sizeof(search), 0, 0, UI_UNIT_X * 6, UI_UNIT_Y, 0, 0, ""); + UI_but_func_menu_search(but); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Operator Redo Properties Template * \{ */ diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index efa7ffb22d0..a69837e9b51 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -445,7 +445,7 @@ void ui_rna_collection_search_cb(const struct bContext *C, /* add search items from temporary list */ for (cis = items_list->first; cis; cis = cis->next) { - if (UI_search_item_add(items, cis->name, cis->data, cis->iconid) == false) { + if (!UI_search_item_add(items, cis->name, cis->data, cis->iconid, 0)) { break; } } diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 2c9e7a213c2..98fcf862290 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -1131,7 +1131,7 @@ static void node_find_cb(const struct bContext *C, else { BLI_strncpy(name, node->name, 256); } - if (false == UI_search_item_add(items, name, node, 0)) { + if (!UI_search_item_add(items, name, node, ICON_NONE, 0)) { break; } } @@ -1178,7 +1178,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op) 0, 0, ""); - UI_but_func_search_set(but, NULL, node_find_cb, op->type, false, node_find_call_cb, NULL); + UI_but_func_search_set(but, NULL, node_find_cb, op->type, NULL, node_find_call_cb, NULL); UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); /* fake button, it holds space for search items */ diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 768ec17c1e7..12be08550af 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -528,7 +528,7 @@ static void merged_element_search_cb_recursive( /* Don't allow duplicate named items */ if (UI_search_items_find_index(items, name) == -1) { - if (!UI_search_item_add(items, name, te, iconid)) { + if (!UI_search_item_add(items, name, te, iconid, 0)) { break; } } @@ -589,7 +589,7 @@ static uiBlock *merged_element_search_menu(bContext *C, ARegion *region, void *d but = uiDefSearchBut( block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, ""); UI_but_func_search_set( - but, NULL, merged_element_search_cb, data, false, merged_element_search_call_cb, NULL); + but, NULL, merged_element_search_cb, data, NULL, merged_element_search_call_cb, NULL); UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); /* Fake button to hold space for search items */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 30b4098c2bb..54470dc59fc 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -619,7 +619,9 @@ typedef struct UserDef_FileSpaceData { typedef struct UserDef_Experimental { char use_undo_speedup; - char _pad0[7]; /* makesdna does not allow empty structs. */ + char use_menu_search; + /** `makesdna` does not allow empty structs. */ + char _pad0[6]; } UserDef_Experimental; #define USER_EXPERIMENTAL_TEST(userdef, member) \ diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 5eadbaf55e4..a169e81237d 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1474,6 +1474,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_CONTEXT); RNA_def_function(srna, "template_operator_search", "uiTemplateOperatorSearch"); + RNA_def_function(srna, "template_menu_search", "uiTemplateMenuSearch"); func = RNA_def_function(srna, "template_header_3D_mode", "uiTemplateHeader3D_mode"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index f68c3c3bceb..f4bb2f8ef0f 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6051,6 +6051,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) prop, "Undo Speedup", "Use new undo speedup (WARNING: can lead to crashes and serious .blend file corruption)"); + + prop = RNA_def_property(srna, "use_menu_search", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_menu_search", 1); + RNA_def_property_ui_text(prop, "Menu Search", "Search actions by menus instead of operators"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 961854ac23e..2ae71eb2490 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1687,10 +1687,15 @@ static void WM_OT_operator_defaults(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Operator Search Menu +/** \name Operator/Menu Search Operator * \{ */ struct SearchPopupInit_Data { + enum { + SEARCH_TYPE_OPERATOR = 0, + SEARCH_TYPE_MENU = 1, + } search_type; + int size[2]; }; @@ -1717,7 +1722,17 @@ static uiBlock *wm_block_search_menu(bContext *C, ARegion *region, void *userdat 0, 0, ""); - UI_but_func_operator_search(but); + + if (init_data->search_type == SEARCH_TYPE_OPERATOR) { + UI_but_func_operator_search(but); + } + else if (init_data->search_type == SEARCH_TYPE_MENU) { + UI_but_func_menu_search(but); + } + else { + BLI_assert(0); + } + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); /* fake button, it holds space for search items */ @@ -1747,7 +1762,7 @@ static int wm_search_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -static int wm_search_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int wm_search_menu_invoke(bContext *C, wmOperator *op, const wmEvent *event) { /* Exception for launching via spacebar */ if (event->type == EVT_SPACEKEY) { @@ -1775,9 +1790,20 @@ static int wm_search_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEv } } + PropertyRNA *prop = op->type->prop; + int search_type; + if (RNA_property_is_set(op->ptr, prop)) { + search_type = RNA_property_enum_get(op->ptr, prop); + } + else { + search_type = U.experimental.use_menu_search ? SEARCH_TYPE_MENU : SEARCH_TYPE_OPERATOR; + } + static struct SearchPopupInit_Data data; - data.size[0] = UI_searchbox_size_x() * 2; - data.size[1] = UI_searchbox_size_y(); + data = (struct SearchPopupInit_Data){ + .search_type = search_type, + .size = {UI_searchbox_size_x() * 2, UI_searchbox_size_y()}, + }; UI_popup_block_invoke(C, wm_block_search_menu, &data, NULL); @@ -1793,6 +1819,15 @@ static void WM_OT_search_menu(wmOperatorType *ot) ot->invoke = wm_search_menu_invoke; ot->exec = wm_search_menu_exec; ot->poll = WM_operator_winactive; + + static const EnumPropertyItem search_type_items[] = { + {SEARCH_TYPE_OPERATOR, "OPERATOR", 0, "Operator", "Search all operators"}, + {SEARCH_TYPE_MENU, "MENU", 0, "Menu", "Search active menu items"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", search_type_items, SEARCH_TYPE_OPERATOR, "Type", ""); } static int wm_call_menu_exec(bContext *C, wmOperator *op) |