From 2dafd1bfb8294acd996607f2b31961f66b5a3587 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 8 Feb 2014 09:03:25 +1100 Subject: UI: butstore API to generalize button storage for modal handlers --- source/blender/editors/include/UI_interface.h | 14 ++ source/blender/editors/interface/interface.c | 21 ++- .../blender/editors/interface/interface_intern.h | 4 + source/blender/editors/interface/interface_utils.c | 162 +++++++++++++++++++++ 4 files changed, 200 insertions(+), 1 deletion(-) (limited to 'source') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 65811b7c009..59257964890 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -942,6 +942,20 @@ void UI_template_fix_linking(void); bool UI_editsource_enable_check(void); void UI_editsource_active_but_test(uiBut *but); +/* UI_butstore_ helpers */ +typedef struct uiButStore uiButStore; +typedef struct uiButStoreElem uiButStoreElem; + +uiButStore *UI_butstore_create(uiBlock *block); +void UI_butstore_clear(uiBlock *block); +void UI_butstore_update(uiBlock *block); +void UI_butstore_free(uiBlock *block, uiButStore *bs); +bool UI_butstore_is_valid(uiButStore *bs); +bool UI_butstore_is_registered(uiBlock *block, uiBut *but); +void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p); +void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p); + + /* Float precision helpers */ #define UI_PRECISION_FLOAT_MAX 7 diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index db2b804363c..40e3b15e191 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -525,7 +525,7 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) return true; } -static uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new) +uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new) { uiBut *but_old; for (but_old = block_old->buttons.first; but_old; but_old = but_old->next) { @@ -535,6 +535,16 @@ static uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new) } return but_old; } +uiBut *ui_but_find_new(uiBlock *block_new, const uiBut *but_old) +{ + uiBut *but_new; + for (but_new = block_new->buttons.first; but_new; but_new = but_new->next) { + if (ui_but_equals_old(but_new, but_old)) { + break; + } + } + return but_new; +} /* oldbut is being inserted in new block, so we use the lines from new button, and replace button pointers */ static void ui_but_update_linklines(uiBlock *block, uiBut *oldbut, uiBut *newbut) @@ -1020,6 +1030,11 @@ void uiEndBlock(const bContext *C, uiBlock *block) uiBut *but; Scene *scene = CTX_data_scene(C); + + if (has_old && BLI_listbase_is_empty(&block->oldblock->butstore) == false) { + UI_butstore_update(block); + } + /* inherit flags from 'old' buttons that was drawn here previous, based * on matching buttons, we need this to make button event handling non * blocking, while still allowing buttons to be remade each redraw as it @@ -2228,6 +2243,8 @@ static void ui_free_but(const bContext *C, uiBut *but) IMB_freeImBuf((struct ImBuf *)but->poin); } + BLI_assert(UI_butstore_is_registered(but->block, but) == false); + MEM_freeN(but); } @@ -2236,6 +2253,8 @@ void uiFreeBlock(const bContext *C, uiBlock *block) { uiBut *but; + UI_butstore_clear(block); + while ((but = BLI_pophead(&block->buttons))) { ui_free_but(C, but); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 4a0864e22e7..f3d720305bf 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -280,6 +280,8 @@ struct uiBlock { Panel *panel; uiBlock *oldblock; + ListBase butstore; /* UI_butstore_* runtime function */ + ListBase layouts; struct uiLayout *curlayout; @@ -526,6 +528,8 @@ extern int ui_button_open_menu_direction(uiBut *but); extern void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore); void ui_button_clipboard_free(void); void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa); +uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new); +uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new); /* interface_widgets.c */ void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3); diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 2b9d7a74e95..ed4852bf81d 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -34,21 +34,26 @@ #include #include "DNA_object_types.h" +#include "DNA_screen_types.h" #include "BLI_utildefines.h" #include "BLI_math.h" #include "BLI_string.h" +#include "BLI_listbase.h" #include "BLF_translation.h" #include "BKE_context.h" +#include "MEM_guardedalloc.h" #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "interface_intern.h" + /*************************** RNA Utilities ******************************/ @@ -280,3 +285,160 @@ int uiFloatPrecisionCalc(int prec, double value) return prec; } + + +/* -------------------------------------------------------------------- */ +/* Modal Button Store API */ + +/** \name Button Store + * + * Store for modal operators & handlers to register button pointers + * which are maintained while drawing or NULL when removed. + * + * This is needed since button pointers are continuously freed and re-allocated. + * + * \{ */ + +typedef struct uiButStore { + struct uiButStore *next, *prev; + uiBlock *block; + ListBase items; +} uiButStore; + +typedef struct uiButStoreElem { + struct uiButStoreElem *next, *prev; + uiBut **but_p; +} uiButStoreElem; + +/** + * Create a new button sture, the caller must manage and run #UI_butstore_free + */ +uiButStore *UI_butstore_create(uiBlock *block) +{ + uiButStore *bs_handle = MEM_callocN(sizeof(uiButStore), __func__); + + bs_handle->block = block; + BLI_addtail(&block->butstore, bs_handle); + + return bs_handle; +} + +void UI_butstore_free(uiBlock *block, uiButStore *bs_handle) +{ + BLI_freelistN(&bs_handle->items); + BLI_remlink(&block->butstore, bs_handle); + + MEM_freeN(bs_handle); +} + +bool UI_butstore_is_valid(uiButStore *bs) +{ + return (bs->block != NULL); +} + +bool UI_butstore_is_registered(uiBlock *block, uiBut *but) +{ + uiButStore *bs_handle; + + for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) { + uiButStoreElem *bs_elem; + + for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) { + if (*bs_elem->but_p == but) { + return true; + } + } + } + + return false; +} + +void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p) +{ + uiButStoreElem *bs_elem = MEM_callocN(sizeof(uiButStoreElem), __func__); + BLI_assert(*but_p); + bs_elem->but_p = but_p; + + BLI_addtail(&bs_handle->items, bs_elem); + +} + +void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p) +{ + uiButStoreElem *bs_elem, *bs_elem_next; + + for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem_next) { + bs_elem_next = bs_elem->next; + if (bs_elem->but_p == but_p) { + BLI_remlink(&bs_handle->items, bs_elem); + MEM_freeN(bs_elem); + } + } + + BLI_assert(0); +} + +/** + * NULL all pointers, don't free since the owner needs to be able to inspect. + */ +void UI_butstore_clear(uiBlock *block) +{ + uiButStore *bs_handle; + + for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) { + uiButStoreElem *bs_elem; + + bs_handle->block = NULL; + + for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) { + *bs_elem->but_p = NULL; + } + } +} + +/** + * Map freed buttons from the old block and update pointers. + */ +void UI_butstore_update(uiBlock *block) +{ + uiButStore *bs_handle; + + /* move this list to the new block */ + if (block->oldblock) { + if (block->oldblock->butstore.first) { + block->butstore = block->oldblock->butstore; + BLI_listbase_clear(&block->oldblock->butstore); + } + } + + if (LIKELY(block->butstore.first == NULL)) + return; + + /* warning, loop-in-loop, in practice we only store <10 buttons at a time, + * so this isn't going to be a problem, if that changes old-new mapping can be cached first */ + for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) { + + BLI_assert((bs_handle->block == NULL) || + (bs_handle->block == block) || + (block->oldblock && block->oldblock == bs_handle->block)); + + if (bs_handle->block == block->oldblock) { + uiButStoreElem *bs_elem; + + bs_handle->block = block; + + for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) { + if (*bs_elem->but_p) { + uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p); + + /* can be NULL if the buttons removed, + * note: we could allow passing in a callback when buttons are removed + * so the caller can cleanup */ + *bs_elem->but_p = but_new; + } + } + } + } +} + +/** \} */ -- cgit v1.2.3