319 lines
12 KiB
C++
319 lines
12 KiB
C++
/* 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 "BLT_translation.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<TemplateSearch *>(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<TemplateSearch>(__func__, template_search),
|
|
ui_description,
|
|
template_search.use_previews,
|
|
editable,
|
|
live_icon,
|
|
but_func_argN_free<TemplateSearch>,
|
|
but_func_argN_copy<TemplateSearch>);
|
|
}
|
|
|
|
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,
|
|
const std::optional<StringRefNull> button_text = {})
|
|
{
|
|
if (!operator_name) {
|
|
return;
|
|
}
|
|
|
|
uiBut *but;
|
|
if (button_text) {
|
|
const int button_width = std::max(
|
|
UI_fontstyle_string_width(UI_FSTYLE_WIDGET, button_text->c_str()) + int(UI_UNIT_X * 1.5f),
|
|
UI_UNIT_X * 5);
|
|
|
|
but = uiDefIconTextButO(block,
|
|
UI_BTYPE_BUT,
|
|
operator_name,
|
|
opcontext,
|
|
icon,
|
|
*button_text,
|
|
0,
|
|
0,
|
|
button_width,
|
|
UI_UNIT_Y,
|
|
std::nullopt);
|
|
}
|
|
else {
|
|
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<StringRef> 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);
|
|
|
|
/* For Blender 4.4, the "New" button is only shown on Action Slot selectors.
|
|
* Blender 4.5 may have this enabled for all uses of this template, in which
|
|
* case this type-specific code will be removed. */
|
|
const bool may_show_new_button = (type == &RNA_ActionSlot);
|
|
if (may_show_new_button && !active_ptr.data) {
|
|
template_search_add_button_operator(
|
|
block, newop, WM_OP_INVOKE_DEFAULT, ICON_ADD, editable, IFACE_("New"));
|
|
}
|
|
else {
|
|
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<StringRef> 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<StringRef> 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);
|
|
}
|
|
}
|