36#define UI_TREEVIEW_INDENT short(0.7f * UI_UNIT_X)
52 std::unique_ptr<AbstractTreeViewItem> item)
57 if (
root_ ==
nullptr) {
90 child->foreach_item_recursive(iter_fn,
options);
131 std::optional<rctf> win_rect = item.
get_win_rect(region);
133 hovered_item = &item;
144 "Default value is smaller than the minimum rows. Limit is required to prevent "
145 "resizing below specific height.");
156 if (!custom_height_ && !scroll_value_) {
160 if (custom_height_) {
164 state.scroll_offset = *scroll_value_;
172 if (
state.custom_height) {
176 if (
state.scroll_offset) {
177 scroll_value_ = std::make_shared<int>(
state.scroll_offset);
190 for (
const auto &item : parent.
children_) {
191 if (!item->is_filtered_visible()) {
195 count += count_visible_descendants(*item);
201void AbstractTreeView::get_hierarchy_lines(
const ARegion ®ion,
204 Vector<std::pair<int2, int2>> &lines,
205 int &visible_item_index)
const
207 const int scroll_ofs = scroll_value_ ? *scroll_value_ : 0;
208 const int max_visible_row_count = tot_visible_row_count().value_or(
209 std::numeric_limits<int>::max());
211 for (
const auto &item : parent.children_) {
212 if (!item->is_filtered_visible()) {
216 const int item_index = visible_item_index;
217 visible_item_index++;
219 if (!item->is_collapsible() || item->is_collapsed()) {
222 if (item->children_.is_empty()) {
230 const int descendant_count = count_visible_descendants(*item);
232 const int first_descendant_index = item_index + 1;
233 const int last_descendant_index = item_index + descendant_count;
236 const bool line_ends_above_visible = last_descendant_index < scroll_ofs;
237 if (line_ends_above_visible) {
240 visible_item_index = last_descendant_index + 1;
244 const bool line_starts_below_visible = first_descendant_index >
245 (scroll_ofs +
long(max_visible_row_count));
247 if (line_starts_below_visible) {
255 const int ymax = std::max(0, first_descendant_index - scroll_ofs) *
padded_item_height() /
257 const int ymin = std::min(max_visible_row_count, last_descendant_index + 1 - scroll_ofs) *
259 lines.append(std::make_pair(
int2(
x, ymax),
int2(
x, ymin)));
261 this->get_hierarchy_lines(region, *item, aspect, lines, visible_item_index);
267 for (
const std::unique_ptr<uiBut> &but : block.
buttons) {
273 return view_item_but;
279void AbstractTreeView::draw_hierarchy_lines(
const ARegion ®ion,
const uiBlock &block)
const
287 if (!first_item_but) {
293 get_hierarchy_lines(region, *
this, aspect, lines, index);
306 rcti first_item_but_pixel_rect;
308 int2 top_left{first_item_but_pixel_rect.
xmin, first_item_but_pixel_rect.
ymax};
310 for (
const auto &line : lines) {
323 this->draw_hierarchy_lines(region, block);
326void AbstractTreeView::update_children_from_old(
const AbstractView &old_view)
330 custom_height_ = old_tree_view.custom_height_;
331 scroll_value_ = old_tree_view.scroll_value_;
332 search_string_ = old_tree_view.search_string_;
333 show_display_options_ = old_tree_view.show_display_options_;
334 update_children_from_old_recursive(*
this, old_tree_view);
337void AbstractTreeView::update_children_from_old_recursive(
const TreeViewOrItem &new_items,
343 for (
const auto &old_item : old_items.children_) {
344 old_children_by_label.
add(old_item->label_, old_item.get());
347 for (
const auto &new_item : new_items.children_) {
351 possible_old_children);
352 if (!matching_old_item) {
356 new_item->update_from_old(*matching_old_item);
359 update_children_from_old_recursive(*new_item, *matching_old_item);
366 for (
auto *iter_item : possible_items) {
367 if (lookup_item.matches(*iter_item)) {
376std::optional<int> AbstractTreeView::tot_visible_row_count()
const
378 if (!custom_height_) {
388 return custom_height_ && scroll_value_;
393 return this->tot_visible_row_count().value_or(0) >= last_tot_items_;
405void AbstractTreeView::scroll_active_into_view()
408 const std::optional<int> visible_row_count = tot_visible_row_count();
410 if (!custom_height_) {
414 if (!visible_row_count.has_value()) {
418 if (scroll_active_into_view_on_draw_) {
419 if (!scroll_value_) {
420 scroll_value_ = std::make_unique<int>(0);
424 if (item.is_active_) {
425 *scroll_value_ = std::max(0, index - *visible_row_count + 1);
450 std::optional<rctf> win_rect =
view_item_.get_win_rect(region);
459 const int segment_count =
465 const float segment_height = item_height / segment_count;
467 if (event.
xy[1] < win_rect->ymin) {
470 if (event.
xy[1] - win_rect->ymin > (item_height - segment_height)) {
473 if (event.
xy[1] - win_rect->ymin <= segment_height) {
490void AbstractTreeViewItem::add_treerow_button(
uiBlock &block)
510int AbstractTreeViewItem::indent_width()
const
515void AbstractTreeViewItem::add_indent(
uiLayout &row)
const
521 uiDefBut(block,
ButType::Sepr, 0,
"", 0, 0, this->indent_width(), 0,
nullptr, 0.0, 0.0,
"");
527 uiDefBut(block,
ButType::Sepr, 0,
"", 0, 0,
UI_TREEVIEW_INDENT, 0,
nullptr, 0.0, 0.0,
"");
534void AbstractTreeViewItem::collapse_chevron_click_fn(bContext *
C,
550 hovered_item->toggle_collapsed_from_view(*
C);
553 if (hovered_item->has_active_child()) {
554 hovered_item->activate(*
C);
558void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block)
const
566 &block,
ButType::ButToggle, 0, icon, 0, 0,
UI_TREEVIEW_INDENT,
UI_UNIT_Y,
nullptr, 0, 0,
"");
571void AbstractTreeViewItem::add_rename_button(uiLayout &row)
573 uiBlock *block = row.
block();
586bool AbstractTreeViewItem::has_active_child()
const
590 if (item.is_active()) {
621 is_open_ = old_tree_item.is_open_;
685 "State cannot be queried until reconstruction is completed");
687 "Hovered state cannot be queried before the tree row is being built");
699 "State cannot be queried until reconstruction is completed");
720 if (collapsed == !is_open_) {
724 is_open_ = !collapsed;
741 "Default state should only be set while building the tree");
750 "State can't be queried until reconstruction is completed");
762 const bool prev_active_state =
is_active();
766 this->
get_tree_view().scroll_active_into_view_on_draw_ =
true;
770 if (should_be_collapsed.has_value()) {
780 parent->set_collapsed(
false);
796 parent && other_parent;
799 if (!parent->matches_single(*other_parent)) {
821class TreeViewLayoutBuilder {
823 bool add_box_ =
true;
825 friend TreeViewBuilder;
836 TreeViewLayoutBuilder(
uiLayout &layout);
839TreeViewLayoutBuilder::TreeViewLayoutBuilder(
uiLayout &layout) : block_(*layout.block()) {}
866 const std::optional<int> visible_row_count = tree_view.tot_visible_row_count();
868 tree_view.last_tot_items_ = tot_items;
873 if (tree_view.scroll_active_into_view_on_draw_) {
874 tree_view.scroll_active_into_view();
878 if (tree_view.scroll_value_ && visible_row_count) {
879 *tree_view.scroll_value_ = std::clamp(
880 *tree_view.scroll_value_, 0, tot_items - *visible_row_count);
883 const int first_visible_index = tree_view.scroll_value_ ? *tree_view.scroll_value_ : 0;
884 const int max_visible_index = visible_row_count ? first_visible_index + *visible_row_count - 1 :
885 std::numeric_limits<int>::max();
889 if ((index >= first_visible_index) && (index <= max_visible_index)) {
898 if (tree_view.custom_height_) {
901 if (!tree_view.scroll_value_) {
902 tree_view.scroll_value_ = std::make_unique<int>(0);
905 if (visible_row_count && (tot_items > *visible_row_count)) {
914 *tree_view.custom_height_,
915 tree_view.scroll_value_.get(),
917 tot_items - *visible_row_count,
929 int icon = *tree_view.show_display_options_ ? ICON_DISCLOSURE_TRI_DOWN :
930 ICON_DISCLOSURE_TRI_RIGHT;
940 tree_view.show_display_options_.get(),
956 tree_view.custom_height_.get(),
961 if (*tree_view.show_display_options_) {
971 tree_view.search_string_.get(),
990 const int width = prev_layout.
width();
1007 item.add_treerow_button(block_);
1015 if (margin_top > 0) {
1016 uiDefBut(&block_,
ButType::Label, 0,
"", 0, 0,
UI_UNIT_X, margin_top,
nullptr, 0, 0,
"");
1018 row = &content_col->
row(
true);
1021 item.add_indent(*row);
1022 item.add_collapse_chevron(block_);
1025 item.add_rename_button(*row);
1054 const std::optional<int> visible_rows = tree_view.tot_visible_row_count();
1055 if (!visible_rows) {
1059 int tot_visible_items = 0;
1061 [&tot_visible_items](AbstractTreeViewItem & ) { tot_visible_items++; },
1064 if (tot_visible_items >= *visible_rows) {
1068 for (
int i = 0;
i < (*visible_rows - tot_visible_items);
i++) {
1069 BasicTreeViewItem &new_item = tree_view.
add_tree_item<BasicTreeViewItem>(
"");
1070 new_item.disable_interaction();
1093 tree_view.
filter(tree_view.search_string_ ? std::optional{string} : std::nullopt);
1095 ensure_min_rows_items(tree_view);
1101 builder.add_box_ = add_box;
1125void BasicTreeViewItem::on_activate(
bContext &
C)
1147 return std::nullopt;
ARegion * CTX_wm_region_popup(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define BLI_assert_unreachable()
#define BLI_assert_msg(a, msg)
MINLINE int round_fl_to_int(float a)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
bool BLI_rctf_isect_y(const rctf *rect, float y)
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
char char * BLI_strncpy_ensure_pad(char *__restrict dst, const char *__restrict src, char pad, size_t dst_maxncpy) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define SET_FLAG_FROM_TEST(value, test, flag)
@ UI_VIEW_SHOW_FILTER_OPTIONS
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_line_width(float width)
void GPU_blend(GPUBlend blend)
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
uiBut * uiDefButI(uiBlock *block, ButType type, int retval, blender::StringRef str, int x, int y, short width, short height, int *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_disable(uiBut *but, int flag)
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
uiBut * uiDefIconBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
const uiStyle * UI_style_get_dpi()
void UI_block_flag_disable(uiBlock *block, int flag)
uiBut * uiDefIconButBitC(uiBlock *block, ButType type, int bit, int retval, int icon, int x, int y, short width, short height, char *poin, float min, float max, std::optional< blender::StringRef > tip)
uiBut * uiDefIconButI(uiBlock *block, ButType type, int retval, int icon, int x, int y, short width, short height, int *poin, float min, float max, std::optional< blender::StringRef > tip)
blender::ui::EmbossType UI_block_emboss_get(uiBlock *block)
void UI_block_flag_enable(uiBlock *block, int flag)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_enable(uiBut *but, int flag)
blender::ui::AbstractViewItem * UI_region_views_find_item_at(const ARegion ®ion, const int xy[2])
int uiLayoutListItemPaddingWidth()
void uiLayoutListItemAddPadding(uiLayout *layout)
Span< Value > lookup(const Key &key) const
void add(const Key &key, const Value &value)
constexpr bool is_empty() const
Abstract base class for defining a customizable tree-view item.
std::optional< rctf > get_win_rect(const ARegion ®ion) const
void update_from_old(const AbstractViewItem &old) override
bool is_collapsed() const
void change_state_delayed() override
void on_filter_change() override
int count_parents() const
AbstractTreeView & get_tree_view() const
std::unique_ptr< DropTargetInterface > create_item_drop_target() final
bool is_collapsible() const
StringRef get_rename_string() const override
virtual void build_row(uiLayout &row)=0
bool matches(const AbstractViewItem &other) const override
void toggle_collapsed_from_view(bContext &C)
virtual std::unique_ptr< TreeViewItemDropTarget > create_drop_target()
virtual void on_collapse_change(bContext &C, bool is_collapsed)
virtual std::optional< bool > should_be_collapsed() const
virtual bool set_collapsed(bool collapsed)
virtual bool matches_single(const AbstractTreeViewItem &other) const
std::optional< std::string > debug_name() const override
friend class AbstractTreeView
virtual bool supports_collapsing() const
void uncollapse_by_default()
bool rename(const bContext &C, StringRefNull new_name) override
void ensure_parents_uncollapsed()
AbstractTreeViewItem * find_hovered(const ARegion ®ion, const int2 &xy)
void scroll(ViewScrollDirection direction) override
void foreach_root_item(ItemIterFn iter_fn) const
friend class AbstractTreeViewItem
bool is_fully_visible() const override
void persistent_state_apply(const uiViewState &state) override
void draw_overlays(const ARegion ®ion, const uiBlock &block) const override
virtual void build_tree()=0
void foreach_item(ItemIterFn iter_fn, IterOptions options=IterOptions::None) const
std::optional< uiViewState > persistent_state() const override
void set_default_rows(int default_rows)
virtual void change_state_delayed()
virtual void update_from_old(const AbstractViewItem &old)
void add_rename_button(uiBlock &block)
bool is_always_collapsible_
virtual bool set_state_active()
AbstractView & get_view() const
AbstractViewItem()=default
bool is_filtered_visible() const
uiButViewItem * view_item_but_
uiButViewItem * view_item_button() const
virtual std::optional< bool > should_be_active() const
bool is_filtered_visible_
virtual bool supports_scrolling() const
void update_from_old(uiBlock &new_block)
virtual void change_state_delayed()
void register_item(AbstractViewItem &item)
void filter(std::optional< StringRef > filter_str)
BasicTreeViewItem(StringRef label, BIFIconID icon=ICON_NONE)
void build_row(uiLayout &row) override
std::function< bool()> IsActiveFn
std::function< void(bContext &C, BasicTreeViewItem &new_active)> ActivateFn
void add_label(uiLayout &layout, StringRefNull label_override="")
void set_is_active_fn(IsActiveFn is_active_fn)
void set_on_activate_fn(ActivateFn fn)
static void build_tree_view(const bContext &C, AbstractTreeView &tree_view, uiLayout &layout, bool add_box=true)
ItemT & add_tree_item(Args &&...args)
friend class AbstractTreeViewItem
AbstractTreeViewItem * parent_
TreeViewItemContainer * root_
void foreach_item_recursive(ItemIterFn iter_fn, IterOptions options=IterOptions::None) const
Vector< std::unique_ptr< AbstractTreeViewItem > > children_
void foreach_parent(ItemIterFn iter_fn) const
friend class AbstractTreeView
FunctionRef< void(AbstractTreeViewItem &)> ItemIterFn
AbstractTreeViewItem & view_item_
TreeViewItemDropTarget(AbstractTreeViewItem &view_item, DropBehavior behavior=DropBehavior::Insert)
const DropBehavior behavior_
std::optional< DropLocation > choose_drop_location(const ARegion ®ion, const wmEvent &event) const override
void build_from_tree(AbstractTreeView &tree_view)
uiLayout & current_layout() const
void build_row(AbstractTreeViewItem &item) const
CCL_NAMESPACE_BEGIN struct Options options
void ui_def_but_icon(uiBut *but, const int icon, const int flag)
void ui_but_to_pixelrect(rcti *rect, const ARegion *region, const uiBlock *block, const uiBut *but)
void ui_block_to_window_rctf(const ARegion *region, const uiBlock *block, rctf *rct_dst, const rctf *rct_src)
void ui_block_view_persistent_state_restore(const ARegion ®ion, const uiBlock &block, blender::ui::AbstractView &view)
uiButViewItem * ui_block_view_find_matching_view_item_but_in_old_block(const uiBlock &new_block, const blender::ui::AbstractViewItem &new_item)
void ui_layout_list_set_labels_active(uiLayout *layout)
T max(const T &a, const T &b)
static int unpadded_item_height()
static int count_visible_items(AbstractTreeView &tree_view)
static uiButViewItem * find_first_view_item_but(const uiBlock &block, const AbstractTreeView &view)
static int padded_item_height()
TreeViewItemContainer TreeViewOrItem
void block_layout_set_current(uiBlock *block, uiLayout *layout)
VecBase< int32_t, 2 > int2
blender::Vector< std::unique_ptr< uiBut > > buttons
blender::ui::AbstractViewItem * view_item
void fixed_size_set(bool fixed_size)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void active_set(bool active)
uiLayout & row(bool align)
void emboss_set(blender::ui::EmbossType emboss)
struct wmEvent * eventstate
#define UI_TREEVIEW_INDENT