UI Code Quality: Use derived structs for search buttons and decorators

The current on-size-fits-all `uiBut` creates quite a mess, where it's
hard to reason about which members are free for use, under which
conditions they are used and how.
`uiBut` also has members that aren't used at times, violating the "don't
pay for what you don't use" principle.

To address this, we want to move to typed buttons, where `uiBut` is just
a base struct and each type extends it as needed. That structures data
better and type specific data is only available if it's actually used by
a button type.

Two trade-offs:
* Many casts to the derived type have to be done.
* Sometimes we change the button type after it's created. So I had to
  add logic to reallocate the button for use with the new, possibly
  derived struct. Ideally that wouldn't be needed, but for now that's
  what we have.

Part of T74432.

Differential Revision: https://developer.blender.org/D7610

Reviewed by: Brecht Van Lommel, Campbell Barton
This commit is contained in:
Julian Eisel 2020-08-07 14:34:11 +02:00
parent 48e089375e
commit 49f088e2d0
Notes: blender-bot 2023-02-14 10:18:56 +01:00
Referenced by commit 766340856d, UI Code Quality: Use derived struct for hot-key buttons
Referenced by commit e6f0b60c2e, UI Code Quality: Use derived struct for number buttons
Referenced by commit 570044e9f4, UI Code Quality: Use derived struct for curve profile buttons
Referenced by commit dcff28e1e7, UI Code Quality: Use derived struct for HSV Cube buttons
Referenced by issue #80854, Search buttons with pointer properties fail in Adjust Last Operation Panel (e.g. cannot change ShapeKey on Blend From Shape operator)
12 changed files with 385 additions and 174 deletions

View File

