diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 8e26d6aa939..a2dcc168374 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1020,6 +1020,8 @@ def km_user_interface(_params): ("ui.list_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), # UI views (polls check if there's a UI view under the cursor). ("ui.view_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), + ("ui.view_scroll", {"type": 'WHEELUPMOUSE', "value": 'ANY'}, None), + ("ui.view_scroll", {"type": 'WHEELDOWNMOUSE', "value": 'ANY'}, None), ]) return keymap diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh index aa708723531..dc0140639ae 100644 --- a/source/blender/editors/include/UI_abstract_view.hh +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -47,6 +47,11 @@ namespace blender::ui { class AbstractViewItem; class AbstractViewItemDragController; +enum class ViewScrollDirection { + UP, + DOWN, +}; + class AbstractView { friend class AbstractViewItem; friend struct ::ViewLink; @@ -86,10 +91,13 @@ class AbstractView { */ virtual bool begin_filtering(const bContext &C) const; - virtual void draw_overlays(const ARegion ®ion) const; + virtual void draw_overlays(const ARegion ®ion, const uiBlock &block) const; virtual void foreach_view_item(FunctionRef iter_fn) const = 0; + virtual bool supports_scrolling() const; + virtual void scroll(ViewScrollDirection direction); + /** * Makes \a item valid for display in this view. Behavior is undefined for items not registered * with this. diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 792d4e35643..13bf7df8e61 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -112,29 +112,36 @@ using TreeViewOrItem = TreeViewItemContainer; * \{ */ class AbstractTreeView : public AbstractView, public TreeViewItemContainer { - int min_rows_ = 0; + /* Shared pointer so the pointer can be kept persistent over redraws. The grip button gets a + * pointer to modify the value on resizing, and it uses it to identify the button over redraws.*/ + /* TODO support region zoom. */ + std::shared_ptr custom_height_ = nullptr; + std::shared_ptr scroll_value_ = nullptr; friend class AbstractTreeViewItem; friend class TreeViewBuilder; + friend class TreeViewLayoutBuilder; friend class TreeViewItemDropTarget; public: /* virtual */ ~AbstractTreeView() override = default; - void draw_overlays(const ARegion ®ion) const override; + void draw_overlays(const ARegion ®ion, const uiBlock &block) const override; void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + void scroll(ViewScrollDirection direction) override; + /** * \param xy: The mouse coordinates in window space. */ AbstractTreeViewItem *find_hovered(const ARegion ®ion, const int2 &xy); - /** Visual feature: Define a number of item rows the view will always show at minimum. If there + /** Visual feature: Define a number of item rows the view will show by default. If there * are fewer items, empty dummy items will be added. These contribute to the view bounds, so the * drop target of the view includes them, but they are not interactive (e.g. no mouse-hover * highlight). */ - void set_min_rows(int min_rows); + void set_default_rows(int min_rows); protected: virtual void build_tree() = 0; @@ -146,14 +153,18 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer { const TreeViewOrItem &old_items); static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items); + std::optional tot_visible_row_count() const; - void draw_hierarchy_lines(const ARegion ®ion) const; - void draw_hierarchy_lines_recursive(const ARegion ®ion, - const TreeViewOrItem &parent, - const uint pos, - const float aspect) const; + bool supports_scrolling() const override; - AbstractTreeViewItem *find_last_visible_descendant(const AbstractTreeViewItem &parent) const; + void draw_hierarchy_lines(const ARegion ®ion, const uiBlock &block) const; + void get_hierarchy_lines(const ARegion ®ion, + const TreeViewOrItem &parent, + const float aspect, + Vector> &lines, + int &visible_item_index) const; + + int count_visible_descendants(const AbstractTreeViewItem &parent) const; }; /** \} */ diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index 02971b9371c..e5f0b5a6b06 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -52,6 +52,7 @@ #include "RNA_types.hh" #include "UI_interface.hh" +#include "UI_abstract_view.hh" #include "interface_intern.hh" @@ -2403,18 +2404,23 @@ static void UI_OT_list_start_filter(wmOperatorType *ot) /** \name UI View Start Filter Operator * \{ */ -static bool ui_view_focused_poll(bContext *C) +static AbstractView *get_view_focused(bContext *C) { const wmWindow *win = CTX_wm_window(C); if (!(win && win->eventstate)) { - return false; + return nullptr; } const ARegion *region = CTX_wm_region(C); if (!region) { - return false; + return nullptr; } - const uiViewHandle *view = UI_region_view_find_at(region, win->eventstate->xy, 0); + return reinterpret_cast(UI_region_view_find_at(region, win->eventstate->xy, 0)); +} + +static bool ui_view_focused_poll(bContext *C) +{ + const AbstractView *view = get_view_focused(C); return view != nullptr; } @@ -2495,6 +2501,72 @@ static void UI_OT_view_drop(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UI View Drop Operator + * \{ */ + +static bool ui_view_scroll_poll(bContext *C) +{ + const AbstractView *view = get_view_focused(C); + if (!view) { + return false; + } + + return view->supports_scrolling(); +} + +static int ui_view_scroll_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + int type = event->type; + bool invert_direction = false; + + if (type == MOUSEPAN) { + int dummy_val; + ui_pan_to_scroll(event, &type, &dummy_val); + + /* 'ui_pan_to_scroll' gives the absolute direction. */ + if (event->flag & WM_EVENT_SCROLL_INVERT) { + invert_direction = true; + } + } + + AbstractView *view = get_view_focused(C); + std::optional direction = + [type, invert_direction]() -> std::optional { + switch (type) { + case WHEELUPMOUSE: + return invert_direction ? ViewScrollDirection::DOWN : ViewScrollDirection::UP; + case WHEELDOWNMOUSE: + return invert_direction ? ViewScrollDirection::UP : ViewScrollDirection::DOWN; + default: + return std::nullopt; + } + }(); + if (!direction) { + return OPERATOR_CANCELLED; + } + + BLI_assert(view->supports_scrolling()); + view->scroll(*direction); + + ED_region_tag_redraw(region); + return OPERATOR_FINISHED; +} + +static void UI_OT_view_scroll(wmOperatorType *ot) +{ + ot->name = "View Scroll"; + ot->idname = "UI_OT_view_scroll"; + + ot->invoke = ui_view_scroll_invoke; + ot->poll = ui_view_scroll_poll; + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name UI View Item Rename Operator * @@ -2638,6 +2710,7 @@ void ED_operatortypes_ui() WM_operatortype_append(UI_OT_view_start_filter); WM_operatortype_append(UI_OT_view_drop); + WM_operatortype_append(UI_OT_view_scroll); WM_operatortype_append(UI_OT_view_item_rename); WM_operatortype_append(UI_OT_override_type_set_button); diff --git a/source/blender/editors/interface/interface_template_bone_collection_tree.cc b/source/blender/editors/interface/interface_template_bone_collection_tree.cc index 231dbf4bc05..30d0e8f272d 100644 --- a/source/blender/editors/interface/interface_template_bone_collection_tree.cc +++ b/source/blender/editors/interface/interface_template_bone_collection_tree.cc @@ -473,7 +473,7 @@ void uiTemplateBoneCollectionTree(uiLayout *layout, bContext *C) *block, "Bone Collection Tree View", std::make_unique(*armature)); - tree_view->set_min_rows(3); + tree_view->set_default_rows(3); ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); } diff --git a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc index 4593f3b577e..f293e28b74c 100644 --- a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc +++ b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc @@ -410,7 +410,7 @@ void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C) *block, "Grease Pencil Layer Tree View", std::make_unique(grease_pencil)); - tree_view->set_min_rows(3); + tree_view->set_default_rows(3); ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); } diff --git a/source/blender/editors/interface/interface_template_light_linking.cc b/source/blender/editors/interface/interface_template_light_linking.cc index e7b773676c8..770cc7ca885 100644 --- a/source/blender/editors/interface/interface_template_light_linking.cc +++ b/source/blender/editors/interface/interface_template_light_linking.cc @@ -399,7 +399,7 @@ void uiTemplateLightLinkingCollection(uiLayout *layout, *block, "Light Linking Collection Tree View", std::make_unique(*context_layout, *collection)); - tree_view->set_min_rows(3); + tree_view->set_default_rows(3); blender::ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); } diff --git a/source/blender/editors/interface/interface_template_node_tree_interface.cc b/source/blender/editors/interface/interface_template_node_tree_interface.cc index b9756bd666e..14ec2d3d196 100644 --- a/source/blender/editors/interface/interface_template_node_tree_interface.cc +++ b/source/blender/editors/interface/interface_template_node_tree_interface.cc @@ -517,7 +517,7 @@ void uiTemplateNodeTreeInterface(uiLayout *layout, PointerRNA *ptr) *block, "Node Tree Declaration Tree View", std::make_unique(nodetree, interface)); - tree_view->set_min_rows(3); + tree_view->set_default_rows(3); blender::ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); } diff --git a/source/blender/editors/interface/views/abstract_view.cc b/source/blender/editors/interface/views/abstract_view.cc index c218de8b127..a675a5222b0 100644 --- a/source/blender/editors/interface/views/abstract_view.cc +++ b/source/blender/editors/interface/views/abstract_view.cc @@ -111,11 +111,21 @@ bool AbstractView::begin_filtering(const bContext & /*C*/) const return false; } -void AbstractView::draw_overlays(const ARegion & /*region*/) const +void AbstractView::draw_overlays(const ARegion & /*region*/, const uiBlock & /*block*/) const { /* Nothing by default. */ } +bool AbstractView::supports_scrolling() const +{ + return false; +} + +void AbstractView::scroll(ViewScrollDirection /*direction*/) +{ + BLI_assert_msg(false, "Unsupported for this view type"); +} + /** \} */ /* ---------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index c25560a9e14..ee7aa3a6c0c 100644 --- a/source/blender/editors/interface/views/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -145,7 +145,7 @@ void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l void ui_block_views_draw_overlays(const ARegion *region, const uiBlock *block) { LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { - view_link->view->draw_overlays(*region); + view_link->view->draw_overlays(*region, *block); } } diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index 203cc12a992..b9054e89bba 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -18,6 +18,7 @@ #include "interface_intern.hh" #include "UI_interface.hh" +#include "UI_view2d.hh" #include "WM_api.hh" #include "WM_types.hh" @@ -115,74 +116,119 @@ AbstractTreeViewItem *AbstractTreeView::find_hovered(const ARegion ®ion, cons return hovered_item; } -void AbstractTreeView::set_min_rows(int min_rows) +void AbstractTreeView::set_default_rows(int default_rows) { - min_rows_ = min_rows; + custom_height_ = std::make_unique(default_rows * padded_item_height()); } -AbstractTreeViewItem *AbstractTreeView::find_last_visible_descendant( - const AbstractTreeViewItem &parent) const +int AbstractTreeView::count_visible_descendants(const AbstractTreeViewItem &parent) const { if (parent.is_collapsed()) { - return nullptr; + return 0; + } + int count = 0; + for (const auto &item : parent.children_) { + if (!item->is_filtered_visible()) { + continue; + } + count++; + count += count_visible_descendants(*item); } - AbstractTreeViewItem *last_descendant = parent.children_.last().get(); - while (!last_descendant->children_.is_empty() && !last_descendant->is_collapsed()) { - last_descendant = last_descendant->children_.last().get(); - } - - return last_descendant; + return count; } -void AbstractTreeView::draw_hierarchy_lines_recursive(const ARegion ®ion, - const TreeViewOrItem &parent, - const uint pos, - const float aspect) const + +void AbstractTreeView::get_hierarchy_lines(const ARegion ®ion, + const TreeViewOrItem &parent, + const float aspect, + Vector> &lines, + int &visible_item_index) const { + const int scroll_ofs = scroll_value_ ? *scroll_value_ : 0; + const int max_visible_row_count = tot_visible_row_count().value_or( + std::numeric_limits::max()); + for (const auto &item : parent.children_) { + if (!item->is_filtered_visible()) { + continue; + } + + const int item_index = visible_item_index; + visible_item_index++; + if (!item->is_collapsible() || item->is_collapsed()) { continue; } - draw_hierarchy_lines_recursive(region, *item, pos, aspect); + /* Draw a hierarchy line for the descendants of this item. */ const AbstractTreeViewItem *first_descendant = item->children_.first().get(); - const AbstractTreeViewItem *last_descendant = find_last_visible_descendant(*item); - if (!first_descendant->view_item_but_ || !last_descendant || !last_descendant->view_item_but_) + const int descendant_count = count_visible_descendants(*item); + + const int first_descendant_index = item_index + 1; + const int last_descendant_index = first_descendant_index + descendant_count; + { - return; + const bool line_ends_above_visible = last_descendant_index < scroll_ofs; + if (line_ends_above_visible) { + continue; + } + + const bool line_starts_below_visible = first_descendant_index > + (scroll_ofs + long(max_visible_row_count)); + /* Can return here even, following items won't be in view anymore. */ + if (line_starts_below_visible) { + return; + } } - const uiButViewItem &first_child_but = *first_descendant->view_item_button(); - const uiButViewItem &last_child_but = *last_descendant->view_item_button(); - BLI_assert(first_child_but.block == last_child_but.block); - const uiBlock *block = first_child_but.block; + const int x = ((first_descendant->indent_width() + (5 * UI_SCALE_FAC) - + (0.5f * UI_ICON_SIZE) + U.pixelsize + UI_SCALE_FAC) / + aspect); + const int ymax = std::max(0, first_descendant_index - scroll_ofs) * padded_item_height(); + const int ymin = std::min(max_visible_row_count, last_descendant_index - scroll_ofs) * + padded_item_height(); + lines.append(std::make_pair(int2(x, ymax), int2(x, ymin))); - rcti first_child_rect; - ui_but_to_pixelrect(&first_child_rect, ®ion, block, &first_child_but); - rcti last_child_rect; - ui_but_to_pixelrect(&last_child_rect, ®ion, block, &last_child_but); - - const float x = first_child_rect.xmin + ((first_descendant->indent_width() - - (0.5f * UI_ICON_SIZE) + U.pixelsize + UI_SCALE_FAC) / - aspect); - const int first_child_top = first_child_rect.ymax - (2.0f * UI_SCALE_FAC / aspect); - const int last_child_bottom = last_child_rect.ymin + (4.0f * UI_SCALE_FAC / aspect); - immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, x, first_child_top); - immVertex2f(pos, x, last_child_bottom); - immEnd(); + this->get_hierarchy_lines(region, *item, aspect, lines, visible_item_index); } } -void AbstractTreeView::draw_hierarchy_lines(const ARegion ®ion) const +static uiButViewItem *find_first_view_item_but(const uiBlock &block, const AbstractTreeView &view) +{ + LISTBASE_FOREACH (uiBut *, but, &block.buttons) { + if (but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + uiButViewItem *view_item_but = static_cast(but); + AbstractViewItem *view_item = reinterpret_cast(view_item_but->view_item); + if (&view_item->get_view() == &view) { + return view_item_but; + } + } + return nullptr; +} + +void AbstractTreeView::draw_hierarchy_lines(const ARegion ®ion, const uiBlock &block) const { const float aspect = (region.v2d.flag & V2D_IS_INIT) ? BLI_rctf_size_y(®ion.v2d.cur) / (BLI_rcti_size_y(®ion.v2d.mask) + 1) : 1.0f; + uiButViewItem *first_item_but = find_first_view_item_but(block, *this); + if (!first_item_but) { + return; + } + + Vector> lines; + int index = 0; + get_hierarchy_lines(region, *this, aspect, lines, index); + if (lines.is_empty()) { + return; + } + GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); @@ -190,22 +236,34 @@ void AbstractTreeView::draw_hierarchy_lines(const ARegion ®ion) const GPU_line_width(1.0f / aspect); GPU_blend(GPU_BLEND_ALPHA); - draw_hierarchy_lines_recursive(region, *this, pos, aspect); + + rcti first_item_but_pixel_rect; + ui_but_to_pixelrect(&first_item_but_pixel_rect, ®ion, &block, first_item_but); + int2 top_left{first_item_but_pixel_rect.xmin, first_item_but_pixel_rect.ymax}; + + for (const auto &line : lines) { + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, top_left.x + line.first.x, top_left.y - line.first.y); + immVertex2f(pos, top_left.x + line.second.x, top_left.y - line.second.y); + immEnd(); + } GPU_blend(GPU_BLEND_NONE); immUnbindProgram(); } -void AbstractTreeView::draw_overlays(const ARegion ®ion) const +void AbstractTreeView::draw_overlays(const ARegion ®ion, const uiBlock &block) const { - draw_hierarchy_lines(region); + draw_hierarchy_lines(region, block); } void AbstractTreeView::update_children_from_old(const AbstractView &old_view) { const AbstractTreeView &old_tree_view = dynamic_cast(old_view); - update_children_from_old_recursive(*this, old_tree_view); + custom_height_ = old_tree_view.custom_height_; + scroll_value_ = old_tree_view.scroll_value_; + this->update_children_from_old_recursive(*this, old_tree_view); } void AbstractTreeView::update_children_from_old_recursive(const TreeViewOrItem &new_items, @@ -237,6 +295,31 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child( return nullptr; } +std::optional AbstractTreeView::tot_visible_row_count() const +{ + if (!custom_height_) { + return {}; + } + if (*custom_height_ < UI_UNIT_Y) { + return 1; + } + return round_fl_to_int(float(*custom_height_) / padded_item_height()); +} + +bool AbstractTreeView::supports_scrolling() const +{ + return custom_height_ && scroll_value_; +} + +void AbstractTreeView::scroll(ViewScrollDirection direction) +{ + if (!supports_scrolling()) { + return; + } + /* Scroll value will be sanitized/clamped when drawing. */ + *scroll_value_ += ((direction == ViewScrollDirection::UP) ? -1 : 1); +} + /* ---------------------------------------------------------------------- */ TreeViewItemDropTarget::TreeViewItemDropTarget(AbstractTreeViewItem &view_item, @@ -613,7 +696,7 @@ class TreeViewLayoutBuilder { friend TreeViewBuilder; public: - void build_from_tree(const AbstractTreeView &tree_view); + void build_from_tree(AbstractTreeView &tree_view); void build_row(AbstractTreeViewItem &item) const; uiBlock &block() const; @@ -628,18 +711,99 @@ TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiLayout &layout) : block_(*uiLayou { } -void TreeViewLayoutBuilder::build_from_tree(const AbstractTreeView &tree_view) +static int count_visible_items(AbstractTreeView &tree_view) { - uiLayout &parent_layout = current_layout(); - - uiLayout *box = uiLayoutBox(&parent_layout); - uiLayoutColumn(box, true); - - tree_view.foreach_item([this](AbstractTreeViewItem &item) { build_row(item); }, + int item_count = 0; + tree_view.foreach_item([&](AbstractTreeViewItem &) { item_count++; }, AbstractTreeView::IterOptions::SkipCollapsed | AbstractTreeView::IterOptions::SkipFiltered); + return item_count; +} - UI_block_layout_set_current(&block(), &parent_layout); +void TreeViewLayoutBuilder::build_from_tree(AbstractTreeView &tree_view) +{ + uiLayout &parent_layout = current_layout(); + uiBlock *block = uiLayoutGetBlock(&parent_layout); + + uiLayout *col = nullptr; + if (true) { + uiLayout *box = uiLayoutBox(&parent_layout); + col = uiLayoutColumn(box, true); + } + else { + col = uiLayoutColumn(&parent_layout, true); + } + /* Row for the tree-view and the scroll bar. */ + uiLayout *row = uiLayoutRow(col, false); + + const std::optional visible_row_count = tree_view.tot_visible_row_count(); + const int tot_items = count_visible_items(tree_view); + + /* Column for the tree view. */ + uiLayoutColumn(row, true); + + /* Clamp scroll-value to valid range. */ + if (tree_view.scroll_value_ && visible_row_count) { + *tree_view.scroll_value_ = std::clamp( + *tree_view.scroll_value_, 0, tot_items - *visible_row_count); + } + + const int first_visible_index = tree_view.scroll_value_ ? *tree_view.scroll_value_ : 0; + const int max_visible_index = visible_row_count ? first_visible_index + *visible_row_count - 1 : + std::numeric_limits::max(); + int index = 0; + tree_view.foreach_item( + [&, this](AbstractTreeViewItem &item) { + if ((index >= first_visible_index) && (index <= max_visible_index)) { + this->build_row(item); + } + index++; + }, + AbstractTreeView::IterOptions::SkipCollapsed | AbstractTreeView::IterOptions::SkipFiltered); + + if (tree_view.custom_height_) { + uiLayoutColumn(row, false); + + *tree_view.custom_height_ = visible_row_count.value_or(1) * padded_item_height(); + if (!tree_view.scroll_value_) { + tree_view.scroll_value_ = std::make_unique(0); + } + + if (visible_row_count && (tot_items > *visible_row_count)) { + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + *tree_view.custom_height_, + tree_view.scroll_value_.get(), + 0, + tot_items - *visible_row_count, + *visible_row_count, + 0.0, + ""); + } + + UI_block_layout_set_current(block, col); + uiDefIconButI(block, + UI_BTYPE_GRIP, + 0, + ICON_GRIP, + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y * 0.5f, + tree_view.custom_height_.get(), + 0, + 0, + 0, + 0, + ""); + } + + UI_block_layout_set_current(block, &parent_layout); } void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const @@ -695,16 +859,21 @@ uiLayout &TreeViewLayoutBuilder::current_layout() const void TreeViewBuilder::ensure_min_rows_items(AbstractTreeView &tree_view) { + const std::optional visible_rows = tree_view.tot_visible_row_count(); + if (!visible_rows) { + return; + } + int tot_visible_items = 0; tree_view.foreach_item( [&tot_visible_items](AbstractTreeViewItem & /*item*/) { tot_visible_items++; }, AbstractTreeView::IterOptions::SkipCollapsed | AbstractTreeView::IterOptions::SkipFiltered); - if (tot_visible_items >= tree_view.min_rows_) { + if (tot_visible_items >= *visible_rows) { return; } - for (int i = 0; i < (tree_view.min_rows_ - tot_visible_items); i++) { + for (int i = 0; i < (*visible_rows - tot_visible_items); i++) { BasicTreeViewItem &new_item = tree_view.add_tree_item(""); new_item.disable_interaction(); }