UI: Add template_search (version of template_ID for non-IDs)

Adds a version of template_ID that can be used for non-ID properties.
The property to search for and the collection to search in has to be
passed to it.
Like template_ID it also takes arguments to define a 'new' and 'unlink'
operator. They will be displayed as icon-only buttons then.
Also added a version that can display preview thumbnails.

Had to do some additional changes to make text-buttons support
displaying/modifying empty RNA properties.

This will be needed for workspaces, see D2451.

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D2666
This commit is contained in:
Julian Eisel
2017-05-12 01:42:42 +02:00
parent c8ab7d4656
commit c20c203b82
12 changed files with 518 additions and 192 deletions

View File

@@ -912,6 +912,17 @@ void uiTemplateIDPreview(uiLayout *layout, struct bContext *C, struct PointerRNA
const char *newop, const char *openop, const char *unlinkop, int rows, int cols);
void uiTemplateAnyID(uiLayout *layout, struct PointerRNA *ptr, const char *propname,
const char *proptypename, const char *text);
void uiTemplateSearch(
uiLayout *layout, struct bContext *C,
struct PointerRNA *ptr, const char *propname,
struct PointerRNA *searchptr, const char *searchpropname,
const char *newop, const char *unlinkop);
void uiTemplateSearchPreview(
uiLayout *layout, struct bContext *C,
struct PointerRNA *ptr, const char *propname,
struct PointerRNA *searchptr, const char *searchpropname,
const char *newop, const char *unlinkop,
const int rows, const int cols);
void uiTemplatePathBuilder(uiLayout *layout, struct PointerRNA *ptr, const char *propname,
struct PointerRNA *root_ptr, const char *text);
uiLayout *uiTemplateModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr);

View File

