diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2017-05-12 02:42:42 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2017-05-12 02:47:55 +0300 |
commit | c20c203b82260c06888c2a535c08ec383923ee8a (patch) | |
tree | bc6dba9408a4c37d2105c9a91f4dd5d29f077424 | |
parent | c8ab7d46566c4a65c6176cd2ebe27778512f7fef (diff) |
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
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 11 | ||||
-rw-r--r-- | source/blender/editors/interface/interface.c | 21 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 3 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 19 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_layout.c | 102 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 421 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_utils.c | 85 | ||||
-rw-r--r-- | source/blender/makesrna/RNA_access.h | 4 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_ID.c | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_access.c | 5 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_ui_api.c | 26 | ||||
-rw-r--r-- | source/blenderplayer/bad_level_call_stubs/stubs.c | 11 |
12 files changed, 518 insertions, 192 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index ee15413b4d0..b3c95aa0712 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -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); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index e1ca6c60a74..562a2f6e32c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -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); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index ca0a5ffcbfc..6dcdb312256 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -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; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 68bb48f66fb..ace3bb5b4f8 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -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__ */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 30a2094fee7..5462043c961 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -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; } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index e3a71feb1a9..266d8aacae3 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -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 ********************/ /* ---------- */ diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 636b7e4e9ce..f2e79ec0c7f 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -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 *******************************/ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 1a6b3008a79..7f919ef058a 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -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); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index a74758a4f71..4291e5b63ae 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -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; diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index bd91315ed11..facb7116b67 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -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); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 6ef4bc6a371..abad70a46d2 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -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); diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 642140d1b00..8790e73d039 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -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 |