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,