@@ -2373,7 +2373,7 @@ static void ui_but_string_free_internal(uiBut *but)
bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
{
if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
if (but->rnaprop && but->rnapoin.data && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
if (RNA_property_editable(&but->rnapoin, but->rnaprop)) {
PropertyType type;
@@ -2420,8 +2420,15 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
}
else if (but->type == UI_BTYPE_TEXT) {
/* string */
if (ui_but_is_utf8(but)) BLI_strncpy_utf8(but->poin, str, but->hardmax);
else BLI_strncpy(but->poin, str, but->hardmax);
if (!but->poin || (str[0] == '\0')) {
str = "";
}
else if (ui_but_is_utf8(but)) {
BLI_strncpy_utf8(but->poin, str, but->hardmax);
}
else {
BLI_strncpy(but->poin, str, but->hardmax);
}
return true;
}
@@ -2627,6 +2634,10 @@ static void ui_but_free(const bContext *C, uiBut *but)
MEM_freeN(but->tip_argN);
}
if (!but->editstr && but->free_search_arg) {
MEM_SAFE_FREE(but->search_arg);
}
if (but->active) {
/* XXX solve later, buttons should be free-able without context ideally,
* however they may have open tooltips or popup windows, which need to
@@ -3499,7 +3510,7 @@ static uiBut *ui_def_but_rna(
}
const char *info;
if (!RNA_property_editable_info(&but->rnapoin, prop, &info)) {
if (but->rnapoin.data && !RNA_property_editable_info(&but->rnapoin, prop, &info)) {
ui_def_but_rna__disable(but, info);
}
@@ -4522,7 +4533,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
tmp = BLI_strdup(RNA_property_identifier(but->rnaprop));
}
else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) {
if (but->rnaprop)
if (but->rnaprop && but->rnapoin.data)
tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type));
else if (but->optype)
tmp = BLI_strdup(but->optype->idname);

View File

@@ -3159,6 +3159,9 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
ui_searchbox_free(C, data->searchbox);
data->searchbox = NULL;
if (but->free_search_arg) {
MEM_SAFE_FREE(but->search_arg);
}
}
but->editstr = NULL;

View File

@@ -259,6 +259,7 @@ struct uiBut {
uiButSearchCreateFunc search_create_func;
uiButSearchFunc search_func;
bool free_search_arg;
void *search_arg;
uiButHandleRenameFunc rename_func;
@@ -278,7 +279,7 @@ struct uiBut {
BIFIconID icon;
char dt; /* drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the block */
signed char pie_dir; /* direction in a pie menu, used for collision detection (RadialDirection) */
char changed; /* could be made into a single flag */
bool changed; /* could be made into a single flag */
unsigned char unit_type; /* so buttons can support unit systems which are not RNA */
short modifier_key;
short iconadd;
@@ -748,4 +749,20 @@ void UI_OT_eyedropper_id(struct wmOperatorType *ot);
void UI_OT_eyedropper_depth(struct wmOperatorType *ot);
void UI_OT_eyedropper_driver(struct wmOperatorType *ot);
/* interface_util.c */
/**
* For use with #ui_rna_collection_search_cb.
*/
typedef struct uiRNACollectionSearch {
PointerRNA target_ptr;
PropertyRNA *target_prop;
PointerRNA search_ptr;
PropertyRNA *search_prop;
bool *but_changed; /* pointer to uiBut.changed */
} uiRNACollectionSearch;
void ui_rna_collection_search_cb(const struct bContext *C, void *arg, const char *str, uiSearchItems *items);
#endif /* __INTERFACE_INTERN_H__ */

View File

@@ -1574,95 +1574,6 @@ void uiItemsEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname
/* Pointer RNA button with search */
typedef struct CollItemSearch {
struct CollItemSearch *next, *prev;
char *name;
int index;
int iconid;
} CollItemSearch;
static int sort_search_items_list(const void *a, const void *b)
{
const CollItemSearch *cis1 = a;
const CollItemSearch *cis2 = b;
if (BLI_strcasecmp(cis1->name, cis2->name) > 0)
return 1;
else
return 0;
}
static void rna_search_cb(const struct bContext *C, void *arg_but, const char *str, uiSearchItems *items)
{
uiBut *but = arg_but;
char *name;
int i = 0, iconid = 0, flag = RNA_property_flag(but->rnaprop);
ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
CollItemSearch *cis;
const bool skip_filter = !but->changed;
/* build a temporary list of relevant items first */
RNA_PROP_BEGIN (&but->rnasearchpoin, itemptr, but->rnasearchprop)
{
if (flag & PROP_ID_SELF_CHECK)
if (itemptr.data == but->rnapoin.id.data)
continue;
/* use filter */
if (RNA_property_type(but->rnaprop) == PROP_POINTER) {
if (RNA_property_pointer_poll(&but->rnapoin, but->rnaprop, &itemptr) == 0)
continue;
}
if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
ID *id = itemptr.data;
char name_ui[MAX_ID_NAME];
#if 0 /* this name is used for a string comparison and can't be modified, TODO */
/* if ever enabled, make name_ui be MAX_ID_NAME+1 */
BKE_id_ui_prefix(name_ui, id);
#else
BLI_strncpy(name_ui, id->name + 2, sizeof(name_ui));
#endif
name = BLI_strdup(name_ui);
iconid = ui_id_icon_get(C, id, false);
}
else {
name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); /* could use the string length here */
iconid = 0;
}
if (name) {
if (skip_filter || BLI_strcasestr(name, str)) {
cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
cis->name = MEM_dupallocN(name);
cis->index = i;
cis->iconid = iconid;
BLI_addtail(items_list, cis);
}
MEM_freeN(name);
}
i++;
}
RNA_PROP_END;
BLI_listbase_sort(items_list, sort_search_items_list);
/* add search items from temporary list */
for (cis = items_list->first; cis; cis = cis->next) {
if (false == UI_search_item_add(items, cis->name, SET_INT_IN_POINTER(cis->index), cis->iconid)) {
break;
}
}
for (cis = items_list->first; cis; cis = cis->next) {
MEM_freeN(cis->name);
}
BLI_freelistN(items_list);
MEM_freeN(items_list);
}
static void search_id_collection(StructRNA *ptype, PointerRNA *ptr, PropertyRNA **prop)
{
StructRNA *srna;
@@ -1703,6 +1614,8 @@ void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRN
/* turn button into search button */
if (searchprop) {
uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__);
but->type = UI_BTYPE_SEARCH_MENU;
but->hardmax = MAX2(but->hardmax, 256.0f);
but->rnasearchpoin = *searchptr;
@@ -1712,13 +1625,22 @@ void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRN
but->flag |= UI_BUT_VALUE_CLEAR;
}
coll_search->target_ptr = *ptr;
coll_search->target_prop = prop;
coll_search->search_ptr = *searchptr;
coll_search->search_prop = searchprop;
coll_search->but_changed = &but->changed;
if (RNA_property_type(prop) == PROP_ENUM) {
/* XXX, this will have a menu string,
* but in this case we just want the text */
but->str[0] = 0;
}
UI_but_func_search_set(but, ui_searchbox_create_generic, rna_search_cb, but, NULL, NULL);
UI_but_func_search_set(
but, ui_searchbox_create_generic, ui_rna_collection_search_cb,
coll_search, NULL, NULL);
but->free_search_arg = true;
}
}