@ -57,6 +57,7 @@ struct bNodeSocket;
struct bNodeTree;
struct bScreen;
struct rcti;
struct uiButSearch;
struct uiFontStyle;
struct uiList;
struct uiStyle;
@ -371,6 +372,7 @@ typedef enum {
UI_BTYPE_SEPR_SPACER = 56 << 9,
/** Resize handle (resize uilist). */
UI_BTYPE_GRIP = 57 << 9,
UI_BTYPE_DECORATOR = 58 << 9,
} eButType;
#define BUTTYPE (63 << 9)
@ -500,7 +502,7 @@ typedef int (*uiButCompleteFunc)(struct bContext *C, char *str, void *arg);
/* Search types. */
typedef struct ARegion *(*uiButSearchCreateFn)(struct bContext *C,
struct ARegion *butregion,
uiBut *but);
struct uiButSearch *search_but);
typedef void (*uiButSearchUpdateFn)(const struct bContext *C,
void *arg,
const char *str,
@ -537,7 +539,7 @@ typedef bool (*uiMenuStepFunc)(struct bContext *C, int direction, void *arg1);
bool UI_but_has_tooltip_label(const uiBut *but);
bool UI_but_is_tool(const uiBut *but);
bool UI_but_is_utf8(const uiBut *but);
#define UI_but_is_decorator(but) ((but)->func == ui_but_anim_decorate_cb)
#define UI_but_is_decorator(but) ((but)->type == UI_BTYPE_DECORATOR)
bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title);
bool UI_block_is_empty(const uiBlock *block);
@ -1929,8 +1931,6 @@ uiLayout *uiLayoutGridFlow(uiLayout *layout,
uiLayout *uiLayoutBox(uiLayout *layout);
uiLayout *uiLayoutListBox(uiLayout *layout,
struct uiList *ui_list,
struct PointerRNA *ptr,
struct PropertyRNA *prop,
struct PointerRNA *actptr,
struct PropertyRNA *actprop);
uiLayout *uiLayoutAbsolute(uiLayout *layout, bool align);

View File

@ -807,7 +807,12 @@ static bool ui_but_update_from_old_block(const bContext *C,
SWAP(ListBase, but->extra_op_icons, oldbut->extra_op_icons);
SWAP(struct uiButSearchData *, oldbut->search, but->search);
if (oldbut->type == UI_BTYPE_SEARCH_MENU) {
uiButSearch *search_oldbut = (uiButSearch *)oldbut, *search_but = (uiButSearch *)but;
SWAP(uiButSearchArgFreeFn, search_oldbut->arg_free_fn, search_but->arg_free_fn);
SWAP(void *, search_oldbut->arg, search_but->arg);
}
/* copy hardmin for list rows to prevent 'sticking' highlight to mouse position
* when scrolling without moving mouse (see [#28432]) */
@ -1773,7 +1778,7 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x
ui_but_anim_flag(but, &anim_eval_context);
ui_but_override_flag(CTX_data_main(C), but);
if (UI_but_is_decorator(but)) {
ui_but_anim_decorate_update_from_flag(but);
ui_but_anim_decorate_update_from_flag((uiButDecorator *)but);
}
ui_but_predefined_extra_operator_icons_add(but);
}
@ -2021,6 +2026,7 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
case UI_BTYPE_HOTKEY_EVENT:
case UI_BTYPE_KEY_EVENT:
case UI_BTYPE_COLOR:
case UI_BTYPE_DECORATOR:
is_push = -1;
break;
case UI_BTYPE_BUT_TOGGLE:
@ -2945,10 +2951,10 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
RNA_property_pointer_set(&but->rnapoin, but->rnaprop, PointerRNA_NULL, NULL);
return true;
}
uiButSearch *search_but = (but->type == UI_BTYPE_SEARCH_MENU) ? (uiButSearch *)but : NULL;
/* RNA pointer */
PointerRNA rptr;
PointerRNA ptr = but->rnasearchpoin;
PropertyRNA *prop = but->rnasearchprop;
/* This is kind of hackish, in theory think we could only ever use the second member of
* this if/else, since ui_searchbox_apply() is supposed to always set that pointer when
@ -2956,12 +2962,16 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
* to try to break as little as possible existing code. All this is band-aids anyway.
* Fact remains, using editstr as main 'reference' over whole search button thingy
* is utterly weak and should be redesigned imho, but that's not a simple task. */
if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr)) {
if (search_but && search_but->rnasearchprop &&
RNA_property_collection_lookup_string(
&search_but->rnasearchpoin, search_but->rnasearchprop, str, &rptr)) {
RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr, NULL);
}
else if (but->func_arg2 != NULL) {
RNA_pointer_create(
NULL, RNA_property_pointer_type(&but->rnapoin, but->rnaprop), but->func_arg2, &rptr);
else if (search_but->item_active != NULL) {
RNA_pointer_create(NULL,
RNA_property_pointer_type(&but->rnapoin, but->rnaprop),
search_but->item_active,
&rptr);
RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr, NULL);
}
@ -3231,6 +3241,28 @@ void ui_but_range_set_soft(uiBut *but)
/* ******************* Free ********************/
/**
* Free data specific to a certain button type.
* For now just do in a switch-case, we could instead have a callback stored in #uiBut and set that
* in #ui_but_alloc_info().
*/
static void ui_but_free_type_specific(uiBut *but)
{
switch (but->type) {
case UI_BTYPE_SEARCH_MENU: {
uiButSearch *search_but = (uiButSearch *)but;
if (search_but->arg_free_fn) {
search_but->arg_free_fn(search_but->arg);
search_but->arg = NULL;
}
break;
}
default:
break;
}
}
/* can be called with C==NULL */
static void ui_but_free(const bContext *C, uiBut *but)
{
@ -3251,13 +3283,7 @@ static void ui_but_free(const bContext *C, uiBut *but)
MEM_freeN(but->hold_argN);
}
if (but->search != NULL) {
if (but->search->arg_free_fn) {
but->search->arg_free_fn(but->search->arg);
but->search->arg = NULL;
}
MEM_freeN(but->search);
}
ui_but_free_type_specific(but);
if (but->active) {
/* XXX solve later, buttons should be free-able without context ideally,
@ -3736,14 +3762,99 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3])
IMB_colormanagement_scene_linear_to_display_v3(pixel, display);
}
static void ui_but_alloc_info(const eButType type,
size_t *r_alloc_size,
const char **r_alloc_str,
bool *r_has_custom_type)
{
size_t alloc_size;
const char *alloc_str;
bool has_custom_type = true;
switch (type) {
case UI_BTYPE_DECORATOR:
alloc_size = sizeof(uiButDecorator);
alloc_str = "uiButDecorator";
break;
case UI_BTYPE_TAB:
alloc_size = sizeof(uiButTab);
alloc_str = "uiButTab";
break;
case UI_BTYPE_SEARCH_MENU:
alloc_size = sizeof(uiButSearch);
alloc_str = "uiButSearch";
break;
default:
alloc_size = sizeof(uiBut);
alloc_str = "uiBut";
has_custom_type = false;
break;
}
if (r_alloc_size) {
*r_alloc_size = alloc_size;
}
if (r_alloc_str) {
*r_alloc_str = alloc_str;
}
if (r_has_custom_type) {
*r_has_custom_type = has_custom_type;
}
}
static uiBut *ui_but_alloc(const eButType type)
{
switch (type) {
case UI_BTYPE_TAB:
return MEM_callocN(sizeof(uiButTab), "uiButTab");
default:
return MEM_callocN(sizeof(uiBut), "uiBut");
size_t alloc_size;
const char *alloc_str;
ui_but_alloc_info(type, &alloc_size, &alloc_str, NULL);
return MEM_callocN(alloc_size, alloc_str);
}
/**
* Reallocate the button (new address is returned) for a new button type.
* This should generally be avoided and instead the correct type be created right away.
*
* \note Only the #uiBut data can be kept. If the old button used a derived type (e.g. #uiButTab),
* the data that is not inside #uiBut will be lost.
*/
uiBut *ui_but_change_type(uiBut *but, eButType new_type)
{
if (but->type != new_type) {
size_t alloc_size;
const char *alloc_str;
uiBut *insert_after_but = but->prev;
bool new_has_custom_type, old_has_custom_type;
/* Remove old button address */
BLI_remlink(&but->block->buttons, but);
ui_but_alloc_info(but->type, NULL, NULL, &old_has_custom_type);
ui_but_alloc_info(new_type, &alloc_size, &alloc_str, &new_has_custom_type);
if (new_has_custom_type || old_has_custom_type) {
const void *old_but_ptr = but;
/* Button may have pointer to a member within itself, this will have to be updated. */
const bool has_str_ptr_to_self = but->str == but->strdata;
but = MEM_recallocN_id(but, alloc_size, alloc_str);
but->type = new_type;
if (has_str_ptr_to_self) {
but->str = but->strdata;
}
BLI_insertlinkafter(&but->block->buttons, insert_after_but, but);
if (but->layout) {
const bool found_layout = ui_layout_replace_but_ptr(but->layout, old_but_ptr, but);
BLI_assert(found_layout);
UNUSED_VARS_NDEBUG(found_layout);
}
}
}
return but;
}
/**
@ -3891,6 +4002,7 @@ static uiBut *ui_def_but(uiBlock *block,
if (ELEM(but->type,
UI_BTYPE_BLOCK,
UI_BTYPE_BUT,
UI_BTYPE_DECORATOR,
UI_BTYPE_LABEL,
UI_BTYPE_PULLDOWN,
UI_BTYPE_ROUNDBOX,
@ -6385,54 +6497,55 @@ void UI_but_func_search_set(uiBut *but,
uiButHandleFunc search_exec_fn,
void *active)
{
uiButSearch *search_but = (uiButSearch *)but;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
/* needed since callers don't have access to internal functions
* (as an alternative we could expose it) */
if (search_create_fn == NULL) {
search_create_fn = ui_searchbox_create_generic;
}
struct uiButSearchData *search = but->search;
if (search != NULL) {
if (search->arg_free_fn != NULL) {
search->arg_free_fn(but->search->arg);
search->arg = NULL;
}
}
else {
search = MEM_callocN(sizeof(*but->search), __func__);
but->search = search;
if (search_but->arg_free_fn != NULL) {
search_but->arg_free_fn(search_but->arg);
search_but->arg = NULL;
}
search->create_fn = search_create_fn;
search->update_fn = search_update_fn;
search_but->popup_create_fn = search_create_fn;
search_but->items_update_fn = search_update_fn;
search_but->item_active = active;
search->arg = arg;
search->arg_free_fn = search_arg_free_fn;
search_but->arg = arg;
search_but->arg_free_fn = search_arg_free_fn;
if (search_exec_fn) {
#ifdef DEBUG
if (but->func) {
if (search_but->but.func) {
/* watch this, can be cause of much confusion, see: T47691 */
printf("%s: warning, overwriting button callback with search function callback!\n",
__func__);
}
#endif
UI_but_func_set(but, search_exec_fn, search->arg, active);
/* Handling will pass the active item as arg2 later, so keep it NULL here. */
UI_but_func_set(but, search_exec_fn, search_but->arg, NULL);
}
/* search buttons show red-alert if item doesn't exist, not for menus */
if (0 == (but->block->flag & UI_BLOCK_LOOP)) {
/* skip empty buttons, not all buttons need input, we only show invalid */
if (but->drawstr[0]) {
ui_but_search_refresh(but);
ui_but_search_refresh(search_but);
}
}
}
void UI_but_func_search_set_context_menu(uiBut *but, uiButSearchContextMenuFn context_menu_fn)
{
struct uiButSearchData *search = but->search;
search->context_menu_fn = context_menu_fn;
uiButSearch *but_search = (uiButSearch *)but;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
but_search->item_context_menu_fn = context_menu_fn;
}
/**
@ -6441,14 +6554,18 @@ void UI_but_func_search_set_context_menu(uiBut *but, uiButSearchContextMenuFn co
*/
void UI_but_func_search_set_sep_string(uiBut *but, const char *search_sep_string)
{
struct uiButSearchData *search = but->search;
search->sep_string = search_sep_string;
uiButSearch *but_search = (uiButSearch *)but;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
but_search->item_sep_string = search_sep_string;
}
void UI_but_func_search_set_tooltip(uiBut *but, uiButSearchTooltipFn tooltip_fn)
{
struct uiButSearchData *search = but->search;
search->tooltip_fn = tooltip_fn;
uiButSearch *but_search = (uiButSearch *)but;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
but_search->item_tooltip_fn = tooltip_fn;
}
/* Callbacks for operator search button. */

View File

@ -120,35 +120,41 @@ void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
}
}
static uiBut *ui_but_anim_decorate_find_attached_button(uiBut *but_decorate)
static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but_decorate)
{
uiBut *but_iter = NULL;
BLI_assert(UI_but_is_decorator(but_decorate));
BLI_assert(but_decorate->rnasearchpoin.data && but_decorate->rnasearchprop);
BLI_assert(UI_but_is_decorator(&but_decorate->but));
BLI_assert(but_decorate->rnapoin.data && but_decorate->rnaprop);
LISTBASE_CIRCULAR_BACKWARD_BEGIN (&but_decorate->block->buttons, but_iter, but_decorate->prev) {
if (but_iter != but_decorate &&
ui_but_rna_equals_ex(but_iter,
&but_decorate->rnasearchpoin,
but_decorate->rnasearchprop,
POINTER_AS_INT(but_decorate->custom_data))) {
LISTBASE_CIRCULAR_BACKWARD_BEGIN (
&but_decorate->but.block->buttons, but_iter, but_decorate->but.prev) {
if (but_iter != (uiBut *)but_decorate &&
ui_but_rna_equals_ex(
but_iter, &but_decorate->rnapoin, but_decorate->rnaprop, but_decorate->rnaindex)) {
return but_iter;
}
}
LISTBASE_CIRCULAR_BACKWARD_END(&but_decorate->block->buttons, but_iter, but_decorate->prev);
LISTBASE_CIRCULAR_BACKWARD_END(
&but_decorate->but.block->buttons, but_iter, but_decorate->but.prev);
return NULL;
}
void ui_but_anim_decorate_update_from_flag(uiBut *but)
void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but)
{
const uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but);
if (!decorator_but->rnapoin.data || !decorator_but->rnaprop) {
/* Nothing to do. */
return;
}
const uiBut *but_anim = ui_but_anim_decorate_find_attached_button(decorator_but);
uiBut *but = &decorator_but->but;
if (!but_anim) {
printf("Could not find button with matching property to decorate (%s.%s)\n",
RNA_struct_identifier(but->rnasearchpoin.type),
RNA_property_identifier(but->rnasearchprop));
RNA_struct_identifier(decorator_but->rnapoin.type),
RNA_property_identifier(decorator_but->rnaprop));
return;
}
@ -325,7 +331,7 @@ void ui_but_anim_paste_driver(bContext *C)
void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy))
{
wmWindowManager *wm = CTX_wm_manager(C);
uiBut *but_decorate = arg_but;
uiButDecorator *but_decorate = arg_but;
uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but_decorate);
if (!but_anim) {
@ -333,7 +339,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)
}
/* FIXME(campbell), swapping active pointer is weak. */
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->active);
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active);
wm->op_undo_depth++;
if (but_anim->flag & UI_BUT_DRIVEN) {
@ -357,6 +363,6 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)
WM_operator_properties_free(&props_ptr);
}
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->active);
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active);
wm->op_undo_depth--;
}

