/* SPDX-FileCopyrightText: 2024 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup edinterface */ #include "BLI_string_ref.hh" #include "RNA_access.hh" #include "RNA_prototypes.hh" #include "UI_interface.hh" #include "interface_intern.hh" #include "interface_templates_intern.hh" using blender::StringRef; using blender::StringRefNull; struct TemplateSearch { uiRNACollectionSearch search_data; bool use_previews; int preview_rows, preview_cols; }; static void template_search_exec_fn(bContext *C, void *arg_template, void *item) { TemplateSearch *template_search = static_cast(arg_template); uiRNACollectionSearch *coll_search = &template_search->search_data; StructRNA *type = RNA_property_pointer_type(&coll_search->target_ptr, coll_search->target_prop); PointerRNA item_ptr = RNA_pointer_create_discrete(nullptr, type, item); RNA_property_pointer_set(&coll_search->target_ptr, coll_search->target_prop, item_ptr, nullptr); RNA_property_update(C, &coll_search->target_ptr, coll_search->target_prop); } static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_template) { static TemplateSearch template_search; /* arg_template is malloced, can be freed by parent button */ template_search = *((TemplateSearch *)arg_template); PointerRNA active_ptr = RNA_property_pointer_get(&template_search.search_data.target_ptr, template_search.search_data.target_prop); return template_common_search_menu(C, region, ui_rna_collection_search_update_fn, &template_search, template_search_exec_fn, active_ptr.data, nullptr, template_search.preview_rows, template_search.preview_cols, 1.0f); } static void template_search_add_button_searchmenu(const bContext *C, uiLayout *layout, uiBlock *block, TemplateSearch &template_search, const bool editable, const bool live_icon) { const StringRef ui_description = RNA_property_ui_description( template_search.search_data.target_prop); template_add_button_search_menu(C, layout, block, &template_search.search_data.target_ptr, template_search.search_data.target_prop, template_search_menu, MEM_new(__func__, template_search), ui_description, template_search.use_previews, editable, live_icon, but_func_argN_free, but_func_argN_copy); } static void template_search_add_button_name(uiBlock *block, PointerRNA *active_ptr, const StructRNA *type) { /* Skip text button without an active item. */ if (active_ptr->data == nullptr) { return; } PropertyRNA *name_prop; if (type == &RNA_ActionSlot) { name_prop = RNA_struct_find_property(active_ptr, "name_display"); } else { name_prop = RNA_struct_name_property(type); } const int width = template_search_textbut_width(active_ptr, name_prop); const int height = template_search_textbut_height(); uiDefAutoButR(block, active_ptr, name_prop, 0, "", ICON_NONE, 0, 0, width, height); } static void template_search_add_button_operator(uiBlock *block, const char *const operator_name, const wmOperatorCallContext opcontext, const int icon, const bool editable) { if (!operator_name) { return; } uiBut *but = uiDefIconButO(block, UI_BTYPE_BUT, operator_name, opcontext, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, std::nullopt); if (!editable) { UI_but_drawflag_enable(but, UI_BUT_DISABLED); } } static void template_search_buttons(const bContext *C, uiLayout *layout, TemplateSearch &template_search, const char *newop, const char *unlinkop, const std::optional text) { uiBlock *block = uiLayoutGetBlock(layout); uiRNACollectionSearch *search_data = &template_search.search_data; const StructRNA *type = RNA_property_pointer_type(&search_data->target_ptr, search_data->target_prop); const bool editable = RNA_property_editable(&search_data->target_ptr, search_data->target_prop); PointerRNA active_ptr = RNA_property_pointer_get(&search_data->target_ptr, search_data->target_prop); if (active_ptr.type) { /* can only get correct type when there is an active item */ type = active_ptr.type; } uiLayout *row = uiLayoutRow(layout, true); UI_block_align_begin(block); uiLayout *decorator_layout = nullptr; if (text && !text->is_empty()) { /* Add label respecting the separated layout property split state. */ decorator_layout = uiItemL_respect_property_split(row, *text, ICON_NONE); } template_search_add_button_searchmenu(C, row, block, template_search, editable, false); template_search_add_button_name(block, &active_ptr, type); template_search_add_button_operator( block, newop, WM_OP_INVOKE_DEFAULT, ICON_DUPLICATE, editable); template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable); UI_block_align_end(block); if (decorator_layout) { uiItemDecoratorR(decorator_layout, nullptr, "", RNA_NO_INDEX); } } static PropertyRNA *template_search_get_searchprop(PointerRNA *targetptr, PropertyRNA *targetprop, PointerRNA *searchptr, const char *const searchpropname) { PropertyRNA *searchprop; if (searchptr && !searchptr->data) { searchptr = nullptr; } if (!searchptr && !searchpropname) { /* both nullptr means we don't use a custom rna collection to search in */ } else if (!searchptr && searchpropname) { RNA_warning("searchpropname defined (%s) but searchptr is missing", searchpropname); } else if (searchptr && !searchpropname) { RNA_warning("searchptr defined (%s) but searchpropname is missing", RNA_struct_identifier(searchptr->type)); } else if (!(searchprop = RNA_struct_find_property(searchptr, searchpropname))) { RNA_warning("search collection property not found: %s.%s", RNA_struct_identifier(searchptr->type), searchpropname); } else if (RNA_property_type(searchprop) != PROP_COLLECTION) { RNA_warning("search collection property is not a collection type: %s.%s", RNA_struct_identifier(searchptr->type), searchpropname); } /* check if searchprop has same type as targetprop */ else if (RNA_property_pointer_type(searchptr, searchprop) != RNA_property_pointer_type(targetptr, targetprop)) { RNA_warning("search collection items from %s.%s are not of type %s", RNA_struct_identifier(searchptr->type), searchpropname, RNA_struct_identifier(RNA_property_pointer_type(targetptr, targetprop))); } else { return searchprop; } return nullptr; } static bool template_search_setup(TemplateSearch &template_search, PointerRNA *ptr, const StringRefNull propname, PointerRNA *searchptr, const char *const searchpropname) { template_search = {}; PropertyRNA *prop = RNA_struct_find_property(ptr, propname.c_str()); if (!prop || RNA_property_type(prop) != PROP_POINTER) { RNA_warning( "pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname.c_str()); return false; } PropertyRNA *searchprop = template_search_get_searchprop(ptr, prop, searchptr, searchpropname); template_search.search_data.target_ptr = *ptr; template_search.search_data.target_prop = prop; template_search.search_data.search_ptr = *searchptr; template_search.search_data.search_prop = searchprop; return true; } void uiTemplateSearch(uiLayout *layout, const bContext *C, PointerRNA *ptr, const StringRefNull propname, PointerRNA *searchptr, const char *searchpropname, const char *newop, const char *unlinkop, const std::optional text) { TemplateSearch template_search; if (template_search_setup(template_search, ptr, propname, searchptr, searchpropname)) { template_search_buttons(C, layout, template_search, newop, unlinkop, text); } } void uiTemplateSearchPreview(uiLayout *layout, bContext *C, PointerRNA *ptr, const StringRefNull propname, PointerRNA *searchptr, const char *searchpropname, const char *newop, const char *unlinkop, const int rows, const int cols, const std::optional text) { TemplateSearch template_search; if (template_search_setup(template_search, ptr, propname, searchptr, searchpropname)) { template_search.use_previews = true; template_search.preview_rows = rows; template_search.preview_cols = cols; template_search_buttons(C, layout, template_search, newop, unlinkop, text); } }