View File

@@ -89,10 +89,126 @@
#include "PIL_time.h"
/* defines for templateID/TemplateSearch */
#define TEMPLATE_SEARCH_TEXTBUT_WIDTH (UI_UNIT_X * 6)
#define TEMPLATE_SEARCH_TEXTBUT_HEIGHT UI_UNIT_Y
void UI_template_fix_linking(void)
{
}
/**
* Add a block button for the search menu for templateID and templateSearch.
*/
static void template_add_button_search_menu(
const bContext *C, uiLayout *layout, uiBlock *block,
PointerRNA *ptr, PropertyRNA *prop,
uiBlockCreateFunc block_func, void *block_argN, const char * const tip,
const bool use_previews, const bool editable)
{
PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop);
ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL;
const ID *idfrom = ptr->id.data;
const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop);
uiBut *but;
if (use_previews) {
ARegion *region = CTX_wm_region(C);
const bool use_big_size = (region->regiontype != RGN_TYPE_HEADER); /* silly check, could be more generic */
/* Ugly exception for screens here, drawing their preview in icon size looks ugly/useless */
const bool use_preview_icon = use_big_size || (id && (GS(id->name) != ID_SCR));
const short width = UI_UNIT_X * (use_big_size ? 6 : 1.6f);
const short height = UI_UNIT_Y * (use_big_size ? 6: 1);
but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip);
if (use_preview_icon) {
int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type);
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
else {
ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
}
if ((idfrom && idfrom->lib) || !editable)
UI_but_flag_enable(but, UI_BUT_DISABLED);
if (use_big_size) {
uiLayoutRow(layout, true);
}
}
else {
but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip);
ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
if (id) {
/* default dragging of icon for id browse buttons */
UI_but_drag_set_id(but, id);
}
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
if ((idfrom && idfrom->lib) || !editable)
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
static uiBlock *template_common_search_menu(
const bContext *C, ARegion *region,
uiButSearchFunc search_func, void *search_arg,
uiButHandleFunc handle_func, void *active_item,
const int preview_rows, const int preview_cols)
{
static char search[256];
wmWindow *win = CTX_wm_window(C);
uiBlock *block;
uiBut *but;
/* clear initial search string, then all items show */
search[0] = 0;
block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU);
/* preview thumbnails */
if (preview_rows > 0 && preview_cols > 0) {
const int w = 4 * U.widget_unit * preview_cols;
const int h = 5 * U.widget_unit * preview_rows;
/* fake button, it holds space for search items */
uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL);
but = uiDefSearchBut(
block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, w, UI_UNIT_Y,
preview_rows, preview_cols, "");
}
/* list view */
else {
const int searchbox_width = UI_searchbox_size_x();
const int searchbox_height = UI_searchbox_size_y();
/* fake button, it holds space for search items */
uiDefBut(
block, UI_BTYPE_LABEL, 0, "", 10, 15, searchbox_width, searchbox_height,
NULL, 0, 0, 0, 0, NULL);
but = uiDefSearchBut(
block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0,
searchbox_width, UI_UNIT_Y - 1, 0, 0, "");
}
UI_but_func_search_set(
but, ui_searchbox_create_generic, search_func,
search_arg, handle_func, active_item);
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_DOWN);
/* give search-field focus */
UI_but_focus_on_enter_event(win, but);
/* this type of search menu requires undo */
but->flag |= UI_BUT_UNDO;
return block;
}
/********************** Header Template *************************/
void uiTemplateHeader(uiLayout *layout, bContext *C)
@@ -174,61 +290,16 @@ static void id_search_cb(const bContext *C, void *arg_template, const char *str,
/* ID Search browse menu, open */
static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem)
{
static char search[256];
static TemplateID template;
PointerRNA idptr;
wmWindow *win = CTX_wm_window(C);
uiBlock *block;
uiBut *but;
/* clear initial search string, then all items show */
search[0] = 0;
PointerRNA active_item_ptr;
/* arg_litem is malloced, can be freed by parent button */
template = *((TemplateID *)arg_litem);
/* get active id for showing first item */
idptr = RNA_property_pointer_get(&template.ptr, template.prop);
active_item_ptr = RNA_property_pointer_get(&template.ptr, template.prop);
block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU);
/* preview thumbnails */
if (template.prv_rows > 0 && template.prv_cols > 0) {
int w = 4 * U.widget_unit * template.prv_cols;
int h = 5 * U.widget_unit * template.prv_rows;
/* fake button, it holds space for search items */
uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL);
but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, w, UI_UNIT_Y,
template.prv_rows, template.prv_cols, "");
UI_but_func_search_set(
but, ui_searchbox_create_generic, id_search_cb,
&template, id_search_call_cb, idptr.data);
}
/* list view */
else {
const int searchbox_width = UI_searchbox_size_x();
const int searchbox_height = UI_searchbox_size_y();
/* fake button, it holds space for search items */
uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 15, searchbox_width, searchbox_height, NULL, 0, 0, 0, 0, NULL);
but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, searchbox_width, UI_UNIT_Y - 1, 0, 0, "");
UI_but_func_search_set(
but, ui_searchbox_create_generic, id_search_cb,
&template, id_search_call_cb, idptr.data);
}
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_DOWN);
/* give search-field focus */
UI_but_focus_on_enter_event(win, but);
/* this type of search menu requires undo */
but->flag |= UI_BUT_UNDO;
return block;
return template_common_search_menu(
C, ar, id_search_cb, &template, id_search_call_cb, active_item_ptr.data,
template.prv_rows, template.prv_cols);
}
/************************ ID Template ***************************/
@@ -345,7 +416,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
}
}
static const char *template_id_browse_tip(StructRNA *type)
static const char *template_id_browse_tip(const StructRNA *type)
{
if (type) {
switch (RNA_type_to_ID_code(type)) {
@@ -406,6 +477,7 @@ static void template_ID(
// ListBase *lb; // UNUSED
ID *id, *idfrom;
const bool editable = RNA_property_editable(&template->ptr, template->prop);
const bool use_previews = template->preview = (flag & UI_ID_PREVIEWS) != 0;
idptr = RNA_property_pointer_get(&template->ptr, template->prop);
id = idptr.data;
@@ -418,43 +490,11 @@ static void template_ID(
if (idptr.type)
type = idptr.type;
if (flag & UI_ID_PREVIEWS) {
ARegion *region = CTX_wm_region(C);
const bool use_big_size = (region->regiontype != RGN_TYPE_HEADER); /* silly check, could be more generic */
/* Ugly exception for screens here, drawing their preview in icon size looks ugly/useless */
const bool use_preview_icon = use_big_size || (id && (GS(id->name) != ID_SCR));
const short width = UI_UNIT_X * (use_big_size ? 6 : 1.6f);
const short height = UI_UNIT_Y * (use_big_size ? 6: 1);
template->preview = true;
but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, width, height,
TIP_(template_id_browse_tip(type)));
if (use_preview_icon) {
int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type);
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
else {
ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
}
if ((idfrom && idfrom->lib) || !editable)
UI_but_flag_enable(but, UI_BUT_DISABLED);
if (use_big_size) {
uiLayoutRow(layout, true);
}
}
else if (flag & UI_ID_BROWSE) {
but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y,
TIP_(template_id_browse_tip(type)));
ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
/* default dragging of icon for id browse buttons */
UI_but_drag_set_id(but, id);
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
if ((idfrom && idfrom->lib) || !editable)
UI_but_flag_enable(but, UI_BUT_DISABLED);
if (flag & UI_ID_BROWSE) {
template_add_button_search_menu(
C, layout, block, &template->ptr, template->prop,
id_search_menu, MEM_dupallocN(template), TIP_(template_id_browse_tip(type)),
use_previews, editable);
}
/* text button with name */
@@ -464,8 +504,9 @@ static void template_ID(
//text_idbutton(id, name);
name[0] = '\0';
but = uiDefButR(block, UI_BTYPE_TEXT, 0, name, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y,
&idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type));
but = uiDefButR(
block, UI_BTYPE_TEXT, 0, name, 0, 0, TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_HEIGHT,
&idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type));
UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_RENAME));
if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT);
@@ -751,6 +792,204 @@ void uiTemplateAnyID(
uiItemFullR(sub, ptr, propID, 0, 0, 0, "", ICON_NONE);
}
/********************* Search Template ********************/
typedef struct TemplateSearch {
uiRNACollectionSearch search_data;
bool use_previews;
int preview_rows, preview_cols;
} TemplateSearch;
static void template_search_handle_cb(bContext *C, void *arg_template, void *item)
{
TemplateSearch *template_search = 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(NULL, type, item, &item_ptr);
RNA_property_pointer_set(&coll_search->target_ptr, coll_search->target_prop, item_ptr);
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;
PointerRNA active_ptr;
/* arg_template is malloced, can be freed by parent button */
template_search = *((TemplateSearch *)arg_template);
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_cb, &template_search,
template_search_handle_cb, active_ptr.data,
template_search.preview_rows, template_search.preview_cols);
}
static void template_search_add_button_searchmenu(
const bContext *C, uiLayout *layout, uiBlock *block,
TemplateSearch *template_search, const bool editable)
{
const char *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_dupallocN(template_search), ui_description,
template_search->use_previews, editable);
}
static void template_search_add_button_name(
uiBlock *block, PointerRNA *active_ptr, const StructRNA *type)
{
uiDefAutoButR(
block, active_ptr, RNA_struct_name_property(type), 0, "", ICON_NONE,
0, 0, TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_HEIGHT);
}
static void template_search_add_button_operator(
uiBlock *block, const char * const operator_name,
const int 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, NULL);
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)
{
uiBlock *block = uiLayoutGetBlock(layout);
uiRNACollectionSearch *search_data = &template_search->search_data;
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;
}
uiLayoutRow(layout, true);
UI_block_align_begin(block);
template_search_add_button_searchmenu(C, layout, block, template_search, editable);
template_search_add_button_name(block, &active_ptr, type);
template_search_add_button_operator(block, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, editable);
template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable);
UI_block_align_end(block);
}
static PropertyRNA *template_search_get_searchprop(
PointerRNA *targetptr, PropertyRNA *targetprop,
PointerRNA *searchptr, const char * const searchpropname)
{
PropertyRNA *searchprop;
if (searchptr && !searchptr->data) {
searchptr = NULL;
}
if (!searchptr && !searchpropname) {
/* both NULL 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 NULL;
}
static TemplateSearch *template_search_setup(
PointerRNA *ptr, const char * const propname,
PointerRNA *searchptr, const char * const searchpropname)
{
TemplateSearch *template_search;
PropertyRNA *prop, *searchprop;
prop = RNA_struct_find_property(ptr, propname);
if (!prop || RNA_property_type(prop) != PROP_POINTER) {
RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
return NULL;
}
searchprop = template_search_get_searchprop(ptr, prop, searchptr, searchpropname);
template_search = MEM_callocN(sizeof(*template_search), __func__);
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 template_search;
}
/**
* Search menu to pick an item from a collection.
* A version of uiTemplateID that works for non-ID types.
*/
void uiTemplateSearch(
uiLayout *layout, bContext *C,
PointerRNA *ptr, const char *propname,
PointerRNA *searchptr, const char *searchpropname,
const char *newop, const char *unlinkop)
{
TemplateSearch *template_search = template_search_setup(ptr, propname, searchptr, searchpropname);
template_search_buttons(C, layout, template_search, newop, unlinkop);
MEM_freeN(template_search);
}
void uiTemplateSearchPreview(
uiLayout *layout, bContext *C,
PointerRNA *ptr, const char *propname,
PointerRNA *searchptr, const char *searchpropname,
const char *newop, const char *unlinkop,
const int rows, const int cols)
{
TemplateSearch *template_search = template_search_setup(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);
MEM_freeN(template_search);
}
/********************* RNA Path Builder Template ********************/
/* ---------- */

View File

@@ -214,6 +214,91 @@ int uiDefAutoButsRNA(
return tot;
}
/* *** RNA collection search menu *** */
typedef struct CollItemSearch {
struct CollItemSearch *next, *prev;
ID *id;
char *name;
int index;
int iconid;
} CollItemSearch;
static int sort_search_items_list(const void *a, const void *b)
{
const CollItemSearch *cis1 = a;
const CollItemSearch *cis2 = b;
if (BLI_strcasecmp(cis1->name, cis2->name) > 0)
return 1;
else
return 0;
}
void ui_rna_collection_search_cb(const struct bContext *C, void *arg, const char *str, uiSearchItems *items)
{
uiRNACollectionSearch *data = arg;
char *name;
int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop);
ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
CollItemSearch *cis;
const bool skip_filter = !(data->but_changed && *data->but_changed);
/* build a temporary list of relevant items first */
RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop)
{
ID *id = NULL;
if (flag & PROP_ID_SELF_CHECK)
if (itemptr.data == data->target_ptr.id.data)
continue;
/* use filter */
if (RNA_property_type(data->target_prop) == PROP_POINTER) {
if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0)
continue;
}
name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); /* could use the string length here */
iconid = 0;
if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
id = itemptr.data;
iconid = ui_id_icon_get(C, id, false);
}
if (name) {
if (skip_filter || BLI_strcasestr(name, str)) {
cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
cis->id = id;
cis->name = MEM_dupallocN(name);
cis->index = i;
cis->iconid = iconid;
BLI_addtail(items_list, cis);
}
MEM_freeN(name);
}
i++;
}
RNA_PROP_END;
BLI_listbase_sort(items_list, sort_search_items_list);
/* add search items from temporary list */
for (cis = items_list->first; cis; cis = cis->next) {
void *poin = cis->id ? cis->id : SET_INT_IN_POINTER(cis->index);
if (UI_search_item_add(items, cis->name, poin, cis->iconid) == false) {
break;
}
}
for (cis = items_list->first; cis; cis = cis->next) {
MEM_freeN(cis->name);
}
BLI_freelistN(items_list);
MEM_freeN(items_list);
}
/***************************** ID Utilities *******************************/