View File

@ -962,7 +962,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
const PropertyType prop_type = RNA_property_type(but->rnaprop);
if (((prop_type == PROP_POINTER) ||
(prop_type == PROP_STRING && but->type == UI_BTYPE_SEARCH_MENU &&
but->search->update_fn == ui_rna_collection_search_update_fn)) &&
((uiButSearch *)but)->items_update_fn == ui_rna_collection_search_update_fn)) &&
ui_jump_to_target_button_poll(C)) {
uiItemO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Jump to Target"),

View File

@ -767,11 +767,12 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
after->rnapoin = but->rnapoin;
after->rnaprop = but->rnaprop;
if (but->search != NULL) {
after->search_arg_free_fn = but->search->arg_free_fn;
after->search_arg = but->search->arg;
but->search->arg_free_fn = NULL;
but->search->arg = NULL;
if (but->type == UI_BTYPE_SEARCH_MENU) {
uiButSearch *search_but = (uiButSearch *)but;
after->search_arg_free_fn = search_but->arg_free_fn;
after->search_arg = search_but->arg;
search_but->arg_free_fn = NULL;
search_but->arg = NULL;
}
if (but->context) {
@ -1047,8 +1048,19 @@ static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
but->rename_orig = data->origstr;
data->origstr = NULL;
}
void *orig_arg2 = but->func_arg2;
/* If arg2 isn't in use already, pass the active search item through it. */
if ((but->func_arg2 == NULL) && (but->type == UI_BTYPE_SEARCH_MENU)) {
uiButSearch *search_but = (uiButSearch *)but;
but->func_arg2 = search_but->item_active;
}
ui_apply_but_func(C, but);
but->func_arg2 = orig_arg2;
data->retval = but->retval;
data->applied = true;
}
@ -2087,6 +2099,7 @@ static void ui_apply_but(
/* handle different types */
switch (but->type) {
case UI_BTYPE_BUT:
case UI_BTYPE_DECORATOR:
ui_apply_but_BUT(C, but, data);
break;
case UI_BTYPE_TEXT:
@ -3321,7 +3334,9 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
/* optional searchbox */
if (but->type == UI_BTYPE_SEARCH_MENU) {
data->searchbox = but->search->create_fn(C, data->region, but);
uiButSearch *search_but = (uiButSearch *)but;
data->searchbox = search_but->popup_create_fn(C, data->region, search_but);
ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */
}
@ -7522,6 +7537,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
switch (but->type) {
case UI_BTYPE_BUT:
case UI_BTYPE_DECORATOR:
retval = ui_do_but_BUT(C, but, data, event);
break;
case UI_BTYPE_KEY_EVENT:
@ -8430,7 +8446,7 @@ void UI_context_update_anim_flag(const bContext *C)
ui_but_anim_flag(but, &anim_eval_context);
ui_but_override_flag(CTX_data_main(C), but);
if (UI_but_is_decorator(but)) {
ui_but_anim_decorate_update_from_flag(but);
ui_but_anim_decorate_update_from_flag((uiButDecorator *)but);
}
ED_region_tag_redraw(region);

View File

@ -147,19 +147,11 @@ enum {
/* max amount of items a radial menu (pie menu) can contain */
#define PIE_MAX_ITEMS 8
struct uiButSearchData {
uiButSearchCreateFn create_fn;
uiButSearchUpdateFn update_fn;
void *arg;
uiButSearchArgFreeFn arg_free_fn;
uiButSearchContextMenuFn context_menu_fn;
uiButSearchTooltipFn tooltip_fn;
const char *sep_string;
};
struct uiBut {
struct uiBut *next, *prev;
/* Pointer back to the layout item holding this button. */
uiLayout *layout;
int flag, drawflag;
eButType type;
eButPointerType pointype;
@ -214,8 +206,6 @@ struct uiBut {
uiButCompleteFunc autocomplete_func;
void *autofunc_arg;
struct uiButSearchData *search;
uiButHandleRenameFunc rename_func;
void *rename_arg1;
void *rename_orig;
@ -256,9 +246,6 @@ struct uiBut {
struct PropertyRNA *rnaprop;
int rnaindex;
struct PointerRNA rnasearchpoin;
struct PropertyRNA *rnasearchprop;
/* Operator data */
struct wmOperatorType *optype;
struct PointerRNA *opptr;
@ -294,11 +281,41 @@ struct uiBut {
uiBlock *block;
};
/** Derived struct for #UI_BTYPE_TAB */
typedef struct uiButTab {
uiBut but;
struct MenuType *menu;
} uiButTab;
/** Derived struct for #UI_BTYPE_SEARCH_MENU */
typedef struct uiButSearch {
uiBut but;
uiButSearchCreateFn popup_create_fn;
uiButSearchUpdateFn items_update_fn;
void *item_active;
void *arg;
uiButSearchArgFreeFn arg_free_fn;
uiButSearchContextMenuFn item_context_menu_fn;
uiButSearchTooltipFn item_tooltip_fn;
const char *item_sep_string;
struct PointerRNA rnasearchpoin;
struct PropertyRNA *rnasearchprop;
} uiButSearch;
/** Derived struct for #UI_BTYPE_DECORATOR */
typedef struct uiButDecorator {
uiBut but;
struct PointerRNA rnapoin;
struct PropertyRNA *rnaprop;
int rnaindex;
} uiButDecorator;
/**
* Additional, superimposed icon for a button, invoking an operator.
*/
@ -493,6 +510,8 @@ extern void ui_window_to_region_rcti(const struct ARegion *region,
extern void ui_region_to_window(const struct ARegion *region, int *x, int *y);
extern void ui_region_winrct_get_no_margin(const struct ARegion *region, struct rcti *r_rect);
uiBut *ui_but_change_type(uiBut *but, eButType new_type);
extern double ui_but_value_get(uiBut *but);
extern void ui_but_value_set(uiBut *but, double value);
extern void ui_but_hsv_set(uiBut *but);
@ -662,13 +681,13 @@ ColorPicker *ui_block_colorpicker_create(struct uiBlock *block);
/* Searchbox for string button */
struct ARegion *ui_searchbox_create_generic(struct bContext *C,
struct ARegion *butregion,
uiBut *but);
uiButSearch *search_but);
struct ARegion *ui_searchbox_create_operator(struct bContext *C,
struct ARegion *butregion,
uiBut *but);
uiButSearch *search_but);
struct ARegion *ui_searchbox_create_menu(struct bContext *C,
struct ARegion *butregion,
uiBut *but);
uiButSearch *search_but);
bool ui_searchbox_inside(struct ARegion *region, int x, int y);
int ui_searchbox_find_index(struct ARegion *region, const char *name);
@ -681,7 +700,7 @@ bool ui_searchbox_event(struct bContext *C,
const struct wmEvent *event);
bool ui_searchbox_apply(uiBut *but, struct ARegion *region);
void ui_searchbox_free(struct bContext *C, struct ARegion *region);
void ui_but_search_refresh(uiBut *but);
void ui_but_search_refresh(uiButSearch *but);
/* interface_region_menu_popup.c */
int ui_but_menu_step(uiBut *but, int step);
@ -925,11 +944,12 @@ void ui_resources_free(void);
/* interface_layout.c */
void ui_layout_add_but(uiLayout *layout, uiBut *but);
void ui_but_add_search(uiBut *but,
PointerRNA *ptr,
PropertyRNA *prop,
PointerRNA *searchptr,
PropertyRNA *searchprop);
bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but);
uiBut *ui_but_add_search(uiBut *but,
PointerRNA *ptr,
PropertyRNA *prop,
PointerRNA *searchptr,
PropertyRNA *searchprop);
void ui_layout_list_set_labels_active(uiLayout *layout);
/* menu callback */
void ui_item_menutype_func(struct bContext *C, struct uiLayout *layout, void *arg_mt);
@ -950,7 +970,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str);
void ui_but_anim_autokey(struct bContext *C, uiBut *but, struct Scene *scene, float cfra);
void ui_but_anim_decorate_cb(struct bContext *C, void *arg_but, void *arg_dummy);
void ui_but_anim_decorate_update_from_flag(uiBut *but);
void ui_but_anim_decorate_update_from_flag(uiButDecorator *but);
/* interface_query.c */
bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT;

View File

@ -2278,7 +2278,7 @@ void uiItemFullR(uiLayout *layout,
/* property with separate label */
else if (type == PROP_ENUM || type == PROP_STRING || type == PROP_POINTER) {
but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag);
ui_but_add_search(but, ptr, prop, NULL, NULL);
but = ui_but_add_search(but, ptr, prop, NULL, NULL);
if (layout->redalert) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
@ -2651,7 +2651,10 @@ static void ui_rna_collection_search_arg_free_fn(void *ptr)
MEM_freeN(ptr);
}
void ui_but_add_search(
/**
* \note May reallocate \a but, so the possibly new address is returned.
*/
uiBut *ui_but_add_search(
uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop)
{
StructRNA *ptype;
@ -2669,11 +2672,13 @@ void ui_but_add_search(
/* turn button into search button */
if (searchprop) {
uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__);
uiButSearch *search_but;
but->type = UI_BTYPE_SEARCH_MENU;
but = ui_but_change_type(but, UI_BTYPE_SEARCH_MENU);
search_but = (uiButSearch *)but;
search_but->rnasearchpoin = *searchptr;
search_but->rnasearchprop = searchprop;
but->hardmax = MAX2(but->hardmax, 256.0f);
but->rnasearchpoin = *searchptr;
but->rnasearchprop = searchprop;
but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT;
if (RNA_property_is_unlink(prop)) {
but->flag |= UI_BUT_VALUE_CLEAR;
@ -2707,6 +2712,8 @@ void ui_but_add_search(
* so other code might have already set but->type to search menu... */
but->flag |= UI_BUT_DISABLED;
}
return but;
}
void uiItemPointerR_prop(uiLayout *layout,
@ -2939,29 +2946,28 @@ void uiItemMContents(uiLayout *layout, const char *menuname)
void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index)
{
uiBlock *block = layout->root->block;
uiBut *but = NULL;
uiLayout *col;
UI_block_layout_set_current(block, layout);
col = uiLayoutColumn(layout, false);
col->space = 0;
col->emboss = UI_EMBOSS_NONE;
if (ELEM(NULL, ptr, prop) || !RNA_property_animateable(ptr, prop)) {
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
ICON_BLANK1,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
"");
uiBut *but = uiDefIconBut(block,
UI_BTYPE_DECORATOR,
0,
ICON_BLANK1,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
"");
but->flag |= UI_BUT_DISABLED;
return;
}
@ -2971,27 +2977,28 @@ void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop,
/* Loop for the array-case, but only do in case of an expanded array. */
for (int i = 0; i < (is_expand ? RNA_property_array_length(ptr, prop) : 1); i++) {
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
ICON_DOT,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
TIP_("Animate property"));
UI_but_func_set(but, ui_but_anim_decorate_cb, but, NULL);
but->flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK;
uiButDecorator *decorator_but = (uiButDecorator *)uiDefIconBut(block,
UI_BTYPE_DECORATOR,
0,
ICON_DOT,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0.0,
0.0,
0.0,
0.0,
TIP_("Animate property"));
UI_but_func_set(&decorator_but->but, ui_but_anim_decorate_cb, decorator_but, NULL);
decorator_but->but.flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK;
/* Reusing RNA search members, setting actual RNA data has many side-effects. */
but->rnasearchpoin = *ptr;
but->rnasearchprop = prop;
decorator_but->rnapoin = *ptr;
decorator_but->rnaprop = prop;
/* ui_def_but_rna() sets non-array buttons to have a RNA index of 0. */
but->custom_data = POINTER_FROM_INT((!is_array || is_expand) ? i : index);
decorator_but->rnaindex = (!is_array || is_expand) ? i : index;
}
}
@ -4819,8 +4826,6 @@ void ui_layout_list_set_labels_active(uiLayout *layout)
uiLayout *uiLayoutListBox(uiLayout *layout,
uiList *ui_list,
PointerRNA *ptr,
PropertyRNA *prop,
PointerRNA *actptr,
PropertyRNA *actprop)
{
@ -4829,8 +4834,6 @@ uiLayout *uiLayoutListBox(uiLayout *layout,
but->custom_data = ui_list;
but->rnasearchpoin = *ptr;
but->rnasearchprop = prop;
but->rnapoin = *actptr;
but->rnaprop = actprop;
@ -5407,6 +5410,7 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but)
else {
BLI_addtail(&layout->items, bitem);
}
but->layout = layout;
if (layout->context) {
but->context = layout->context;
@ -5418,6 +5422,30 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but)
}
}
bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but)
{
ListBase *child_list = layout->child_items_layout ? &layout->child_items_layout->items :
&layout->items;
LISTBASE_FOREACH (uiItem *, item, child_list) {
if (item->type == ITEM_BUTTON) {
uiButtonItem *bitem = (uiButtonItem *)item;
if (bitem->but == old_but_ptr) {
bitem->but = new_but;
return true;
}
}
else {
if (ui_layout_replace_but_ptr((uiLayout *)item, old_but_ptr, new_but)) {
return true;
}
}
}
return false;
}
void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size)
{
if (fixed_size) {

View File

@ -1148,10 +1148,11 @@ static bool jump_to_target_button(bContext *C, bool poll)
/* For string properties with prop_search, look up the search collection item. */
if (type == PROP_STRING) {
const uiBut *but = UI_context_active_but_get(C);
const uiButSearch *search_but = (but->type == UI_BTYPE_SEARCH_MENU) ? (uiButSearch *)but :
NULL;
if (but->type == UI_BTYPE_SEARCH_MENU && but->search &&
but->search->update_fn == ui_rna_collection_search_update_fn) {
uiRNACollectionSearch *coll_search = but->search->arg;
if (search_but && search_but->items_update_fn == ui_rna_collection_search_update_fn) {
uiRNACollectionSearch *coll_search = search_but->arg;
char str_buf[MAXBONENAME];
char *str_ptr = RNA_property_string_get_alloc(&ptr, prop, str_buf, sizeof(str_buf), NULL);

View File

@ -113,7 +113,7 @@ bool UI_but_is_utf8(const uiBut *but)
#ifdef USE_UI_POPOVER_ONCE
bool ui_but_is_popover_once_compat(const uiBut *but)
{
return ((but->type == UI_BTYPE_BUT) || ui_but_is_toggle(but));
return (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR) || ui_but_is_toggle(but));
}
#endif

View File

@ -302,8 +302,11 @@ bool ui_searchbox_inside(ARegion *region, int x, int y)
bool ui_searchbox_apply(uiBut *but, ARegion *region)
{
uiSearchboxData *data = region->regiondata;
uiButSearch *search_but = (uiButSearch *)but;
but->func_arg2 = NULL;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
search_but->item_active = NULL;
if (data->active != -1) {
const char *name = data->items.names[data->active] +
@ -316,7 +319,7 @@ bool ui_searchbox_apply(uiBut *but, ARegion *region)
BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen);
but->func_arg2 = data->items.pointers[data->active];
search_but->item_active = data->items.pointers[data->active];
return true;
}
@ -340,8 +343,13 @@ static struct ARegion *wm_searchbox_tooltip_init(struct bContext *C,
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->search && but->search->tooltip_fn) {
return but->search->tooltip_fn(C, region, but->search->arg, but->func_arg2);
if (but->type != UI_BTYPE_SEARCH_MENU) {
continue;
}
uiButSearch *search_but = (uiButSearch *)but;
if (search_but->item_tooltip_fn) {
return search_but->item_tooltip_fn(C, region, search_but->arg, search_but->item_active);
}
}
}
@ -352,10 +360,13 @@ bool ui_searchbox_event(
bContext *C, ARegion *region, uiBut *but, ARegion *butregion, const wmEvent *event)
{
uiSearchboxData *data = region->regiondata;
uiButSearch *search_but = (uiButSearch *)but;
int type = event->type, val = event->val;
bool handled = false;
bool tooltip_timer_started = false;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
if (type == MOUSEPAN) {
ui_pan_to_scroll(event, &type, &val);
}
@ -373,7 +384,7 @@ bool ui_searchbox_event(
break;
case RIGHTMOUSE:
if (val) {
if (but->search->context_menu_fn) {
if (search_but->item_context_menu_fn) {
if (data->active != -1) {
/* Check the cursor is over the active element
* (a little confusing if this isn't the case, although it does work). */
@ -383,7 +394,7 @@ bool ui_searchbox_event(
&rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) {
void *active = data->items.pointers[data->active];
if (but->search->context_menu_fn(C, but->search->arg, active, event)) {
if (search_but->item_context_menu_fn(C, search_but->arg, active, event)) {
handled = true;
}
}
@ -417,7 +428,7 @@ bool ui_searchbox_event(
if (is_inside) {
if (data->active != -1) {
ScrArea *area = CTX_wm_area(C);
but->func_arg2 = data->items.pointers[data->active];
search_but->item_active = data->items.pointers[data->active];
WM_tooltip_timer_init(C, CTX_wm_window(C), area, butregion, wm_searchbox_tooltip_init);
tooltip_timer_started = true;
}
@ -437,18 +448,24 @@ bool ui_searchbox_event(
}
/** Wrap #uiButSearchUpdateFn callback. */
static void ui_searchbox_update_fn(bContext *C, uiBut *but, const char *str, uiSearchItems *items)
static void ui_searchbox_update_fn(bContext *C,
uiButSearch *search_but,
const char *str,
uiSearchItems *items)
{
wmWindow *win = CTX_wm_window(C);
WM_tooltip_clear(C, win);
but->search->update_fn(C, but->search->arg, str, items);
search_but->items_update_fn(C, search_but->arg, str, items);
}
/* region is the search box itself */
void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset)
{
uiButSearch *search_but = (uiButSearch *)but;
uiSearchboxData *data = region->regiondata;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
/* reset vars */
data->items.totitem = 0;
data->items.more = 0;
@ -460,9 +477,9 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re
data->active = -1;
/* handle active */
if (but->search->update_fn && but->func_arg2) {
data->items.active = but->func_arg2;
ui_searchbox_update_fn(C, but, but->editstr, &data->items);
if (search_but->items_update_fn && search_but->item_active) {
data->items.active = search_but->item_active;
ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
data->items.active = NULL;
/* found active item, calculate real offset by centering it */
@ -491,8 +508,8 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re
}
/* callback */
if (but->search->update_fn) {
ui_searchbox_update_fn(C, but, but->editstr, &data->items);
if (search_but->items_update_fn) {
ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
}
/* handle case where editstr is equal to one of items */
@ -523,13 +540,16 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re
int ui_searchbox_autocomplete(bContext *C, ARegion *region, uiBut *but, char *str)
{
uiButSearch *search_but = (uiButSearch *)but;
uiSearchboxData *data = region->regiondata;
int match = AUTOCOMPLETE_NO_MATCH;
BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
if (str[0]) {
data->items.autocpl = UI_autocomplete_begin(str, ui_but_string_get_max_length(but));
ui_searchbox_update_fn(C, but, but->editstr, &data->items);
ui_searchbox_update_fn(C, search_but, but->editstr, &data->items);
match = UI_autocomplete_end(data->items.autocpl, str);
data->items.autocpl = NULL;
@ -673,10 +693,11 @@ static void ui_searchbox_region_free_cb(ARegion *region)
region->regiondata = NULL;
}
ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but)
ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearch *search_but)
{
wmWindow *win = CTX_wm_window(C);
const uiStyle *style = UI_style_get();
uiBut *but = &search_but->but;
static ARegionType type;
ARegion *region;
uiSearchboxData *data;
@ -725,7 +746,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but
if (but->optype != NULL || (but->drawflag & UI_BUT_HAS_SHORTCUT) != 0) {
data->use_sep = true;
}
data->sep_string = but->search->sep_string;
data->sep_string = search_but->item_sep_string;
/* compute position */
if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
@ -945,12 +966,12 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe
}
}
ARegion *ui_searchbox_create_operator(bContext *C, ARegion *butregion, uiBut *but)
ARegion *ui_searchbox_create_operator(bContext *C, ARegion *butregion, uiButSearch *search_but)
{
ARegion *region;
UI_but_drawflag_enable(but, UI_BUT_HAS_SHORTCUT);
region = ui_searchbox_create_generic(C, butregion, but);
UI_but_drawflag_enable(&search_but->but, UI_BUT_HAS_SHORTCUT);
region = ui_searchbox_create_generic(C, butregion, search_but);
region->type->draw = ui_searchbox_region_draw_cb__operator;
@ -967,12 +988,12 @@ static void ui_searchbox_region_draw_cb__menu(const bContext *UNUSED(C), ARegion
/* Currently unused. */
}
ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiBut *but)
ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiButSearch *search_but)
{
ARegion *region;
UI_but_drawflag_enable(but, UI_BUT_HAS_SHORTCUT);
region = ui_searchbox_create_generic(C, butregion, but);
UI_but_drawflag_enable(&search_but->but, UI_BUT_HAS_SHORTCUT);
region = ui_searchbox_create_generic(C, butregion, search_but);
if (false) {
region->type->draw = ui_searchbox_region_draw_cb__menu;
@ -983,8 +1004,9 @@ ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiBut *but)
/* 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)
void ui_but_search_refresh(uiButSearch *search_but)
{
uiBut *but = &search_but->but;
uiSearchItems *items;
int x1;
@ -1004,7 +1026,7 @@ void ui_but_search_refresh(uiBut *but)
items->names[x1] = MEM_callocN(but->hardmax + 1, "search names");
}
ui_searchbox_update_fn(but->block->evil_C, but, but->drawstr, items);
ui_searchbox_update_fn(but->block->evil_C, search_but, but->drawstr, items);
/* only redalert when we are sure of it, this can miss cases when >10 matches */
if (items->totitem == 0) {

View File

@ -6210,7 +6210,7 @@ void uiTemplateList(uiLayout *layout,
switch (layout_type) {
case UILST_LAYOUT_DEFAULT:
/* layout */
box = uiLayoutListBox(layout, ui_list, dataptr, prop, active_dataptr, activeprop);
box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
glob = uiLayoutColumn(box, true);
row = uiLayoutRow(glob, false);
col = uiLayoutColumn(row, true);
@ -6348,7 +6348,7 @@ void uiTemplateList(uiLayout *layout,
}
break;
case UILST_LAYOUT_GRID:
box = uiLayoutListBox(layout, ui_list, dataptr, prop, active_dataptr, activeprop);
box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
glob = uiLayoutColumn(box, true);
row = uiLayoutRow(glob, false);
col = uiLayoutColumn(row, true);

View File

@ -1400,7 +1400,7 @@ static void widget_draw_icon(
alpha *= but->a2;
}
}
else if (ELEM(but->type, UI_BTYPE_BUT)) {
else if (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR)) {
if (but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
alpha *= 0.5f;
}
@ -4552,6 +4552,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
break;
case UI_BTYPE_BUT:
case UI_BTYPE_DECORATOR:
#ifdef USE_UI_TOOLBAR_HACK
if ((but->icon != ICON_NONE) && UI_but_is_tool(but)) {
wt = widget_type(UI_WTYPE_TOOLBAR_ITEM);