From 7682258ff9150bf5d2e686be3d4eaf3bb5c4415b Mon Sep 17 00:00:00 2001 From: Guillermo Venegas Date: Fri, 14 Feb 2025 15:29:26 +0100 Subject: [PATCH] Refactor: UI: Use a Vector to store buttons in UI blocks This changes the ui-blocks buttons storage from Listbase to Vector. Major changes that might cause a performance considerations are in `ui_but_update_from_old_block` that requires to track buttons when restoring button state between block redraws or in `uiItemFullR` that may needs to insert uiButs in the middle of the vector to add decorators. This might not be as fast as removing or inserting elements in the middle of a listbase container. Also buttons currently don't know its position in the container, so to get the previous and next button its required to make a lookup of the button in the container. `UI_block_update_from_old> ui_but_update_from_old_block` restores the state of buttons between frames, this is done by sequentially testing if a button is the same as an old button, however since UI can be created procedurally some old buttons may not be drawn while editing other button data, this requires an extra track of what buttons may not match to a new button while comparing for restoring state, but still this buttons may be candidates to match to an new button. Not functional changes expected. Ref: #117604 Co-authored-by: Julian Eisel Pull Request: https://projects.blender.org/blender/blender/pulls/127128 --- source/blender/editors/interface/interface.cc | 449 ++++++++++-------- .../editors/interface/interface_align.cc | 8 +- .../editors/interface/interface_anim.cc | 14 +- .../interface/interface_button_sections.cc | 4 +- .../interface/interface_context_menu.cc | 2 +- .../editors/interface/interface_handlers.cc | 183 +++---- .../editors/interface/interface_intern.hh | 13 +- .../editors/interface/interface_layout.cc | 81 ++-- .../editors/interface/interface_panel.cc | 4 +- .../editors/interface/interface_query.cc | 53 ++- .../editors/interface/interface_utils.cc | 3 +- .../editors/interface/interface_widgets.cc | 4 +- .../regions/interface_region_color_picker.cc | 22 +- .../regions/interface_region_menu_popup.cc | 18 +- .../regions/interface_region_popover.cc | 8 +- .../regions/interface_region_popup.cc | 26 +- .../regions/interface_region_search.cc | 4 +- .../interface_template_asset_shelf_popover.cc | 2 +- .../interface_template_color_ramp.cc | 3 +- .../templates/interface_template_keymap.cc | 10 +- .../interface_template_operator_property.cc | 6 +- .../interface_template_search_menu.cc | 21 +- .../templates/interface_template_status.cc | 6 +- .../interface/views/abstract_view_item.cc | 4 +- .../editors/interface/views/interface_view.cc | 8 +- .../editors/interface/views/tree_view.cc | 4 +- .../editors/space_buttons/buttons_texture.cc | 2 +- .../editors/space_node/node_templates.cc | 4 +- 28 files changed, 526 insertions(+), 440 deletions(-) diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 9de593eea8f..d57a12cf620 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -25,6 +25,7 @@ #include "BLI_listbase.h" #include "BLI_rect.h" +#include "BLI_set.hh" #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_vector.hh" @@ -258,10 +259,50 @@ void ui_region_to_window(const ARegion *region, int *x, int *y) *y += region->winrct.ymin; } +int uiBlock::but_index(const uiBut *but) const +{ + BLI_assert(!buttons.is_empty() && but); + auto index = std::distance( + buttons.begin(), + std::find_if(buttons.begin(), buttons.end(), [but](const std::unique_ptr &test) { + return test.get() == but; + })); + BLI_assert(index != std::distance(buttons.begin(), buttons.end())); + return index; +} + +uiBut *uiBlock::next_but(const uiBut *but) const +{ + const int idx = this->but_index(but) + 1; + return idx < this->buttons.size() ? this->buttons[idx].get() : nullptr; +} + +uiBut *uiBlock::prev_but(const uiBut *but) const +{ + const int idx = this->but_index(but) - 1; + return idx >= 0 ? this->buttons[idx].get() : nullptr; +} + +void uiBlock::remove_but(const uiBut *but) +{ + int64_t target_index = this->but_index(but); + this->buttons.remove(target_index); +} + +uiBut *uiBlock::first_but() const +{ + return !this->buttons.is_empty() ? this->buttons.first().get() : nullptr; +} + +uiBut *uiBlock::last_but() const +{ + return !this->buttons.is_empty() ? this->buttons.last().get() : nullptr; +} + static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block) { int sepr_flex_len = 0; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->type == UI_BTYPE_SEPR_SPACER) { sepr_flex_len++; } @@ -272,7 +313,7 @@ static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block) } rcti rect; - ui_but_to_pixelrect(&rect, region, block, static_cast(block->buttons.last)); + ui_but_to_pixelrect(&rect, region, block, block->buttons.last().get()); const float buttons_width = float(rect.xmax) + UI_HEADER_OFFSET; const float region_width = float(region->sizex) * UI_SCALE_FAC; @@ -282,9 +323,9 @@ static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block) /* We could get rid of this loop if we agree on a max number of spacer */ Vector spacers_pos; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->type == UI_BTYPE_SEPR_SPACER) { - ui_but_to_pixelrect(&rect, region, block, but); + ui_but_to_pixelrect(&rect, region, block, but.get()); spacers_pos.append(rect.xmax + UI_HEADER_OFFSET); } } @@ -293,7 +334,7 @@ static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block) const float segment_width = region_width / float(sepr_flex_len); float offset = 0, remaining_space = region_width - buttons_width; int i = 0; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { BLI_rctf_translate(&but->rect, offset / view_scale_x, 0); if (but->type == UI_BTYPE_SEPR_SPACER) { /* How much the next block overlap with the current segment */ @@ -348,7 +389,7 @@ void ui_region_winrct_get_no_margin(const ARegion *region, rcti *r_rect) void UI_block_translate(uiBlock *block, float x, float y) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { BLI_rctf_translate(&but->rect, x, y); } @@ -363,16 +404,20 @@ static bool ui_but_is_row_alignment_group(const uiBut *left, const uiBut *right) static void ui_block_bounds_calc_text(uiBlock *block, float offset) { + if (block->buttons.is_empty()) { + return; + } const uiStyle *style = UI_style_get(); - uiBut *col_bt; + std::unique_ptr *col_bt; int i = 0, j, x1addval = offset; UI_fontstyle_set(&style->widget); + std::unique_ptr *end = block->buttons.end(); - uiBut *init_col_bt = static_cast(block->buttons.first); - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { - if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) { - j = BLF_width(style->widget.uifont_id, bt->drawstr.c_str(), bt->drawstr.size()); + std::unique_ptr *init_col_bt = block->buttons.begin(); + for (std::unique_ptr *bt = init_col_bt; bt < end; bt++) { + if (!ELEM((*bt)->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) { + j = BLF_width(style->widget.uifont_id, (*bt)->drawstr.c_str(), (*bt)->drawstr.size()); i = std::max(j, i); } @@ -380,27 +425,27 @@ static void ui_block_bounds_calc_text(uiBlock *block, float offset) /* Skip all buttons that are in a horizontal alignment group. * We don't want to split them apart (but still check the row's width and apply current * offsets). */ - if (bt->next && ui_but_is_row_alignment_group(bt, bt->next)) { + if (bt + 1 < end && ui_but_is_row_alignment_group((*bt).get(), bt[1].get())) { int width = 0; - const int alignnr = bt->alignnr; - for (col_bt = bt; col_bt && col_bt->alignnr == alignnr; col_bt = col_bt->next) { - width += BLI_rctf_size_x(&col_bt->rect); - col_bt->rect.xmin += x1addval; - col_bt->rect.xmax += x1addval; + const int alignnr = (*bt)->alignnr; + for (col_bt = bt; col_bt < end && (*col_bt)->alignnr == alignnr; col_bt++) { + width += BLI_rctf_size_x(&(*col_bt)->rect); + (*col_bt)->rect.xmin += x1addval; + (*col_bt)->rect.xmax += x1addval; } i = std::max(width, i); /* Give the following code the last button in the alignment group, there might have to be a * split immediately after. */ - bt = col_bt ? col_bt->prev : nullptr; + bt = col_bt != end ? col_bt-- : nullptr; } - if (bt && bt->next && bt->rect.xmin < bt->next->rect.xmin) { + if (bt < end && (bt + 1 < end) && (*bt)->rect.xmin < bt[1]->rect.xmin) { /* End of this column, and it's not the last one. */ - for (col_bt = init_col_bt; col_bt->prev != bt; col_bt = col_bt->next) { - col_bt->rect.xmin = x1addval; - col_bt->rect.xmax = x1addval + i + block->bounds; + for (col_bt = init_col_bt; (col_bt - 1) != bt; col_bt++) { + (*col_bt)->rect.xmin = x1addval; + (*col_bt)->rect.xmax = x1addval + i + block->bounds; - ui_but_update(col_bt); /* clips text again */ + ui_but_update((*col_bt).get()); /* clips text again */ } /* And we prepare next column. */ @@ -411,28 +456,28 @@ static void ui_block_bounds_calc_text(uiBlock *block, float offset) } /* Last column. */ - for (col_bt = init_col_bt; col_bt; col_bt = col_bt->next) { + for (col_bt = init_col_bt; col_bt < end; col_bt++) { /* Recognize a horizontally arranged alignment group and skip its items. */ - if (col_bt->next && ui_but_is_row_alignment_group(col_bt, col_bt->next)) { - const int alignnr = col_bt->alignnr; - for (; col_bt && col_bt->alignnr == alignnr; col_bt = col_bt->next) { + if ((col_bt + 1 < end) && ui_but_is_row_alignment_group((*col_bt).get(), col_bt[1].get())) { + const int alignnr = (*col_bt)->alignnr; + for (; col_bt < end && (*col_bt)->alignnr == alignnr; col_bt++) { /* pass */ } } - if (!col_bt) { + if (col_bt == end) { break; } - col_bt->rect.xmin = x1addval; - col_bt->rect.xmax = max_ff(x1addval + i + block->bounds, offset + block->minbounds); + (*col_bt)->rect.xmin = x1addval; + (*col_bt)->rect.xmax = max_ff(x1addval + i + block->bounds, offset + block->minbounds); - ui_but_update(col_bt); /* clips text again */ + ui_but_update((*col_bt).get()); /* clips text again */ } } void ui_block_bounds_calc(uiBlock *block) { - if (BLI_listbase_is_empty(&block->buttons)) { + if (block->buttons.is_empty()) { if (block->panel) { block->rect.xmin = 0.0; block->rect.xmax = block->panel->sizex; @@ -444,7 +489,7 @@ void ui_block_bounds_calc(uiBlock *block) BLI_rctf_init_minmax(&block->rect); - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { BLI_rctf_union(&block->rect, &bt->rect); } @@ -457,7 +502,7 @@ void ui_block_bounds_calc(uiBlock *block) block->rect.xmax = block->rect.xmin + max_ff(BLI_rctf_size_x(&block->rect), block->minbounds); /* hardcoded exception... but that one is annoying with larger safety */ - uiBut *bt = static_cast(block->buttons.first); + uiBut *bt = block->first_but(); const int xof = ((bt && STRPREFIX(bt->str.c_str(), "ERROR")) ? 10 : 40) * UI_SCALE_FAC; block->safety.xmin = block->rect.xmin - xof; @@ -797,21 +842,43 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) return true; } -uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new) +static uiBut *ui_but_find_old(uiBlock *block_old, + const uiBut *but_new, + const blender::Set &ignore_old_buttons) { - LISTBASE_FOREACH (uiBut *, but, &block_old->buttons) { - if (ui_but_equals_old(but_new, but)) { - return but; + for (const std::unique_ptr &but : block_old->buttons) { + if (!ignore_old_buttons.contains(but.get()) && ui_but_equals_old(but_new, but.get())) { + return but.get(); } } return nullptr; } +uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new) +{ + return ui_but_find_old(block_old, but_new, {}); +} + +static std::optional ui_but_find_old_idx( + uiBlock *block_old, + const uiBut *but_new, + const blender::Set &ignore_old_buttons = {}) +{ + int64_t i = 0; + for (const std::unique_ptr &but : block_old->buttons) { + if (!ignore_old_buttons.contains(but.get()) && ui_but_equals_old(but_new, but.get())) { + return i; + } + i++; + } + return std::nullopt; +} + uiBut *ui_but_find_new(uiBlock *block_new, const uiBut *but_old) { - LISTBASE_FOREACH (uiBut *, but, &block_new->buttons) { - if (ui_but_equals_old(but, but_old)) { - return but; + for (const std::unique_ptr &but : block_new->buttons) { + if (ui_but_equals_old(but.get(), but_old)) { + return but.get(); } } return nullptr; @@ -960,49 +1027,84 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) } /** - * \return true when \a but_p is set (only done for active buttons). + * Optimization: + * \a but_old_idx is used to avoid having to lookup the matching button from the old + * block on every iteration. On most redraws, button order doesn't change, so the index of the new + * button is the index of the matching old button. Only if they don't match using the expected + * index, a lookup has to be performed. Even if individual buttons are inserted or removed, likely + * at some point the following buttons (if any) will match again, so successive indices will + * produce successive matches again. Think of \a but_old_idx as a cursor that indicates the + * likely/expected position of the matching button in the old block. This optimization brings + * the whole button updating to O(n) amortized time instead of O(n^2). + * + * \param matched_old_buttons: Collects all previously found matches in the old block. These should + * be ignored when looking up further matches. + * \param but_uptr: The owning pointer for the button to update. The pointed to button may be + * replaced, in which case the function will return true. + * \param but_old_idx: Index into the old-button vector indicating the likely/expected position of + * the matching button in the old block, for the optimization explained above. Value is optional + * because sometimes the expected position of the following matching button can not be determined, + * in which case a full lookup will have to be performed. + * + * \return true when the button pointed to by \a but_uptr is replaced (only done for active + * buttons). */ -static bool ui_but_update_from_old_block(const bContext *C, - uiBlock *block, - uiBut **but_p, - uiBut **but_old_p) +static bool ui_but_update_from_old_block(uiBlock *block, + blender::Set &matched_old_buttons, + std::unique_ptr *but_uptr, + std::optional *but_old_idx) { uiBlock *oldblock = block->oldblock; - uiBut *but = *but_p; + uiBut *but = but_uptr->get(); #if 0 /* Simple method - search every time. Keep this for easy testing of the "fast path." */ - uiBut *oldbut = ui_but_find_old(oldblock, but); + uiBut *oldbut = ui_but_find_old(oldblock, but, matched_old_buttons); UNUSED_VARS(but_old_p); #else - BLI_assert(*but_old_p == nullptr || BLI_findindex(&oldblock->buttons, *but_old_p) != -1); + BLI_assert(!but_old_idx->has_value() || oldblock->buttons.index_range().contains(**but_old_idx)); /* As long as old and new buttons are aligned, avoid loop-in-loop (calling #ui_but_find_old). */ - uiBut *oldbut; - if (LIKELY(*but_old_p && ui_but_equals_old(but, *but_old_p))) { - oldbut = *but_old_p; + std::unique_ptr *oldbut_uptr; + if (LIKELY(but_old_idx->has_value() && + /* Ignore previously matched buttons. */ + !matched_old_buttons.contains(oldblock->buttons[**but_old_idx].get()) && + ui_but_equals_old(but, oldblock->buttons[**but_old_idx].get()))) + { + oldbut_uptr = &oldblock->buttons[**but_old_idx]; } else { /* Fallback to block search. */ - oldbut = ui_but_find_old(oldblock, but); + *but_old_idx = ui_but_find_old_idx(oldblock, but, matched_old_buttons); + oldbut_uptr = but_old_idx->has_value() ? &oldblock->buttons[**but_old_idx] : nullptr; } - (*but_old_p) = oldbut ? oldbut->next : nullptr; + /* Increase for next iteration. */ + *but_old_idx = (but_old_idx->has_value() && + (but_old_idx->value() + 1 < oldblock->buttons.size())) ? + std::optional{but_old_idx->value() + 1} : + std::nullopt; #endif bool found_active = false; - if (!oldbut) { + if (!oldbut_uptr) { return false; } + uiBut *oldbut = oldbut_uptr->get(); + + BLI_assert(!matched_old_buttons.contains(oldbut)); if (oldbut->active || oldbut->semi_modal_state) { /* Move button over from oldblock to new block. */ - BLI_remlink(&oldblock->buttons, oldbut); - BLI_insertlinkafter(&block->buttons, but, oldbut); + oldbut_uptr->swap(*but_uptr); + BLI_assert(but_uptr->get() == oldbut); + /* `but` was moved to oldblock, taking oldbut place. */ + BLI_assert(!matched_old_buttons.contains(but)); + matched_old_buttons.add(but); + /* Add the old button to the button groups in the new block. */ ui_button_group_replace_but_ptr(block, but, oldbut); oldbut->block = block; - *but_p = oldbut; ui_but_update_old_active_from_new(oldbut, but); @@ -1010,12 +1112,10 @@ static bool ui_but_update_from_old_block(const bContext *C, UI_butstore_register_update(block, oldbut, but); } - BLI_remlink(&block->buttons, but); - ui_but_free(C, but); - found_active = true; } else { + matched_old_buttons.add(oldbut); int flag_copy = UI_BUT_DRAG_MULTI; /* Stupid special case: The active button may be inside (as in, overlapped on top) a row @@ -1025,11 +1125,6 @@ static bool ui_but_update_from_old_block(const bContext *C, } but->flag = (but->flag & ~flag_copy) | (oldbut->flag & flag_copy); - - /* ensures one button can get activated, and in case the buttons - * draw are the same this gives O(1) lookup for each button */ - BLI_remlink(&oldblock->buttons, oldbut); - ui_but_free(C, oldbut); } return found_active; @@ -1065,11 +1160,11 @@ bool UI_but_active_only_ex( } else if ((found == true) && (isactive == false)) { if (remove_on_failure) { - BLI_remlink(&block->buttons, but); if (but->layout) { ui_layout_remove_but(but->layout, but); } ui_but_free(C, but); + block->remove_but(but); } return false; } @@ -1089,11 +1184,11 @@ bool UI_block_active_only_flagged_buttons(const bContext *C, ARegion *region, ui BLI_assert(block->endblock); bool done = false; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->flag & UI_BUT_ACTIVATE_ON_INIT) { but->flag &= ~UI_BUT_ACTIVATE_ON_INIT; - if (ui_but_is_editable(but)) { - if (UI_but_active_only_ex(C, region, block, but, false)) { + if (ui_but_is_editable(but.get())) { + if (UI_but_active_only_ex(C, region, block, but.get(), false)) { done = true; break; } @@ -1104,7 +1199,7 @@ bool UI_block_active_only_flagged_buttons(const bContext *C, ARegion *region, ui if (done) { /* Run this in a second pass since it's possible activating the button * removes the buttons being looped over. */ - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { but->flag &= ~UI_BUT_ACTIVATE_ON_INIT; } } @@ -1153,7 +1248,7 @@ static void ui_menu_block_set_keyaccels(uiBlock *block) /* 2 Passes: One for first letter only, second for any letter if the first pass fails. * Run first pass on all buttons so first word chars always get first priority. */ - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (!ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_BUT_MENU, @@ -1569,21 +1664,21 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) } if (block->flag & UI_BLOCK_PIE_MENU) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->pie_dir != UI_RADIAL_NONE) { - const std::string str = ui_but_pie_direction_string(but); - ui_but_add_shortcut(but, str.c_str(), false); + const std::string str = ui_but_pie_direction_string(but.get()); + ui_but_add_shortcut(but.get(), str.c_str(), false); } } } else { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) { /* Skip icon-only buttons (as used in the toolbar). */ if (but->drawstr[0] == '\0') { continue; } - if (((block->flag & UI_BLOCK_POPOVER) == 0) && UI_but_is_tool(but)) { + if (((block->flag & UI_BLOCK_POPOVER) == 0) && UI_but_is_tool(but.get())) { /* For non-popovers, shown in shortcut only * (has special shortcut handling code). */ continue; @@ -1593,13 +1688,13 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) continue; } - if (const std::optional str = ui_but_event_operator_string(C, but)) { - ui_but_add_shortcut(but, str->c_str(), false); + if (const std::optional str = ui_but_event_operator_string(C, but.get())) { + ui_but_add_shortcut(but.get(), str->c_str(), false); } - else if (const std::optional str = ui_but_event_property_operator_string(C, - but)) + else if (const std::optional str = ui_but_event_property_operator_string( + C, but.get())) { - ui_but_add_shortcut(but, str->c_str(), false); + ui_but_add_shortcut(but.get(), str->c_str(), false); } } } @@ -1846,22 +1941,28 @@ void UI_block_update_from_old(const bContext *C, uiBlock *block) return; } - uiBut *but_old = static_cast(block->oldblock->buttons.first); - if (BLI_listbase_is_empty(&block->oldblock->butstore) == false) { UI_butstore_update(block); } - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (ui_but_update_from_old_block(C, block, &but, &but_old)) { - ui_but_update(but); + std::optional but_old_idx = block->oldblock->buttons.is_empty() ? std::nullopt : + std::optional{0}; + blender::Set matched_old_buttons; + matched_old_buttons.reserve(block->oldblock->buttons.size()); + for (std::unique_ptr &but : block->buttons) { + if (ui_but_update_from_old_block(block, matched_old_buttons, &but, &but_old_idx)) { + ui_but_update(but.get()); /* redraw dynamic tooltip if we have one open */ if (but->tip_func) { - UI_but_tooltip_refresh((bContext *)C, but); + UI_but_tooltip_refresh((bContext *)C, but.get()); } } } + for (const std::unique_ptr &but : block->oldblock->buttons) { + ui_but_free(C, but.get()); + } + block->oldblock->buttons.clear_and_shrink(); block->auto_open = block->oldblock->auto_open; block->auto_open_last = block->oldblock->auto_open_last; @@ -1947,8 +2048,8 @@ void UI_block_end_ex(const bContext *C, BLI_assert(block->active); /* Extend button data. This needs to be done before the block updating. */ - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - ui_but_predefined_extra_operator_icons_add(but); + for (const std::unique_ptr &but : block->buttons) { + ui_but_predefined_extra_operator_icons_add(but.get()); } UI_block_update_from_old(C, block); @@ -1957,32 +2058,32 @@ void UI_block_end_ex(const bContext *C, * on matching buttons, we need this to make button event handling non * blocking, while still allowing buttons to be remade each redraw as it * is expected by blender code */ - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { /* temp? Proper check for graying out */ if (but->optype) { wmOperatorType *ot = but->optype; - if (ot == nullptr || !ui_but_context_poll_operator((bContext *)C, ot, but)) { + if (ot == nullptr || !ui_but_context_poll_operator((bContext *)C, ot, but.get())) { but->flag |= UI_BUT_DISABLED; } } LISTBASE_FOREACH (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) { - if (!ui_but_context_poll_operator_ex((bContext *)C, but, op_icon->optype_params)) { + if (!ui_but_context_poll_operator_ex((bContext *)C, but.get(), op_icon->optype_params)) { op_icon->disabled = true; } } const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( depsgraph, (scene) ? scene->r.cfra : 0.0f); - ui_but_anim_flag(but, &anim_eval_context); - ui_but_override_flag(bmain, but); + ui_but_anim_flag(but.get(), &anim_eval_context); + ui_but_override_flag(bmain, but.get()); if (UI_but_is_decorator(but)) { - ui_but_anim_decorate_update_from_flag((uiButDecorator *)but); + ui_but_anim_decorate_update_from_flag((uiButDecorator *)but.get()); } #ifndef NDEBUG - ui_but_validate(but); + ui_but_validate(but.get()); #endif } @@ -2159,12 +2260,12 @@ void UI_block_draw(const bContext *C, uiBlock *block) UI_widgetbase_draw_cache_begin(); /* widgets */ - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->flag & (UI_HIDDEN | UI_SCROLLED)) { continue; } - ui_but_to_pixelrect(&rect, region, block, but); + ui_but_to_pixelrect(&rect, region, block, but.get()); /* Optimization: Don't draw buttons that are not visible (outside view bounds). */ if (!ui_but_pixelrect_in_view(region, &rect)) { continue; @@ -2173,7 +2274,7 @@ void UI_block_draw(const bContext *C, uiBlock *block) /* XXX: figure out why invalid coordinates happen when closing render window */ /* and material preview is redrawn in main window (temp fix for bug #23848) */ if (rect.xmin < rect.xmax && rect.ymin < rect.ymax) { - ui_draw_but(C, region, &style, but, &rect); + ui_draw_but(C, region, &style, but.get(), &rect); } } @@ -2191,7 +2292,7 @@ static void ui_block_message_subscribe(ARegion *region, wmMsgBus *mbus, uiBlock { uiBut *but_prev = nullptr; /* possibly we should keep the region this block is contained in? */ - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->rnapoin.type && but->rnaprop) { /* quick check to avoid adding buttons representing a vector, multiple times. */ if ((but_prev && (but_prev->rnaprop == but->rnaprop) && @@ -2205,7 +2306,7 @@ static void ui_block_message_subscribe(ARegion *region, wmMsgBus *mbus, uiBlock value.user_data = region; value.notify = ED_region_do_msg_notify_tag_redraw; WM_msg_subscribe_rna(mbus, &but->rnapoin, but->rnaprop, &value, __func__); - but_prev = but; + but_prev = but.get(); } } } @@ -2816,9 +2917,9 @@ uiBut *ui_but_drag_multi_edit_get(uiBut *but) BLI_assert(but->flag & UI_BUT_DRAG_MULTI); - LISTBASE_FOREACH (uiBut *, but_iter, &but->block->buttons) { + for (const std::unique_ptr &but_iter : but->block->buttons) { if (but_iter->editstr) { - return_but = but_iter; + return_but = but_iter.get(); break; } } @@ -3537,69 +3638,10 @@ static void ui_but_free_type_specific(uiBut *but) } /** - * Ensures that the proper type of data is destructed, from a generic #uiBut pointer. Should always - * match behavior of #ui_but_new. + * Frees internal data owned by the #but, however this does not free the #but itself, the + * #but is managed with a #std::unique_ptr, this must be called before the #std::unique_ptr owner + * is destroyed. */ -static void ui_but_mem_delete(const uiBut *but) -{ - switch (but->type) { - case UI_BTYPE_NUM: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_NUM_SLIDER: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_COLOR: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_DECORATOR: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_TAB: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_SEARCH_MENU: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_PROGRESS: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_SEPR_LINE: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_HSVCUBE: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_COLORBAND: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_CURVE: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_CURVEPROFILE: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_HOTKEY_EVENT: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_VIEW_ITEM: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_LABEL: - MEM_delete(reinterpret_cast(but)); - break; - case UI_BTYPE_SCROLL: - MEM_delete(reinterpret_cast(but)); - break; - default: - BLI_assert_msg(MEM_allocN_len(but) == sizeof(uiBut), - "Derived button type needs type specific deletion"); - MEM_delete(but); - break; - } -} - -/* can be called with C==nullptr */ static void ui_but_free(const bContext *C, uiBut *but) { if (but->opptr) { @@ -3655,8 +3697,6 @@ static void ui_but_free(const bContext *C, uiBut *but) ui_but_extra_operator_icons_free(but); BLI_assert(UI_butstore_is_registered(but->block, but) == false); - - ui_but_mem_delete(but); } static void ui_block_free_active_operator(uiBlock *block) @@ -3686,9 +3726,10 @@ void UI_block_free(const bContext *C, uiBlock *block) { UI_butstore_clear(block); - while (uiBut *but = static_cast(BLI_pophead(&block->buttons))) { - ui_but_free(C, but); + for (const std::unique_ptr &but : block->buttons) { + ui_but_free(C, but.get()); } + block->buttons.clear(); if (block->unit) { MEM_freeN((void *)block->unit); @@ -4161,64 +4202,62 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]) /** * Factory function: Allocate button and set #uiBut.type. - * - * \note #ui_but_mem_delete is the matching 'destructor' function. */ -static uiBut *ui_but_new(const eButType type) +static std::unique_ptr ui_but_new(const eButType type) { - uiBut *but = nullptr; + std::unique_ptr but{}; switch (type) { case UI_BTYPE_NUM: - but = MEM_new("uiButNumber"); + but = std::make_unique(); break; case UI_BTYPE_NUM_SLIDER: - but = MEM_new("uiButNumber"); + but = std::make_unique(); break; case UI_BTYPE_COLOR: - but = MEM_new("uiButColor"); + but = std::make_unique(); break; case UI_BTYPE_DECORATOR: - but = MEM_new("uiButDecorator"); + but = std::make_unique(); break; case UI_BTYPE_TAB: - but = MEM_new("uiButTab"); + but = std::make_unique(); break; case UI_BTYPE_SEARCH_MENU: - but = MEM_new("uiButSearch"); + but = std::make_unique(); break; case UI_BTYPE_PROGRESS: - but = MEM_new("uiButProgress"); + but = std::make_unique(); break; case UI_BTYPE_SEPR_LINE: - but = MEM_new("uiButSeparatorLine"); + but = std::make_unique(); break; case UI_BTYPE_HSVCUBE: - but = MEM_new("uiButHSVCube"); + but = std::make_unique(); break; case UI_BTYPE_COLORBAND: - but = MEM_new("uiButColorBand"); + but = std::make_unique(); break; case UI_BTYPE_CURVE: - but = MEM_new("uiButCurveMapping"); + but = std::make_unique(); break; case UI_BTYPE_CURVEPROFILE: - but = MEM_new("uiButCurveProfile"); + but = std::make_unique(); break; case UI_BTYPE_HOTKEY_EVENT: - but = MEM_new("uiButHotkeyEvent"); + but = std::make_unique(); break; case UI_BTYPE_VIEW_ITEM: - but = MEM_new("uiButViewItem"); + but = std::make_unique(); break; case UI_BTYPE_LABEL: - but = MEM_new("uiButLabel"); + but = std::make_unique(); break; case UI_BTYPE_SCROLL: - but = MEM_new("uiButScrollBar"); + but = std::make_unique(); break; default: - but = MEM_new("uiBut"); + but = std::make_unique(); break; } @@ -4233,17 +4272,17 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) return but; } - uiBut *insert_after_but = but->prev; + const int64_t but_index = but->block->but_index(but); /* Remove old button address */ - BLI_remlink(&but->block->buttons, but); + std::unique_ptr old_but_ptr = std::move(but->block->buttons[but_index]); - const uiBut *old_but_ptr = but; /* Button may have pointer to a member within itself, this will have to be updated. */ const bool has_poin_ptr_to_self = but->poin == (char *)but; /* Copy construct button with the new type. */ - but = ui_but_new(new_type); + but->block->buttons[but_index] = ui_but_new(new_type); + but = but->block->buttons[but_index].get(); *but = *old_but_ptr; /* We didn't mean to override this :) */ but->type = new_type; @@ -4251,22 +4290,18 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) but->poin = (char *)but; } - 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); + const bool found_layout = ui_layout_replace_but_ptr(but->layout, old_but_ptr.get(), but); BLI_assert(found_layout); UNUSED_VARS_NDEBUG(found_layout); - ui_button_group_replace_but_ptr(uiLayoutGetBlock(but->layout), old_but_ptr, but); + ui_button_group_replace_but_ptr(uiLayoutGetBlock(but->layout), old_but_ptr.get(), but); } #ifdef WITH_PYTHON if (UI_editsource_enable_check()) { - UI_editsource_but_replace(old_but_ptr, but); + UI_editsource_but_replace(old_but_ptr.get(), but); } #endif - ui_but_mem_delete(old_but_ptr); - return but; } @@ -4297,7 +4332,8 @@ static uiBut *ui_def_but(uiBlock *block, } } - uiBut *but = ui_but_new((eButType)(type & BUTTYPE)); + block->buttons.append(ui_but_new((eButType)(type & BUTTYPE))); + uiBut *but = block->buttons.last().get(); but->pointype = (eButPointerType)(type & UI_BUT_POIN_TYPES); but->bit = type & UI_BUT_POIN_BIT; @@ -4407,8 +4443,6 @@ static uiBut *ui_def_but(uiBlock *block, but->dragflag |= UI_BUT_DRAG_FULL_BUT; } - BLI_addtail(&block->buttons, but); - if (block->curlayout) { ui_layout_add_but(block->curlayout, but); } @@ -4528,8 +4562,9 @@ static void ui_def_but_rna__menu(bContext *C, uiLayout *layout, void *but_p) const char *title = RNA_property_ui_name(but->rnaprop); /* Is there a non-blank label before this button on the same row? */ - const bool prior_label = but->prev && but->prev->type == UI_BTYPE_LABEL && but->prev->str[0] && - but->prev->alignnr == but->alignnr; + uiBut *but_prev = but->block->prev_but(but); + const bool prior_label = but_prev && but_prev->type == UI_BTYPE_LABEL && but_prev->str[0] && + but_prev->alignnr == but->alignnr; /* When true, store a copy of the description and use the tool-tip callback to return that copy. * This way, further calls to #EnumPropertyRNA::item_fn which occur when evaluating shortcuts diff --git a/source/blender/editors/interface/interface_align.cc b/source/blender/editors/interface/interface_align.cc index 60e409fc3d4..d221efeb901 100644 --- a/source/blender/editors/interface/interface_align.cc +++ b/source/blender/editors/interface/interface_align.cc @@ -376,10 +376,10 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region) /* First loop: we count number of buttons belonging to an align group, * and clear their align flag. * Tabs get some special treatment here, they get aligned to region border. */ - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { /* special case: tabs need to be aligned to a region border, drawflag tells which one */ if (but->type == UI_BTYPE_TAB) { - ui_block_align_but_to_region(but, region); + ui_block_align_but_to_region(but.get(), region); } else { /* Clear old align flags. */ @@ -411,9 +411,9 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region) /* Second loop: we initialize our ButAlign data for each button. */ butal = butal_array; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->alignnr != 0) { - butal->but = but; + butal->but = but.get(); butal->borders[LEFT] = &but->rect.xmin; butal->borders[RIGHT] = &but->rect.xmax; butal->borders[DOWN] = &but->rect.ymin; diff --git a/source/blender/editors/interface/interface_anim.cc b/source/blender/editors/interface/interface_anim.cc index 9dd38ce9318..4bc2f9b9edb 100644 --- a/source/blender/editors/interface/interface_anim.cc +++ b/source/blender/editors/interface/interface_anim.cc @@ -129,16 +129,22 @@ static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but) BLI_assert(UI_but_is_decorator(but)); BLI_assert(but->decorated_rnapoin.data && but->decorated_rnaprop); - - LISTBASE_CIRCULAR_BACKWARD_BEGIN (uiBut *, &but->block->buttons, but_iter, but->prev) { + if (but->block->buttons.is_empty()) { + return nullptr; + } + int i = but->block->but_index(but); + i = i > 0 ? i - 1 : but->block->buttons.size() - 1; + const int start = i; + do { + but_iter = but->block->buttons[i].get(); if (but_iter != but && ui_but_rna_equals_ex( but_iter, &but->decorated_rnapoin, but->decorated_rnaprop, but->decorated_rnaindex)) { return but_iter; } - } - LISTBASE_CIRCULAR_BACKWARD_END(uiBut *, &but->block->buttons, but_iter, but->prev); + i = i > 0 ? i - 1 : but->block->buttons.size() - 1; + } while (i != start); return nullptr; } diff --git a/source/blender/editors/interface/interface_button_sections.cc b/source/blender/editors/interface/interface_button_sections.cc index 1234327fc7f..22f221b83e2 100644 --- a/source/blender/editors/interface/interface_button_sections.cc +++ b/source/blender/editors/interface/interface_button_sections.cc @@ -75,7 +75,7 @@ static Vector button_section_bounds_calc(const ARegion *region, const bool continue; } - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->type == UI_BTYPE_SEPR_SPACER) { /* Start a new section. */ if (has_section_content) { @@ -89,7 +89,7 @@ static Vector button_section_bounds_calc(const ARegion *region, const bool } rcti but_pixelrect; - ui_but_to_pixelrect(&but_pixelrect, region, block, but); + ui_but_to_pixelrect(&but_pixelrect, region, block, but.get()); BLI_rcti_do_minmax_rcti(&cur_section_bounds, &but_pixelrect); has_section_content = true; } diff --git a/source/blender/editors/interface/interface_context_menu.cc b/source/blender/editors/interface/interface_context_menu.cc index c8b93eb432f..cd540c151b0 100644 --- a/source/blender/editors/interface/interface_context_menu.cc +++ b/source/blender/editors/interface/interface_context_menu.cc @@ -1444,7 +1444,7 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *region, Panel *panel) /* evil, force shortcut flag */ { uiBlock *block = uiLayoutGetBlock(layout); - uiBut *but = static_cast(block->buttons.last); + uiBut *but = block->buttons.last().get(); but->flag |= UI_BUT_HAS_SEP_CHAR; } } diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index efc35bbb5d9..83e4a49f021 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -622,22 +622,25 @@ static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but uiBut *ui_but_find_select_in_enum(uiBut *but, int direction) { - uiBut *but_iter = but; + uiBlock *block = but->block; + int i = block->but_index(but); uiBut *but_found = nullptr; BLI_assert(ELEM(direction, -1, 1)); - while ((but_iter->prev) && ui_but_find_select_in_enum__cmp(but_iter->prev, but)) { - but_iter = but_iter->prev; + while (i > 0 && ui_but_find_select_in_enum__cmp(block->buttons[i - 1].get(), but)) { + i--; } - while (but_iter && ui_but_find_select_in_enum__cmp(but_iter, but)) { - if (but_iter->flag & UI_SELECT) { - but_found = but_iter; + while (i < block->buttons.size() && + ui_but_find_select_in_enum__cmp(block->buttons[i].get(), but)) + { + if (block->buttons[i]->flag & UI_SELECT) { + but_found = block->buttons[i].get(); if (direction == 1) { break; } } - but_iter = but_iter->next; + i++; } return but_found; @@ -1200,9 +1203,10 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu ui_apply_but_func(C, but); /* states of other row buttons */ - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { - if (bt != but && bt->poin == but->poin && ELEM(bt->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) { - ui_but_update_edited(bt); + for (const std::unique_ptr &bt : block->buttons) { + if (bt.get() != but && bt->poin == but->poin && ELEM(bt->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) + { + ui_but_update_edited(bt.get()); } } @@ -1427,16 +1431,19 @@ static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->flag & UI_BUT_DRAG_MULTI) { - uiButMultiState *mbut_state = ui_multibut_lookup(data, but); + uiButMultiState *mbut_state = ui_multibut_lookup(data, but.get()); if (mbut_state) { - ui_but_value_set(but, mbut_state->origvalue); + ui_but_value_set(but.get(), mbut_state->origvalue); # ifdef USE_ALLSELECT if (!mbut_state->select_others.elems.is_empty()) { - ui_selectcontext_apply( - C, but, &mbut_state->select_others, mbut_state->origvalue, mbut_state->origvalue); + ui_selectcontext_apply(C, + but.get(), + &mbut_state->select_others, + mbut_state->origvalue, + mbut_state->origvalue); } # else UNUSED_VARS(C); @@ -1492,7 +1499,7 @@ static bool ui_multibut_states_tag(uiBut *but_active, data->multi_data.has_mbuts = false; /* follow ui_but_find_mouse_over_ex logic */ - LISTBASE_FOREACH (uiBut *, but, &but_active->block->buttons) { + for (const std::unique_ptr &but : but_active->block->buttons) { bool drag_prev = false; bool drag_curr = false; @@ -1502,11 +1509,11 @@ static bool ui_multibut_states_tag(uiBut *but_active, drag_prev = true; } - if (ui_but_is_interactive(but, false)) { + if (ui_but_is_interactive(but.get(), false)) { /* drag checks */ - if (but_active != but) { - if (ui_but_is_compatible(but_active, but)) { + if (but_active != but.get()) { + if (ui_but_is_compatible(but_active, but.get())) { BLI_assert(but->active == nullptr); @@ -1534,9 +1541,9 @@ static void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *dat data->multi_data.bs_mbuts = UI_butstore_create(but_active->block); - LISTBASE_FOREACH (uiBut *, but, &but_active->block->buttons) { + for (const std::unique_ptr &but : but_active->block->buttons) { if (but->flag & UI_BUT_DRAG_MULTI) { - ui_multibut_add(data, but); + ui_multibut_add(data, but.get()); } } @@ -1562,12 +1569,12 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl BLI_assert(data->multi_data.init == uiHandleButtonMulti::INIT_ENABLE); BLI_assert(data->multi_data.skip == false); - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (!(but->flag & UI_BUT_DRAG_MULTI)) { continue; } - uiButMultiState *mbut_state = ui_multibut_lookup(data, but); + uiButMultiState *mbut_state = ui_multibut_lookup(data, but.get()); if (mbut_state == nullptr) { /* Highly unlikely. */ @@ -1578,13 +1585,13 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl } void *active_back; - ui_but_execute_begin(C, region, but, &active_back); + ui_but_execute_begin(C, region, but.get(), &active_back); # ifdef USE_ALLSELECT if (data->select_others.is_enabled) { /* init once! */ if (mbut_state->select_others.elems.is_empty()) { - ui_selectcontext_begin(C, but, &mbut_state->select_others); + ui_selectcontext_begin(C, but.get(), &mbut_state->select_others); } if (mbut_state->select_others.elems.is_empty()) { mbut_state->select_others.elems.clear(); @@ -1602,7 +1609,7 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl if (data->text_edit.edit_string) { /* Entering text (set all). */ but->active->value = data->value; - ui_but_string_set(C, but, data->text_edit.edit_string); + ui_but_string_set(C, but.get(), data->text_edit.edit_string); } else { /* Dragging (use delta). */ @@ -1617,7 +1624,7 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl CLAMP(but->active->value, double(but->softmin), double(but->softmax)); } - ui_but_execute_end(C, region, but, active_back); + ui_but_execute_end(C, region, but.get(), active_back); } } @@ -1693,32 +1700,32 @@ static bool ui_drag_toggle_set_xy_xy( ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]); ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]); - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { /* NOTE: ctrl is always true here because (at least for now) * we always want to consider text control in this case, even when not embossed. */ - if (!ui_but_is_interactive(but, true)) { + if (!ui_but_is_interactive(but.get(), true)) { continue; } if (!BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { continue; } - if (!ui_drag_toggle_but_is_supported(but)) { + if (!ui_drag_toggle_but_is_supported(but.get())) { continue; } /* is it pressed? */ - const int pushed_state_but = ui_drag_toggle_but_pushed_state(but); + const int pushed_state_but = ui_drag_toggle_but_pushed_state(but.get()); if (pushed_state_but == pushed_state) { continue; } /* execute the button */ - UI_but_execute(C, region, but); + UI_but_execute(C, region, but.get()); if (do_check) { - ui_but_update_edited(but); + ui_but_update_edited(but.get()); } if (U.runtime.is_dirty == false) { - ui_but_update_preferences_dirty(but); + ui_but_update_preferences_dirty(but.get()); } changed = true; } @@ -3679,7 +3686,8 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa return; } - for (uiBut *but = actbut->next; but; but = but->next) { + for (int64_t i = block->but_index(actbut) + 1; i < block->buttons.size(); i++) { + uiBut *but = block->buttons[i].get(); if (ui_but_is_editable_as_text(but)) { if (!(but->flag & (UI_BUT_DISABLED | UI_HIDDEN))) { data->postbut = but; @@ -3688,10 +3696,13 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } } - for (uiBut *but = static_cast(block->buttons.first); but != actbut; but = but->next) { - if (ui_but_is_editable_as_text(but)) { + for (const std::unique_ptr &but : block->buttons) { + if (but.get() == actbut) { + break; + } + if (ui_but_is_editable_as_text(but.get())) { if (!(but->flag & (UI_BUT_DISABLED | UI_HIDDEN))) { - data->postbut = but; + data->postbut = but.get(); data->posttype = BUTTON_ACTIVATE_TEXT_EDITING; return; } @@ -3712,7 +3723,8 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa return; } - for (uiBut *but = actbut->prev; but; but = but->prev) { + for (int i = block->but_index(actbut) - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); if (ui_but_is_editable_as_text(but)) { if (!(but->flag & (UI_BUT_DISABLED | UI_HIDDEN))) { data->postbut = but; @@ -3721,7 +3733,11 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } } - for (uiBut *but = static_cast(block->buttons.last); but != actbut; but = but->prev) { + for (int i = block->buttons.size() - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); + if (but == actbut) { + break; + } if (ui_but_is_editable_as_text(but)) { if (!(but->flag & (UI_BUT_DISABLED | UI_HIDDEN))) { data->postbut = but; @@ -4931,7 +4947,8 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons const int direction = (type == WHEELDOWNMOUSE) ? -1 : 1; uiBut *but_select = ui_but_find_select_in_enum(but, direction); if (but_select) { - uiBut *but_other = (direction == -1) ? but_select->next : but_select->prev; + uiBut *but_other = (direction == -1) ? but_select->block->next_but(but_select) : + but_select->block->prev_but(but_select); if (but_other && ui_but_find_select_in_enum__cmp(but, but_other)) { ARegion *region = data->region; @@ -8860,12 +8877,12 @@ static void button_activate_exit( #ifdef USE_DRAG_MULTINUM if (data->multi_data.has_mbuts) { - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { if (bt->flag & UI_BUT_DRAG_MULTI) { bt->flag &= ~UI_BUT_DRAG_MULTI; if (!data->cancel) { - ui_apply_but_autokey(C, bt); + ui_apply_but_autokey(C, bt.get()); } } } @@ -8916,7 +8933,7 @@ static void button_activate_exit( /* Disable tool-tips until mouse-move + last active flag. */ LISTBASE_FOREACH (uiBlock *, block_iter, &data->region->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, bt, &block_iter->buttons) { + for (const std::unique_ptr &bt : block_iter->buttons) { bt->flag &= ~UI_BUT_LAST_ACTIVE; } @@ -9021,15 +9038,15 @@ static uiBut *ui_context_button_active(const ARegion *region, bool (*but_check_c /* find active button */ LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->flag & UI_BUT_ACTIVE_OVERRIDE) { - active_but_override = but; + active_but_override = but.get(); } if (but->active) { - active_but_real = but; + active_but_real = but.get(); } if (but->flag & UI_BUT_LAST_ACTIVE) { - active_but_last = but; + active_but_last = but.get(); } } } @@ -9199,20 +9216,20 @@ void UI_context_update_anim_flag(const bContext *C) uiBut *activebut = nullptr; LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - ui_but_anim_flag(but, &anim_eval_context); - ui_but_override_flag(CTX_data_main(C), but); + for (const std::unique_ptr &but : block->buttons) { + ui_but_anim_flag(but.get(), &anim_eval_context); + ui_but_override_flag(CTX_data_main(C), but.get()); if (UI_but_is_decorator(but)) { - ui_but_anim_decorate_update_from_flag((uiButDecorator *)but); + ui_but_anim_decorate_update_from_flag((uiButDecorator *)but.get()); } ED_region_tag_redraw(region); if (but->active) { - activebut = but; + activebut = but.get(); } else if (!activebut && (but->flag & UI_BUT_LAST_ACTIVE)) { - activebut = but; + activebut = but.get(); } } } @@ -9259,9 +9276,9 @@ void ui_but_update_view_for_active(const bContext *C, const uiBlock *block) static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) { LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but == event->customdata) { - return but; + for (const std::unique_ptr &but : block->buttons) { + if (but.get() == event->customdata) { + return but.get(); } } } @@ -9446,9 +9463,9 @@ static void foreach_semi_modal_but_as_active(bContext *C, * every actually a use-case for multiple semi-active buttons at the same time. */ LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if ((but->flag2 & UI_BUT2_FORCE_SEMI_MODAL_ACTIVE) || but->semi_modal_state) { - with_but_active_as_semi_modal(C, region, but, [&]() { fn(but); }); + with_but_active_as_semi_modal(C, region, but.get(), [&]() { fn(but.get()); }); } } } @@ -10090,8 +10107,8 @@ static int ui_handle_viewlist_items_hover(const wmEvent *event, ARegion *region) } LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but == highlight_row_but) { + for (const std::unique_ptr &but : block->buttons) { + if (but.get() == highlight_row_but) { continue; } if (!ELEM(but->type, UI_BTYPE_VIEW_ITEM, UI_BTYPE_LISTROW)) { @@ -10382,7 +10399,7 @@ static void ui_menu_scroll_apply_offset_y(ARegion *region, uiBlock *block, float if (dy < 0.0f) { /* Stop at top item, extra 0.5 UI_UNIT_Y makes it snap nicer. */ float ymax = -FLT_MAX; - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { ymax = max_ff(ymax, bt->rect.ymax); } if (ymax + dy - UI_UNIT_Y * 0.5f < block->rect.ymax - scroll_pad) { @@ -10392,7 +10409,7 @@ static void ui_menu_scroll_apply_offset_y(ARegion *region, uiBlock *block, float else { /* Stop at bottom item, extra 0.5 UI_UNIT_Y makes it snap nicer. */ float ymin = FLT_MAX; - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { ymin = min_ff(ymin, bt->rect.ymin); } if (ymin + dy + UI_UNIT_Y * 0.5f > block->rect.ymin + scroll_pad) { @@ -10406,7 +10423,7 @@ static void ui_menu_scroll_apply_offset_y(ARegion *region, uiBlock *block, float ui_layout_panel_popup_scroll_apply(block->panel, dy); /* apply scroll offset */ - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { bt->rect.ymin += dy; bt->rect.ymax += dy; } @@ -11015,7 +11032,7 @@ static int ui_handle_menu_event(bContext *C, } count = 0; - for (but = static_cast(block->buttons.first); but; but = but->next) { + for (const std::unique_ptr &but : block->buttons) { bool doit = false; if (!ELEM(but->type, @@ -11056,7 +11073,7 @@ static int ui_handle_menu_event(bContext *C, activate = BUTTON_ACTIVATE_APPLY; } - ui_handle_button_activate(C, region, but, activate); + ui_handle_button_activate(C, region, but.get(), activate); break; } } @@ -11122,17 +11139,17 @@ static int ui_handle_menu_event(bContext *C, } /* Accelerator keys that allow "pressing" a menu entry by pressing a single key. */ - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + for (const std::unique_ptr &but_iter : block->buttons) { if (!(but_iter->flag & UI_BUT_DISABLED) && but_iter->menu_key == event->type) { if (ELEM(but_iter->type, UI_BTYPE_BUT, UI_BTYPE_ICON_TOGGLE, UI_BTYPE_ICON_TOGGLE_N)) { - UI_but_execute(C, region, but_iter); + UI_but_execute(C, region, but_iter.get()); } else { - ui_handle_button_activate_by_type(C, region, but_iter); + ui_handle_button_activate_by_type(C, region, but_iter.get()); } return WM_UI_HANDLER_BREAK; } @@ -11421,9 +11438,9 @@ static int ui_but_pie_menu_apply(bContext *C, static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, RadialDirection dir) { if ((block->flag & UI_BLOCK_NUMSELECT) && event->val == KM_PRESS) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->pie_dir == dir && !ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { - return but; + return but.get(); } } } @@ -11505,7 +11522,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle block->pie_data.flags |= UI_PIE_ANIMATION_FINISHED; } - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->pie_dir != UI_RADIAL_NONE) { float vec[2]; float center[2]; @@ -11668,9 +11685,9 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK) && ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0)) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->menu_key == event->type) { - ui_but_pie_button_activate(C, but, menu); + ui_but_pie_button_activate(C, but.get(), menu); } } } @@ -12235,12 +12252,12 @@ bool UI_textbutton_activate_rna(const bContext *C, uiBut *but_text = nullptr; LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->type == UI_BTYPE_TEXT) { if (but->rnaprop && but->rnapoin.data == rna_poin_data) { if (STREQ(RNA_property_identifier(but->rnaprop), rna_prop_id)) { block_text = block; - but_text = but; + but_text = but.get(); break; } } @@ -12270,10 +12287,10 @@ bool UI_textbutton_activate_but(const bContext *C, uiBut *actbut) uiBut *but_text = nullptr; LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but == actbut && but->type == UI_BTYPE_TEXT) { + for (const std::unique_ptr &but : block->buttons) { + if (but.get() == actbut && but->type == UI_BTYPE_TEXT) { block_text = block; - but_text = but; + but_text = but.get(); break; } } @@ -12299,11 +12316,11 @@ bool UI_textbutton_activate_but(const bContext *C, uiBut *actbut) void UI_region_free_active_but_all(bContext *C, ARegion *region) { LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->active == nullptr) { continue; } - ui_but_active_free(C, but); + ui_but_active_free(C, but.get()); } } } @@ -12381,7 +12398,7 @@ static uiBlockInteraction_Handle *ui_block_interaction_begin(bContext *C, uiBlockInteraction_Handle *interaction = MEM_cnew(__func__); int unique_retval_ids_len = 0; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) { unique_retval_ids_len++; } @@ -12390,7 +12407,7 @@ static uiBlockInteraction_Handle *ui_block_interaction_begin(bContext *C, int *unique_retval_ids = static_cast( MEM_mallocN(sizeof(*unique_retval_ids) * unique_retval_ids_len, __func__)); unique_retval_ids_len = 0; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) { unique_retval_ids[unique_retval_ids_len++] = but->retval; } diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 36f884c6a59..778e14291ad 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -171,7 +171,6 @@ enum { #define PIE_MAX_ITEMS 8 struct uiBut { - uiBut *next = nullptr, *prev = nullptr; /** Pointer back to the layout item holding this button. */ uiLayout *layout = nullptr; @@ -336,6 +335,8 @@ struct uiBut { uiBut(const uiBut &other) = default; /** Mostly shallow copy, just like copy constructor above. */ uiBut &operator=(const uiBut &other) = default; + + virtual ~uiBut() = default; }; /** Derived struct for #UI_BTYPE_NUM */ @@ -557,7 +558,7 @@ struct uiBlockDynamicListener { struct uiBlock { uiBlock *next, *prev; - ListBase buttons; + blender::Vector> buttons; Panel *panel; uiBlock *oldblock; @@ -673,6 +674,13 @@ struct uiBlock { char display_device[64]; PieMenuData pie_data; + + void remove_but(const uiBut *but); + [[nodiscard]] uiBut *first_but() const; + [[nodiscard]] uiBut *last_but() const; + int but_index(const uiBut *but) const; + [[nodiscard]] uiBut *next_but(const uiBut *but) const; + [[nodiscard]] uiBut *prev_but(const uiBut *but) const; }; struct uiSafetyRct { @@ -680,7 +688,6 @@ struct uiSafetyRct { rctf parent; rctf safety; }; - /* `interface.cc` */ void ui_fontscale(float *points, float aspect); diff --git a/source/blender/editors/interface/interface_layout.cc b/source/blender/editors/interface/interface_layout.cc index f4d47408698..f41ebb70391 100644 --- a/source/blender/editors/interface/interface_layout.cc +++ b/source/blender/editors/interface/interface_layout.cc @@ -529,8 +529,8 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) RNA_property_update(C, ptr, prop); - LISTBASE_FOREACH (uiBut *, cbut, &but->block->buttons) { - ui_but_update(cbut); + for (const std::unique_ptr &cbut : but->block->buttons) { + ui_but_update(cbut.get()); } } } @@ -946,14 +946,13 @@ static void ui_item_enum_expand_tabs(uiLayout *layout, const int h, const bool icon_only) { - uiBut *last = static_cast(block->buttons.last); + const int start_size = block->buttons.size(); ui_item_enum_expand_exec(layout, block, ptr, prop, uiname, h, UI_BTYPE_TAB, icon_only); - BLI_assert(last != block->buttons.last); + BLI_assert(start_size != block->buttons.size()); - for (uiBut *tab = last ? last->next : static_cast(block->buttons.first); tab; - tab = tab->next) - { + for (int i = start_size; i < block->buttons.size(); i++) { + uiBut *tab = block->buttons[i].get(); UI_but_drawflag_enable(tab, ui_but_align_opposite_to_area_align_get(CTX_wm_region(C))); if (icon_only) { UI_but_drawflag_enable(tab, UI_BUT_HAS_TOOLTIP_LABEL); @@ -966,11 +965,9 @@ static void ui_item_enum_expand_tabs(uiLayout *layout, const int highlight_array_len = RNA_property_array_length(ptr_highlight, prop_highlight); blender::Array highlight_array(highlight_array_len); RNA_property_boolean_get_array(ptr_highlight, prop_highlight, highlight_array.data()); - int i = 0; - for (uiBut *tab_but = last ? last->next : static_cast(block->buttons.first); - (tab_but != nullptr) && (i < highlight_array_len); - tab_but = tab_but->next, i++) - { + const int end = std::min(start_size + highlight_array_len, block->buttons.size()); + for (int i = start_size; i < end; i++) { + uiBut *tab_but = block->buttons[i].get(); SET_FLAG_FROM_TEST(tab_but->flag, !highlight_array[i], UI_BUT_INACTIVE); } } @@ -1164,10 +1161,10 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C, } LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but && but->rnapoin.data) { if (RNA_property_type(but->rnaprop) == PROP_STRING) { - prevbut = but; + prevbut = but.get(); } } @@ -1578,7 +1575,7 @@ void uiItemsFullEnumO_items(uiLayout *layout, flag, nullptr); - uiBut *but = static_cast(block->buttons.last); + uiBut *but = block->buttons.last().get(); if (active == (i - 1)) { but->flag |= UI_SELECT_DRAW; @@ -1596,7 +1593,7 @@ void uiItemsFullEnumO_items(uiLayout *layout, if (item->icon || radial) { uiItemL(target, item->name, item->icon); - but = static_cast(block->buttons.last); + but = block->buttons.last().get(); } else { /* Do not use uiItemL here, as our root layout is a menu one, @@ -2340,7 +2337,7 @@ void uiItemFullR(uiLayout *layout, ui_decorate.layout = uiLayoutColumn(layout_row, true); ui_decorate.layout->space = 0; UI_block_layout_set_current(block, layout); - ui_decorate.but = static_cast(block->buttons.last); + ui_decorate.but = block->last_but(); /* Clear after. */ layout->flag |= UI_ITEM_PROP_DECORATE_NO_PAD; @@ -2494,8 +2491,14 @@ void uiItemFullR(uiLayout *layout, #ifdef UI_PROP_DECORATE if (ui_decorate.use_prop_decorate) { - uiBut *but_decorate = ui_decorate.but ? ui_decorate.but->next : - static_cast(block->buttons.first); + uiBut *but_decorate = ui_decorate.but ? block->next_but(ui_decorate.but) : block->first_but(); + + /* Move temporarily last buts to avoid multiple reallocations while inserting decorators. */ + blender::Vector> tmp; + tmp.reserve(block->buttons.capacity()); + while (but_decorate && but_decorate != block->buttons.last().get()) { + tmp.append(block->buttons.pop_last()); + } const bool use_blank_decorator = (flag & UI_ITEM_R_FORCE_BLANK_DECORATE); uiLayout *layout_col = uiLayoutColumn(ui_decorate.layout, false); layout_col->space = 0; @@ -2508,13 +2511,18 @@ void uiItemFullR(uiLayout *layout, /* The icons are set in 'ui_but_anim_flag' */ uiItemDecoratorR_prop(layout_col, ptr_dec, prop_dec, but_decorate->rnaindex); - but = static_cast(block->buttons.last); + but = block->buttons.last().get(); - /* Order the decorator after the button we decorate, this is used so we can always - * do a quick lookup. */ - BLI_remlink(&block->buttons, but); - BLI_insertlinkafter(&block->buttons, but_decorate, but); - but_decorate = but->next; + if (!tmp.is_empty()) { + block->buttons.append(tmp.pop_last()); + but_decorate = block->buttons.last().get(); + } + else { + but_decorate = nullptr; + } + } + while (!tmp.is_empty()) { + block->buttons.append(tmp.pop_last()); } BLI_assert(ELEM(i, 1, ui_decorate.len)); @@ -2561,17 +2569,16 @@ void uiItemFullR_with_popover(uiLayout *layout, const char *panel_type) { uiBlock *block = layout->root->block; - uiBut *but = static_cast(block->buttons.last); + int i = block->buttons.size(); uiItemFullR(layout, ptr, prop, index, value, flag, name, icon); - but = but->next; - while (but) { + for (; i < block->buttons.size(); i++) { + uiBut *but = block->buttons[i].get(); if (but->rnaprop == prop && ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_COLOR)) { ui_but_rna_menu_convert_to_panel_type(but, panel_type); break; } - but = but->next; } - if (but == nullptr) { + if (i == block->buttons.size()) { const StringRefNull propname = RNA_property_identifier(prop); ui_item_disabled(layout, panel_type); RNA_warning("property could not use a popover: %s.%s (%s)", @@ -2592,17 +2599,17 @@ void uiItemFullR_with_menu(uiLayout *layout, const char *menu_type) { uiBlock *block = layout->root->block; - uiBut *but = static_cast(block->buttons.last); + int i = block->buttons.size(); uiItemFullR(layout, ptr, prop, index, value, flag, name, icon); - but = but->next; - while (but) { + while (i < block->buttons.size()) { + uiBut *but = block->buttons[i].get(); if (but->rnaprop == prop && but->type == UI_BTYPE_MENU) { ui_but_rna_menu_convert_to_menu_type(but, menu_type); break; } - but = but->next; + i++; } - if (but == nullptr) { + if (i == block->buttons.size()) { const StringRefNull propname = RNA_property_identifier(prop); ui_item_disabled(layout, menu_type); RNA_warning("property could not use a menu: %s.%s (%s)", @@ -2727,7 +2734,7 @@ void uiItemsEnumR(uiLayout *layout, PointerRNA *ptr, const StringRefNull propnam for (int i = 0; i < totitem; i++) { if (item[i].identifier[0]) { uiItemEnumR_prop(column, item[i].name, item[i].icon, ptr, prop, item[i].value); - ui_but_tip_from_enum_item(static_cast(block->buttons.last), &item[i]); + ui_but_tip_from_enum_item(block->buttons.last().get(), &item[i]); } else { if (item[i].name) { @@ -2736,7 +2743,7 @@ void uiItemsEnumR(uiLayout *layout, PointerRNA *ptr, const StringRefNull propnam } uiItemL(column, item[i].name, ICON_NONE); - uiBut *bt = static_cast(block->buttons.last); + uiBut *bt = block->buttons.last().get(); bt->drawflag = UI_BUT_TEXT_LEFT; ui_but_tip_from_enum_item(bt, &item[i]); diff --git a/source/blender/editors/interface/interface_panel.cc b/source/blender/editors/interface/interface_panel.cc index 0efe1264ebe..8dc5148502a 100644 --- a/source/blender/editors/interface/interface_panel.cc +++ b/source/blender/editors/interface/interface_panel.cc @@ -868,7 +868,7 @@ static void ui_offset_panel_block(uiBlock *block) const int ofsy = block->panel->sizey - style->panelspace; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { but->rect.ymin += ofsy; but->rect.ymax += ofsy; } @@ -949,7 +949,7 @@ static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel * if (parent_panel != nullptr && UI_panel_is_closed(parent_panel)) { /* The parent panel is closed, so this panel can be completely removed. */ UI_block_set_search_only(block, true); - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { but->flag |= UI_HIDDEN; } } diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 4ea5120ec84..6b8579cd325 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -306,7 +306,8 @@ static uiBut *ui_but_find(const ARegion *region, const void *find_custom_data) { LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + for (int i = block->buttons.size() - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); if (find_poll && find_poll(but, find_custom_data) == false) { continue; } @@ -333,7 +334,8 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); - LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + for (int i = block->buttons.size() - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); if (find_poll && find_poll(but, find_custom_data) == false) { continue; } @@ -385,7 +387,8 @@ uiBut *ui_but_find_rect_over(const ARegion *region, const rcti *rect_px) rctf rect_block; ui_window_to_block_rctf(region, block, &rect_block, &rect_px_fl); - LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + for (int i = block->buttons.size() - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); if (ui_but_is_interactive(but, labeledit)) { /* No pie menu support. */ BLI_assert(but->pie_dir == UI_RADIAL_NONE); @@ -415,7 +418,8 @@ uiBut *ui_list_find_mouse_over_ex(const ARegion *region, const int xy[2]) LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); - LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + for (int i = block->buttons.size() - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); if (but->type == UI_BTYPE_LISTBOX && ui_but_contains_pt(but, mx, my)) { return but; } @@ -543,8 +547,8 @@ uiBut *ui_view_item_find_search_highlight(const ARegion *region) uiBut *ui_but_prev(uiBut *but) { - while (but->prev) { - but = but->prev; + for (int idx = but->block->but_index(but) - 1; idx >= 0; idx--) { + but = but->block->buttons[idx].get(); if (ui_but_is_editable(but)) { return but; } @@ -554,8 +558,8 @@ uiBut *ui_but_prev(uiBut *but) uiBut *ui_but_next(uiBut *but) { - while (but->next) { - but = but->next; + for (int i = but->block->but_index(but) + 1; i < but->block->buttons.size(); i++) { + but = but->block->buttons[i].get(); if (ui_but_is_editable(but)) { return but; } @@ -565,9 +569,9 @@ uiBut *ui_but_next(uiBut *but) uiBut *ui_but_first(uiBlock *block) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (ui_but_is_editable(but)) { - return but; + for (const std::unique_ptr &but : block->buttons) { + if (ui_but_is_editable(but.get())) { + return but.get(); } } return nullptr; @@ -575,12 +579,11 @@ uiBut *ui_but_first(uiBlock *block) uiBut *ui_but_last(uiBlock *block) { - uiBut *but = static_cast(block->buttons.last); - while (but) { + for (int i = block->buttons.size() - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); if (ui_but_is_editable(but)) { return but; } - but = but->prev; } return nullptr; } @@ -649,9 +652,9 @@ size_t ui_but_tip_len_only_first_line(const uiBut *but) uiBut *ui_block_active_but_get(const uiBlock *block) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->active) { - return but; + return but.get(); } } @@ -682,7 +685,11 @@ bool ui_block_is_popup_any(const uiBlock *block) static const uiBut *ui_but_next_non_separator(const uiBut *but) { - for (; but; but = but->next) { + if (!but) { + return nullptr; + } + for (int i = but->block->but_index(but); i < but->block->buttons.size(); i++) { + but = but->block->buttons[i].get(); if (!ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { return but; } @@ -692,13 +699,13 @@ static const uiBut *ui_but_next_non_separator(const uiBut *but) bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title) { - const uiBut *but = static_cast(block->buttons.first); + const uiBut *but = block->first_but(); if (skip_title) { /* Skip the first label, since popups often have a title, * we may want to consider the block empty in this case. */ but = ui_but_next_non_separator(but); if (but && but->type == UI_BTYPE_LABEL) { - but = but->next; + but = block->next_but(but); } } return (ui_but_next_non_separator(but) == nullptr); @@ -712,7 +719,7 @@ bool UI_block_is_empty(const uiBlock *block) bool UI_block_can_add_separator(const uiBlock *block) { if (ui_block_is_menu(block) && !ui_block_is_pie_menu(block)) { - const uiBut *but = static_cast(block->buttons.last); + const uiBut *but = block->last_but(); return (but && !ELEM(but->type, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR)); } return true; @@ -720,7 +727,7 @@ bool UI_block_can_add_separator(const uiBlock *block) bool UI_block_has_active_default_button(const uiBlock *block) { - LISTBASE_FOREACH (const uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if ((but->flag & UI_BUT_ACTIVE_DEFAULT) && ((but->flag & UI_HIDDEN) == 0)) { return true; } @@ -780,9 +787,9 @@ uiBut *ui_region_find_active_but(ARegion *region) uiBut *ui_region_find_first_but_test_flag(ARegion *region, int flag_include, int flag_exclude) { LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (((but->flag & flag_include) == flag_include) && ((but->flag & flag_exclude) == 0)) { - return but; + return but.get(); } } } diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index adf90f4f258..bdd581bc9f8 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -1022,7 +1022,8 @@ std::optional UI_key_event_operator_string(const bContext *C, short event_type = KM_NOTHING; uiBut *listbox = nullptr; - LISTBASE_FOREACH_BACKWARD (uiBut *, but_iter, &but->block->buttons) { + for (int i = but->block->buttons.size() - 1; i >= 0; i--) { + uiBut *but_iter = but->block->buttons[i].get(); if ((but_iter->type == UI_BTYPE_LISTBOX) && ui_but_contains_rect(but_iter, &but->rect)) { listbox = but_iter; break; diff --git a/source/blender/editors/interface/interface_widgets.cc b/source/blender/editors/interface/interface_widgets.cc index 54275fee2f2..7135d99c334 100644 --- a/source/blender/editors/interface/interface_widgets.cc +++ b/source/blender/editors/interface/interface_widgets.cc @@ -2593,10 +2593,10 @@ static bool draw_emboss(const uiBut *but) if (but->drawflag & UI_BUT_ALIGN_DOWN) { return false; } - + uiBut *but_next = but->block->next_but(but); if (but->type == UI_BTYPE_TAB && (BLI_rctf_size_y(&but->block->rect) > BLI_rctf_size_x(&but->block->rect)) && - !(but->next == nullptr || but->next->type == UI_BTYPE_SEPR)) + !(but_next == nullptr || but_next->type == UI_BTYPE_SEPR)) { /* Vertical tabs, emboss at end and before separators. */ return false; diff --git a/source/blender/editors/interface/regions/interface_region_color_picker.cc b/source/blender/editors/interface/regions/interface_region_color_picker.cc index 9b441691591..abe7f875a20 100644 --- a/source/blender/editors/interface/regions/interface_region_color_picker.cc +++ b/source/blender/editors/interface/regions/interface_region_color_picker.cc @@ -195,16 +195,16 @@ static void ui_update_color_picker_buts_rgba(uiBut *from_but, { ui_color_picker_update_hsv(cpicker, from_but, rgba_scene_linear); - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { if (bt->custom_data != cpicker) { continue; } if (bt->rnaprop) { - ui_but_v4_set(bt, rgba_scene_linear); + ui_but_v4_set(bt.get(), rgba_scene_linear); /* original button that created the color picker already does undo * push, so disable it on RNA buttons in the color picker block */ - UI_but_flag_disable(bt, UI_BUT_UNDO); + UI_but_flag_disable(bt.get(), UI_BUT_UNDO); } else if (bt->type == UI_BTYPE_TEXT) { /* Hex text input field. */ @@ -231,7 +231,7 @@ static void ui_update_color_picker_buts_rgba(uiBut *from_but, memcpy(bt->poin, col, col_len + 1); /* +1 offset for the # symbol. */ } - ui_but_update(bt); + ui_but_update(bt.get()); } } @@ -339,7 +339,7 @@ static void ui_popup_close_cb(bContext * /*C*/, void *bt1, void * /*arg*/) static void ui_colorpicker_hide_reveal(uiBlock *block, ePickerType colormode) { /* tag buttons */ - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { if ((bt->func == ui_colorpicker_rgba_update_cb) && (bt->type == UI_BTYPE_NUM_SLIDER) && (bt->rnaindex != 3)) { @@ -853,7 +853,7 @@ static int ui_colorpicker_wheel_cb(const bContext * /*C*/, uiBlock *block, const } if (add != 0.0f) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->type == UI_BTYPE_HSVCUBE && but->active == nullptr) { uiPopupBlockHandle *popup = block->handle; ColorPicker *cpicker = static_cast(but->custom_data); @@ -861,8 +861,8 @@ static int ui_colorpicker_wheel_cb(const bContext * /*C*/, uiBlock *block, const /* Get the RGBA Color. */ float rgba_perceptual[4]; - ui_but_v4_get(but, rgba_perceptual); - ui_scene_linear_to_perceptual_space(but, rgba_perceptual); + ui_but_v4_get(but.get(), rgba_perceptual); + ui_scene_linear_to_perceptual_space(but.get(), rgba_perceptual); /* Convert it to HSV. */ ui_color_picker_rgb_to_hsv_compat(rgba_perceptual, hsv_perceptual); @@ -874,11 +874,11 @@ static int ui_colorpicker_wheel_cb(const bContext * /*C*/, uiBlock *block, const float rgba_scene_linear[4]; rgba_scene_linear[3] = rgba_perceptual[3]; /* Transfer Alpha component. */ ui_color_picker_hsv_to_rgb(hsv_perceptual, rgba_scene_linear); - ui_perceptual_to_scene_linear_space(but, rgba_scene_linear); - ui_but_v4_set(but, rgba_scene_linear); + ui_perceptual_to_scene_linear_space(but.get(), rgba_scene_linear); + ui_but_v4_set(but.get(), rgba_scene_linear); /* Update all other Color Picker buttons to reflect the color change. */ - ui_update_color_picker_buts_rgba(but, block, cpicker, rgba_scene_linear); + ui_update_color_picker_buts_rgba(but.get(), block, cpicker, rgba_scene_linear); if (popup) { popup->menuretval = UI_RETURN_UPDATE; } diff --git a/source/blender/editors/interface/regions/interface_region_menu_popup.cc b/source/blender/editors/interface/regions/interface_region_menu_popup.cc index 81ae868be25..50382560196 100644 --- a/source/blender/editors/interface/regions/interface_region_menu_popup.cc +++ b/source/blender/editors/interface/regions/interface_region_menu_popup.cc @@ -127,7 +127,7 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) } /* get */ - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + for (const std::unique_ptr &but_iter : block->buttons) { /* Prevent labels (typically headings), from being returned in the case the text * happens to matches one of the menu items. * Skip separators too as checking them is redundant. */ @@ -136,7 +136,7 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) } if (mem[hash_mod] == ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR)) { - return but_iter; + return but_iter.get(); } } @@ -318,16 +318,16 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi /* position mouse at 0.8*width of the button and below the tile * on the first item */ offset[0] = 0; - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + for (const std::unique_ptr &but_iter : block->buttons) { offset[0] = min_ii(offset[0], -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect))); } offset[1] = 2.1 * UI_UNIT_Y; - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { - if (ui_but_is_editable(but_iter)) { - but_activate = but_iter; + for (const std::unique_ptr &but_iter : block->buttons) { + if (ui_but_is_editable(but_iter.get())) { + but_activate = but_iter.get(); break; } } @@ -812,7 +812,7 @@ void UI_popup_block_template_confirm_op(uiLayout *layout, return nullptr; } uiBlock *block = uiLayoutGetBlock(row); - const uiBut *but_ref = (uiBut *)block->buttons.last; + const uiBut *but_ref = block->last_but(); uiItemFullO_ptr(row, ot, confirm_text, @@ -822,10 +822,10 @@ void UI_popup_block_template_confirm_op(uiLayout *layout, UI_ITEM_NONE, r_ptr); - if (but_ref == block->buttons.last) { + if (block->buttons.is_empty() || but_ref == block->buttons.last().get()) { return nullptr; } - return static_cast(block->buttons.last); + return block->buttons.last().get(); }; auto cancel_fn = [&row, &cancel_text, &show_cancel]() -> uiBut * { diff --git a/source/blender/editors/interface/regions/interface_region_popover.cc b/source/blender/editors/interface/regions/interface_region_popover.cc index 3466dd38c29..04723cd9a4d 100644 --- a/source/blender/editors/interface/regions/interface_region_popover.cc +++ b/source/blender/editors/interface/regions/interface_region_popover.cc @@ -209,12 +209,12 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v if (!handle->refresh) { uiBut *but = nullptr; uiBut *but_first = nullptr; - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { - if ((but_first == nullptr) && ui_but_is_editable(but_iter)) { - but_first = but_iter; + for (const std::unique_ptr &but_iter : block->buttons) { + if ((but_first == nullptr) && ui_but_is_editable(but_iter.get())) { + but_first = but_iter.get(); } if (but_iter->flag & (UI_SELECT | UI_SELECT_DRAW)) { - but = but_iter; + but = but_iter.get(); break; } } diff --git a/source/blender/editors/interface/regions/interface_region_popup.cc b/source/blender/editors/interface/regions/interface_region_popup.cc index 5ba3fb9b086..9f6e2c49f4d 100644 --- a/source/blender/editors/interface/regions/interface_region_popup.cc +++ b/source/blender/editors/interface/regions/interface_region_popup.cc @@ -93,10 +93,10 @@ static void ui_popup_block_position(wmWindow *window, /* Compute block size in window space, based on buttons contained in it. */ if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) { - if (block->buttons.first) { + if (!block->buttons.is_empty()) { BLI_rctf_init_minmax(&block->rect); - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { if (block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT) { bt->rect.xmax += UI_MENU_SUBMENU_PADDING; } @@ -116,7 +116,7 @@ static void ui_popup_block_position(wmWindow *window, const float max_radius = (0.5f * U.widget_unit); if (delta >= 0 && delta < max_radius) { - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { /* Only trim the right most buttons in multi-column popovers. */ if (bt->rect.xmax == block->rect.xmax) { bt->rect.xmax -= delta; @@ -311,13 +311,13 @@ static void ui_popup_block_position(wmWindow *window, } /* Apply offset, buttons in window coords. */ - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { ui_block_to_window_rctf(butregion, but->block, &bt->rect, &bt->rect); BLI_rctf_translate(&bt->rect, offset_x, offset_y); /* ui_but_update recalculates drawstring size in pixels */ - ui_but_update(bt); + ui_but_update(bt.get()); } BLI_rctf_translate(&block->rect, offset_x, offset_y); @@ -503,7 +503,7 @@ static void ui_popup_block_clip(wmWindow *window, uiBlock *block) /* ensure menu items draw inside left/right boundary */ const float xofs = block->rect.xmin - xmin_orig; - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { bt->rect.xmin += xofs; bt->rect.xmax += xofs; } @@ -513,16 +513,16 @@ void ui_popup_block_scrolltest(uiBlock *block) { block->flag &= ~(UI_BLOCK_CLIPBOTTOM | UI_BLOCK_CLIPTOP); - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { bt->flag &= ~UI_SCROLLED; } - if (block->buttons.first == block->buttons.last) { + if (block->buttons.size() < 2) { return; } /* mark buttons that are outside boundary */ - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { if (bt->rect.ymin < block->rect.ymin) { bt->flag |= UI_SCROLLED; block->flag |= UI_BLOCK_CLIPBOTTOM; @@ -534,7 +534,7 @@ void ui_popup_block_scrolltest(uiBlock *block) } /* mark buttons overlapping arrows, if we have them */ - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { if (block->flag & UI_BLOCK_CLIPBOTTOM) { if (bt->rect.ymin < block->rect.ymin + UI_MENU_SCROLL_ARROW) { bt->flag |= UI_SCROLLED; @@ -776,7 +776,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, /* lastly set the buttons at the center of the pie menu, ready for animation */ if (U.pie_animation_timeout > 0) { - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { + for (const std::unique_ptr &but_iter : block->buttons) { if (but_iter->pie_dir != UI_RADIAL_NONE) { BLI_rctf_recenter(&but_iter->rect, UNPACK2(block->pie_data.pie_center_spawned)); } @@ -820,7 +820,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, /* Popups can change size, fix scroll offset if a panel was closed. */ float ymin = FLT_MAX; float ymax = -FLT_MAX; - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { ymin = min_ff(ymin, bt->rect.ymin); ymax = max_ff(ymax, bt->rect.ymax); } @@ -830,7 +830,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, handle->scrolloffset = std::clamp(handle->scrolloffset, scroll_min, scroll_max); /* apply scroll offset */ if (handle->scrolloffset != 0.0f) { - LISTBASE_FOREACH (uiBut *, bt, &block->buttons) { + for (const std::unique_ptr &bt : block->buttons) { bt->rect.ymin += handle->scrolloffset; bt->rect.ymax += handle->scrolloffset; } diff --git a/source/blender/editors/interface/regions/interface_region_search.cc b/source/blender/editors/interface/regions/interface_region_search.cc index d2789ed9a9f..f43862aa0eb 100644 --- a/source/blender/editors/interface/regions/interface_region_search.cc +++ b/source/blender/editors/interface/regions/interface_region_search.cc @@ -315,12 +315,12 @@ static ARegion *wm_searchbox_tooltip_init( *r_exit_on_event = true; LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { if (but->type != UI_BTYPE_SEARCH_MENU) { continue; } - uiButSearch *search_but = (uiButSearch *)but; + uiButSearch *search_but = (uiButSearch *)but.get(); if (!search_but->item_tooltip_fn) { continue; } diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc index 865af45ab6b..147ffe06a4c 100644 --- a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -54,7 +54,7 @@ void template_asset_shelf_popover(uiLayout &layout, ed::asset::shelf::ensure_asset_library_fetched(C, *shelf_type); uiItemPopoverPanel(row, &C, "ASSETSHELF_PT_popover_panel", name, icon); - uiBut *but = static_cast(block->buttons.last); + uiBut *but = block->buttons.last().get(); if (use_preview_icon) { ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); /* Avoid small annoyance where asset shelf popover gets spawned unintentionally on mouse hover, diff --git a/source/blender/editors/interface/templates/interface_template_color_ramp.cc b/source/blender/editors/interface/templates/interface_template_color_ramp.cc index 95ac0cdabc1..d6e0361f3f4 100644 --- a/source/blender/editors/interface/templates/interface_template_color_ramp.cc +++ b/source/blender/editors/interface/templates/interface_template_color_ramp.cc @@ -370,7 +370,8 @@ static void colorband_buttons_layout(uiLayout *layout, } /* Some special (rather awkward) treatment to update UI state on certain property changes. */ - LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + for (int i = block->buttons.size() - 1; i >= 0; i--) { + uiBut *but = block->buttons[i].get(); if (but->rnapoin.data != ptr.data) { continue; } diff --git a/source/blender/editors/interface/templates/interface_template_keymap.cc b/source/blender/editors/interface/templates/interface_template_keymap.cc index 2e8253128fe..238c7b024a3 100644 --- a/source/blender/editors/interface/templates/interface_template_keymap.cc +++ b/source/blender/editors/interface/templates/interface_template_keymap.cc @@ -82,14 +82,18 @@ void uiTemplateKeymapItemProperties(uiLayout *layout, PointerRNA *ptr) PointerRNA propptr = RNA_pointer_get(ptr, "properties"); if (propptr.data) { - uiBut *but = static_cast(uiLayoutGetBlock(layout)->buttons.last); + uiBlock *block = uiLayoutGetBlock(layout); + int i = uiLayoutGetBlock(layout)->buttons.size() - 1; WM_operator_properties_sanitize(&propptr, false); template_keymap_item_properties(layout, nullptr, &propptr); - + if (i < 0) { + return; + } /* attach callbacks to compensate for missing properties update, * we don't know which keymap (item) is being modified there */ - for (; but; but = but->next) { + for (; i < block->buttons.size(); i++) { + uiBut *but = block->buttons[i].get(); /* operator buttons may store props for use (file selector, #36492) */ if (but->rnaprop) { UI_but_func_set(but, keymap_item_modified, ptr->data, nullptr); diff --git a/source/blender/editors/interface/templates/interface_template_operator_property.cc b/source/blender/editors/interface/templates/interface_template_operator_property.cc index c78b3691e45..2989a1c3860 100644 --- a/source/blender/editors/interface/templates/interface_template_operator_property.cc +++ b/source/blender/editors/interface/templates/interface_template_operator_property.cc @@ -182,9 +182,9 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single( if (block->oldblock == nullptr) { const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0; - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const std::unique_ptr &but : block->buttons) { /* no undo for buttons for operator redo panels */ - UI_but_flag_disable(but, UI_BUT_UNDO); + UI_but_flag_disable(but.get(), UI_BUT_UNDO); /* only for popups, see #36109. */ @@ -193,7 +193,7 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single( */ if (is_popup) { if ((but->rnaprop == op->type->prop) && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_NUM)) { - UI_but_focus_on_enter_event(CTX_wm_window(C), but); + UI_but_focus_on_enter_event(CTX_wm_window(C), but.get()); } } } diff --git a/source/blender/editors/interface/templates/interface_template_search_menu.cc b/source/blender/editors/interface/templates/interface_template_search_menu.cc index d9573a9058e..5a8330beafa 100644 --- a/source/blender/editors/interface/templates/interface_template_search_menu.cc +++ b/source/blender/editors/interface/templates/interface_template_search_menu.cc @@ -669,29 +669,30 @@ static MenuSearch_Data *menu_items_from_ui_create(bContext *C, UI_block_end(C, block); - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + for (const int i : block->buttons.index_range()) { + const std::unique_ptr &but = block->buttons[i]; MenuType *mt_from_but = nullptr; /* Support menu titles with dynamic from initial labels * (used by edit-mesh context menu). */ if (but->type == UI_BTYPE_LABEL) { /* Check if the label is the title. */ - uiBut *but_test = but->prev; - while (but_test && but_test->type == UI_BTYPE_SEPR) { - but_test = but_test->prev; + const std::unique_ptr *but_test = block->buttons.begin() + i - 1; + while (but_test >= block->buttons.begin() && (*but_test)->type == UI_BTYPE_SEPR) { + but_test--; } - if (but_test == nullptr) { + if (but_test < block->buttons.begin()) { menu_display_name_map.add(mt, scope.linear_allocator().copy_string(but->drawstr).c_str()); } } else if (menu_items_from_ui_create_item_from_button( - data, scope, mt, but, wm_context, current_menu.self_as_parent)) + data, scope, mt, but.get(), wm_context, current_menu.self_as_parent)) { /* pass */ } - else if ((mt_from_but = UI_but_menutype_get(but))) { + else if ((mt_from_but = UI_but_menutype_get(but.get()))) { const bool uses_context = but->context && bool(mt_from_but->flag & MenuTypeFlag::ContextDependent); const bool tagged_first_time = menu_tagged.add(mt_from_but); @@ -769,7 +770,7 @@ static MenuSearch_Data *menu_items_from_ui_create(bContext *C, * could be used as a more general way to know if poll succeeded, * at this point it's not set - this could be further investigated. */ bool poll_success = true; - if (PanelType *pt = UI_but_paneltype_get(but)) { + if (PanelType *pt = UI_but_paneltype_get(but.get())) { if (pt->poll && (pt->poll(C, pt) == false)) { poll_success = false; } @@ -786,9 +787,9 @@ static MenuSearch_Data *menu_items_from_ui_create(bContext *C, menu_parent->drawstr = scope.linear_allocator().copy_string(but->drawstr); menu_parent->parent = current_menu.self_as_parent; - LISTBASE_FOREACH (uiBut *, sub_but, &sub_block->buttons) { + for (const std::unique_ptr &sub_but : sub_block->buttons) { menu_items_from_ui_create_item_from_button( - data, scope, mt, sub_but, wm_context, menu_parent); + data, scope, mt, sub_but.get(), wm_context, menu_parent); } } diff --git a/source/blender/editors/interface/templates/interface_template_status.cc b/source/blender/editors/interface/templates/interface_template_status.cc index b024714268c..d3faae589eb 100644 --- a/source/blender/editors/interface/templates/interface_template_status.cc +++ b/source/blender/editors/interface/templates/interface_template_status.cc @@ -413,7 +413,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) uiLayoutSetEmboss(row, UI_EMBOSS_NONE); /* This operator also works fine for blocked extensions. */ uiItemO(row, "", ICON_ERROR, "EXTENSIONS_OT_userpref_show_for_update"); - uiBut *but = static_cast(uiLayoutGetBlock(layout)->buttons.last); + uiBut *but = uiLayoutGetBlock(layout)->buttons.last().get(); uchar color[4]; UI_GetThemeColor4ubv(TH_TEXT, color); copy_v4_v4_uchar(but->col, color); @@ -438,7 +438,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) else { uiLayoutSetEmboss(row, UI_EMBOSS_NONE); uiItemO(row, "", ICON_INTERNET_OFFLINE, "EXTENSIONS_OT_userpref_show_online"); - uiBut *but = static_cast(uiLayoutGetBlock(layout)->buttons.last); + uiBut *but = uiLayoutGetBlock(layout)->buttons.last().get(); uchar color[4]; UI_GetThemeColor4ubv(TH_TEXT, color); copy_v4_v4_uchar(but->col, color); @@ -462,7 +462,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) } uiLayoutSetEmboss(row, UI_EMBOSS_NONE); uiItemO(row, "", icon, "EXTENSIONS_OT_userpref_show_for_update"); - uiBut *but = static_cast(uiLayoutGetBlock(layout)->buttons.last); + uiBut *but = uiLayoutGetBlock(layout)->buttons.last().get(); uchar color[4]; UI_GetThemeColor4ubv(TH_TEXT, color); copy_v4_v4_uchar(but->col, color); diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc index 9966b4f637d..8866b83b19a 100644 --- a/source/blender/editors/interface/views/abstract_view_item.cc +++ b/source/blender/editors/interface/views/abstract_view_item.cc @@ -166,12 +166,12 @@ static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) /* A minimal sanity check, can't do much more here. */ BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); - LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { + for (const std::unique_ptr &but : rename_but.block->buttons) { if (but->type != UI_BTYPE_VIEW_ITEM) { continue; } - uiButViewItem *view_item_but = (uiButViewItem *)but; + uiButViewItem *view_item_but = (uiButViewItem *)but.get(); AbstractViewItem *item = reinterpret_cast(view_item_but->view_item); const AbstractView &view = item->get_view(); diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index f6a2f0819fc..386c583e163 100644 --- a/source/blender/editors/interface/views/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -97,11 +97,11 @@ void ViewLink::views_bounds_calc(const uiBlock &block) views_bounds.add(link->view.get(), minmax); } - LISTBASE_FOREACH (uiBut *, but, &block.buttons) { + for (const std::unique_ptr &but : block.buttons) { if (but->type != UI_BTYPE_VIEW_ITEM) { continue; } - uiButViewItem *view_item_but = static_cast(but); + uiButViewItem *view_item_but = static_cast(but.get()); if (!view_item_but->view_item) { continue; } @@ -344,11 +344,11 @@ uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( return nullptr; } - LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { + for (const std::unique_ptr &old_but : old_block->buttons) { if (old_but->type != UI_BTYPE_VIEW_ITEM) { continue; } - uiButViewItem *old_item_but = (uiButViewItem *)old_but; + uiButViewItem *old_item_but = (uiButViewItem *)old_but.get(); if (!old_item_but->view_item) { continue; } diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index 8c98c8d4506..25804d0589f 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -240,11 +240,11 @@ void AbstractTreeView::get_hierarchy_lines(const ARegion ®ion, static uiButViewItem *find_first_view_item_but(const uiBlock &block, const AbstractTreeView &view) { - LISTBASE_FOREACH (uiBut *, but, &block.buttons) { + for (const std::unique_ptr &but : block.buttons) { if (but->type != UI_BTYPE_VIEW_ITEM) { continue; } - uiButViewItem *view_item_but = static_cast(but); + uiButViewItem *view_item_but = static_cast(but.get()); if (&view_item_but->view_item->get_view() == &view) { return view_item_but; } diff --git a/source/blender/editors/space_buttons/buttons_texture.cc b/source/blender/editors/space_buttons/buttons_texture.cc index 2e1699f21fe..cd7bca2b2ee 100644 --- a/source/blender/editors/space_buttons/buttons_texture.cc +++ b/source/blender/editors/space_buttons/buttons_texture.cc @@ -483,7 +483,7 @@ static void template_texture_user_menu(bContext *C, uiLayout *layout, void * /*a /* add label per category */ if (!last_category || !STREQ(last_category, user->category)) { uiItemL(layout, IFACE_(user->category), ICON_NONE); - but = static_cast(block->buttons.last); + but = block->buttons.last().get(); but->drawflag = UI_BUT_TEXT_LEFT; } diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 72932d2a723..33efbeb6a2c 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -539,7 +539,7 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) UI_block_layout_set_current(block, column); uiItemL(column, IFACE_(cname), ICON_NODE); - but = (uiBut *)block->buttons.last; + but = block->buttons.last().get(); first = 0; } @@ -628,7 +628,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ if (sock->link) { uiItemL(column, IFACE_("Link"), ICON_NONE); - but = (uiBut *)block->buttons.last; + but = block->buttons.last().get(); but->drawflag = UI_BUT_TEXT_LEFT; but = uiDefBut(block,