View File

@@ -751,7 +751,7 @@ const char *RNA_struct_ui_description_raw(const StructRNA *type);
const char *RNA_struct_translation_context(const StructRNA *type);
int RNA_struct_ui_icon(const StructRNA *type);
PropertyRNA *RNA_struct_name_property(StructRNA *type);
PropertyRNA *RNA_struct_name_property(const StructRNA *type);
PropertyRNA *RNA_struct_iterator_property(StructRNA *type);
StructRNA *RNA_struct_base(StructRNA *type);
@@ -1191,7 +1191,7 @@ int RNA_function_call_direct_va_lookup(struct bContext *C, struct ReportList *re
/* ID */
short RNA_type_to_ID_code(StructRNA *type);
short RNA_type_to_ID_code(const StructRNA *type);
StructRNA *ID_code_to_RNA_type(short idcode);

View File

@@ -136,7 +136,7 @@ static int rna_ID_name_editable(PointerRNA *ptr, const char **UNUSED(r_info))
return PROP_EDITABLE;
}
short RNA_type_to_ID_code(StructRNA *type)
short RNA_type_to_ID_code(const StructRNA *type)
{
if (RNA_struct_is_a(type, &RNA_Action)) return ID_AC;
if (RNA_struct_is_a(type, &RNA_Armature)) return ID_AR;

View File

@@ -560,7 +560,7 @@ const char *RNA_struct_translation_context(const StructRNA *type)
return type->translation_context;
}
PropertyRNA *RNA_struct_name_property(StructRNA *type)
PropertyRNA *RNA_struct_name_property(const StructRNA *type)
{
return type->nameproperty;
}
@@ -2765,6 +2765,9 @@ char *RNA_property_string_get_alloc(PointerRNA *ptr, PropertyRNA *prop,
int length;
BLI_assert(RNA_property_type(prop) == PROP_STRING);
if (!ptr->data) {
return NULL;
}
length = RNA_property_string_length(ptr, prop);

View File

@@ -671,7 +671,31 @@ void RNA_api_ui_layout(StructRNA *srna)
"Identifier of property in data giving the type of the ID-blocks to use");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
api_ui_item_common_text(func);
func = RNA_def_function(srna, "template_search", "uiTemplateSearch");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
api_ui_item_rna_common(func);
parm = RNA_def_pointer(func, "search_data", "AnyType", "", "Data from which to take collection to search in");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
parm = RNA_def_string(func, "search_property", NULL, 0, "", "Identifier of search collection property");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_string(func, "new", NULL, 0, "", "Operator identifier to create a new item for the collection");
RNA_def_string(func, "unlink", NULL, 0, "", "Operator identifier to unlink or delete the active "
"item from the collection");
func = RNA_def_function(srna, "template_search_preview", "uiTemplateSearchPreview");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
api_ui_item_rna_common(func);
parm = RNA_def_pointer(func, "search_data", "AnyType", "", "Data from which to take collection to search in");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
parm = RNA_def_string(func, "search_property", NULL, 0, "", "Identifier of search collection property");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_string(func, "new", NULL, 0, "", "Operator identifier to create a new item for the collection");
RNA_def_string(func, "unlink", NULL, 0, "", "Operator identifier to unlink or delete the active "
"item from the collection");
RNA_def_int(func, "rows", 0, 0, INT_MAX, "Number of thumbnail preview rows to display", "", 0, INT_MAX);
RNA_def_int(func, "cols", 0, 0, INT_MAX, "Number of thumbnail preview columns to display", "", 0, INT_MAX);
func = RNA_def_function(srna, "template_path_builder", "rna_uiTemplatePathBuilder");
parm = RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take property");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);

View File

@@ -623,6 +623,17 @@ struct uiLayout *uiTemplateConstraint(struct uiLayout *layout, struct PointerRNA
void uiTemplatePreview(struct uiLayout *layout, struct bContext *C, struct ID *id, int show_buttons, struct ID *parent,
struct MTex *slot, const char *preview_id) RET_NONE
void uiTemplateIDPreview(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, int rows, int cols) RET_NONE
void uiTemplateSearch(
uiLayout *layout, struct bContext *C,
PointerRNA *ptr, const char *propname,
PointerRNA *searchptr, const char *searchpropname,
const char *newop, const char *unlinkop) RET_NONE
void uiTemplateSearchPreview(
uiLayout *layout, struct bContext *C,
PointerRNA *ptr, const char *propname,
PointerRNA *searchptr, const char *searchpropname,
const char *newop, const char *unlinkop,
const int rows, const int cols) RET_NONE
void uiTemplateCurveMapping(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int type, int levels, int brush, int neg_slope) RET_NONE
void uiTemplateColorRamp(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int expand) RET_NONE
void uiTemplateLayers(uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *used_ptr, const char *used_propname, int active_layer) RET_NONE