diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 94453ef5fac..1b817d06564 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -508,6 +508,10 @@ typedef void (*uiButHandleNFunc)(struct bContext *C, void *argN, void *arg2); typedef void (*uiButHandleHoldFunc)(struct bContext *C, struct ARegion *butregion, uiBut *but); typedef int (*uiButCompleteFunc)(struct bContext *C, char *str, void *arg); +/** Function to compare the identity of two buttons over redraws, to check if they represent the + * same data, and thus should be considered the same button over redraws. */ +typedef bool (*uiButIdentityCompareFunc)(const uiBut *a, const uiBut *b); + /* Search types. */ typedef struct ARegion *(*uiButSearchCreateFn)(struct bContext *C, struct ARegion *butregion, @@ -1649,6 +1653,18 @@ eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout, eButLabelAlign label_align, bool compact); +/** + * Callback to compare the identity of two buttons, used to identify buttons over redraws. If the + * callback returns true, the given buttons are considered to be matching and relevant state is + * preserved (copied from the old to the new button). If it returns false, it's considered + * non-matching and no further checks are done. + * + * If this is set, it is always executed instead of the default comparisons. However it is only + * executed for buttons that have the same type and the same callback. So callbacks can assume the + * button types match. + */ +void UI_but_func_identity_compare_set(uiBut *but, uiButIdentityCompareFunc cmp_fn); + /** * Public function exported for functions that use #UI_BTYPE_SEARCH_MENU. * diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 9310ede31fa..b7098c26bcd 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -725,6 +725,19 @@ bool ui_but_rna_equals_ex(const uiBut *but, /* NOTE: if `but->poin` is allocated memory for every `uiDefBut*`, things fail. */ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) { + if (but->identity_cmp_func) { + /* If the buttons have own identity comparator callbacks (and they match), use this to + * determine equality. */ + if (but->identity_cmp_func && (but->type == oldbut->type) && + (but->identity_cmp_func == oldbut->identity_cmp_func)) { + /* Test if the comparison is symmetrical (if a == b then b == a), may help catch some issues. + */ + BLI_assert(but->identity_cmp_func(but, oldbut) == but->identity_cmp_func(oldbut, but)); + + return but->identity_cmp_func(but, oldbut); + } + } + /* various properties are being compared here, hopefully sufficient * to catch all cases, but it is simple to add more checks later */ if (but->retval != oldbut->retval) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 05acdac3597..c09ff68bbca 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -183,6 +183,9 @@ struct uiBut { uchar col[4]; + /** See \ref UI_but_func_identity_compare_set(). */ + uiButIdentityCompareFunc identity_cmp_func; + uiButHandleFunc func; void *func_arg1; void *func_arg2; diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index c59863f462a..993ccdf92f7 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -452,6 +452,11 @@ eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout, return return_info; } +void UI_but_func_identity_compare_set(uiBut *but, uiButIdentityCompareFunc cmp_fn) +{ + but->identity_cmp_func = cmp_fn; +} + /* *** RNA collection search menu *** */ struct CollItemSearch {