32#define UI_TREEVIEW_INDENT short(0.7f * UI_UNIT_X)
47 std::unique_ptr<AbstractTreeViewItem> item)
52 if (
root_ ==
nullptr) {
85 child->foreach_item_recursive(iter_fn,
options);
119 std::optional<rctf> win_rect = item.
get_win_rect(region);
121 hovered_item = &item;
140 for (
const auto &item : parent.
children_) {
141 if (!item->is_filtered_visible()) {
145 count += count_visible_descendants(*item);
151void AbstractTreeView::get_hierarchy_lines(
const ARegion ®ion,
154 Vector<std::pair<int2, int2>> &lines,
155 int &visible_item_index)
const
157 const int scroll_ofs = scroll_value_ ? *scroll_value_ : 0;
158 const int max_visible_row_count = tot_visible_row_count().value_or(
159 std::numeric_limits<int>::max());
161 for (
const auto &item : parent.
children_) {
162 if (!item->is_filtered_visible()) {
166 const int item_index = visible_item_index;
167 visible_item_index++;
169 if (!item->is_collapsible() || item->is_collapsed()) {
176 const int descendant_count = count_visible_descendants(*item);
178 const int first_descendant_index = item_index + 1;
179 const int last_descendant_index = first_descendant_index + descendant_count;
182 const bool line_ends_above_visible = last_descendant_index < scroll_ofs;
183 if (line_ends_above_visible) {
187 const bool line_starts_below_visible = first_descendant_index >
188 (scroll_ofs + long(max_visible_row_count));
190 if (line_starts_below_visible) {
198 const int ymax = std::max(0, first_descendant_index - scroll_ofs) *
padded_item_height();
199 const int ymin = std::min(max_visible_row_count, last_descendant_index - scroll_ofs) *
201 lines.append(std::make_pair(
int2(x, ymax),
int2(x, ymin)));
203 this->get_hierarchy_lines(region, *item, aspect, lines, visible_item_index);
215 return view_item_but;
221void AbstractTreeView::draw_hierarchy_lines(
const ARegion ®ion,
const uiBlock &block)
const
223 const float aspect = (region.v2d.flag &
V2D_IS_INIT) ?
229 if (!first_item_but) {
233 Vector<std::pair<int2, int2>> lines;
235 get_hierarchy_lines(region, *
this, aspect, lines, index);
236 if (lines.is_empty()) {
248 rcti first_item_but_pixel_rect;
250 int2 top_left{first_item_but_pixel_rect.
xmin, first_item_but_pixel_rect.
ymax};
252 for (
const auto &line : lines) {
254 immVertex2f(
pos, top_left.x + line.first.x, top_left.y - line.first.y);
255 immVertex2f(
pos, top_left.x + line.second.x, top_left.y - line.second.y);
265 this->draw_hierarchy_lines(region, block);
268void AbstractTreeView::update_children_from_old(
const AbstractView &old_view)
272 custom_height_ = old_tree_view.custom_height_;
273 scroll_value_ = old_tree_view.scroll_value_;
274 this->update_children_from_old_recursive(*
this, old_tree_view);
277void AbstractTreeView::update_children_from_old_recursive(
const TreeViewOrItem &new_items,
283 for (
const auto &old_item : old_items.
children_) {
284 old_children_by_label.
add(old_item->label_, old_item.get());
287 for (
const auto &new_item : new_items.
children_) {
288 const Span<AbstractTreeViewItem *> possible_old_children = old_children_by_label.
lookup(
291 possible_old_children);
292 if (!matching_old_item) {
296 new_item->update_from_old(*matching_old_item);
299 update_children_from_old_recursive(*new_item, *matching_old_item);
303AbstractTreeViewItem *AbstractTreeView::find_matching_child(
304 const AbstractTreeViewItem &lookup_item,
const Span<AbstractTreeViewItem *> possible_items)
306 for (
auto *iter_item : possible_items) {
307 if (lookup_item.matches(*iter_item)) {
316std::optional<int> AbstractTreeView::tot_visible_row_count()
const
318 if (!custom_height_) {
327bool AbstractTreeView::supports_scrolling()
const
329 return custom_height_ && scroll_value_;
334 if (!supports_scrolling()) {
345 : view_item_(view_item), behavior_(behavior)
365 const int segment_count =
371 const float segment_height = item_height / segment_count;
373 if (event.
xy[1] - win_rect->ymin > (item_height - segment_height)) {
376 if (event.
xy[1] - win_rect->ymin <= segment_height) {
393void AbstractTreeViewItem::tree_row_click_fn(
bContext *C,
void *but_arg1,
void * )
401void AbstractTreeViewItem::add_treerow_button(
uiBlock &block)
422int AbstractTreeViewItem::indent_width()
const
427void AbstractTreeViewItem::add_indent(
uiLayout &row)
const
433 uiDefBut(block,
UI_BTYPE_SEPR, 0,
"", 0, 0, this->indent_width(), 0,
nullptr, 0.0, 0.0,
"");
438 uiDefBut(block,
UI_BTYPE_SEPR, 0,
"", 0, 0,
UI_TREEVIEW_INDENT, 0,
nullptr, 0.0, 0.0,
"");
445void AbstractTreeViewItem::collapse_chevron_click_fn(
bContext *C,
461 hovered_item->toggle_collapsed_from_view(*C);
464 if (hovered_item->has_active_child()) {
465 hovered_item->activate(*C);
469void AbstractTreeViewItem::add_collapse_chevron(
uiBlock &block)
const
492void AbstractTreeViewItem::add_rename_button(
uiLayout &row)
507bool AbstractTreeViewItem::has_active_child()
const
511 if (item.is_active()) {
542 is_open_ = old_tree_item.is_open_;
547 return label_ == other.label_;
592bool AbstractTreeViewItem::set_state_active()
606 "State can't be queried until reconstruction is completed");
608 "Hovered state can't be queried before the tree row is being built");
620 "State can't be queried until reconstruction is completed");
634 if (collapsed == !is_open_) {
638 is_open_ = !collapsed;
645 "Default state should only be set while building the tree");
654 "State can't be queried until reconstruction is completed");
683 if (should_be_collapsed.has_value()) {
693 parent->set_collapsed(
false);
709 parent && other_parent;
710 parent = parent->parent_, other_parent = other_parent->parent_)
712 if (!parent->matches_single(*other_parent)) {
724 bool add_box_ =
true;
769 const std::optional<int> visible_row_count = tree_view.tot_visible_row_count();
776 if (tree_view.scroll_value_ && visible_row_count) {
777 *tree_view.scroll_value_ = std::clamp(
778 *tree_view.scroll_value_, 0, tot_items - *visible_row_count);
781 const int first_visible_index = tree_view.scroll_value_ ? *tree_view.scroll_value_ : 0;
782 const int max_visible_index = visible_row_count ? first_visible_index + *visible_row_count - 1 :
783 std::numeric_limits<int>::max();
787 if ((index >= first_visible_index) && (index <= max_visible_index)) {
794 if (tree_view.custom_height_) {
798 if (!tree_view.scroll_value_) {
799 tree_view.scroll_value_ = std::make_unique<int>(0);
802 if (visible_row_count && (tot_items > *visible_row_count)) {
810 *tree_view.custom_height_,
811 tree_view.scroll_value_.get(),
813 tot_items - *visible_row_count,
828 tree_view.custom_height_.get(),
854 item.add_treerow_button(block_);
862 if (margin_top > 0) {
863 uiDefBut(&block_,
UI_BTYPE_LABEL, 0,
"", 0, 0,
UI_UNIT_X, margin_top,
nullptr, 0, 0,
"");
868 item.add_indent(*row);
869 item.add_collapse_chevron(block_);
872 item.add_rename_button(*row);
898 const std::optional<int> visible_rows = tree_view.tot_visible_row_count();
903 int tot_visible_items = 0;
905 [&tot_visible_items](AbstractTreeViewItem & ) { tot_visible_items++; },
908 if (tot_visible_items >= *visible_rows) {
912 for (
int i = 0; i < (*visible_rows - tot_visible_items); i++) {
913 BasicTreeViewItem &new_item = tree_view.
add_tree_item<BasicTreeViewItem>(
"");
914 new_item.disable_interaction();
920 std::optional<StringRef> search_string,
928 tree_view.
filter(search_string);
930 ensure_min_rows_items(tree_view);
936 builder.add_box_ = add_box;
958void BasicTreeViewItem::on_activate(
bContext &C)
975std::optional<bool> BasicTreeViewItem::should_be_active()
const
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)
#define LISTBASE_FOREACH(type, var, list)
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)
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_blend(eGPUBlend blend)
void GPU_line_width(float width)
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
void UI_but_flag_disable(uiBut *but, int flag)
void uiLayoutSetActive(uiLayout *layout, bool active)
eUIEmbossType UI_block_emboss_get(uiBlock *block)
uiLayout * uiLayoutBox(uiLayout *layout)
@ UI_EMBOSS_NONE_OR_STATUS
uiBut * uiDefButI(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, int *poin, float min, float max, const char *tip)
void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size)
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
void uiItemL(uiLayout *layout, const char *name, int icon)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
const uiStyle * UI_style_get_dpi()
uiBut * uiDefIconBut(uiBlock *block, int type, int retval, int icon, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
int uiLayoutListItemPaddingWidth()
void UI_block_emboss_set(uiBlock *block, eUIEmbossType emboss)
void uiLayoutSetEmboss(uiLayout *layout, eUIEmbossType emboss)
void uiLayoutListItemAddPadding(uiLayout *layout)
uiBut * uiDefIconButI(uiBlock *block, int type, int retval, int icon, int x, int y, short width, short height, int *poin, float min, float max, const char *tip)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
uiLayout * uiLayoutOverlap(uiLayout *layout)
void UI_block_layout_set_current(uiBlock *block, uiLayout *layout)
blender::ui::AbstractViewItem * UI_region_views_find_item_at(const ARegion ®ion, const int xy[2])
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
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
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
friend class AbstractTreeViewItem
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
void set_default_rows(int default_rows)
void activate(bContext &C)
virtual void change_state_delayed()
virtual void update_from_old(const AbstractViewItem &old)
void add_rename_button(uiBlock &block)
virtual bool set_state_active()
AbstractView & get_view() const
AbstractViewItem()=default
uiButViewItem * view_item_but_
uiButViewItem * view_item_button() 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(AbstractTreeView &tree_view, uiLayout &layout, std::optional< StringRef > search_string={}, 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
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_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)
uiButViewItem * ui_block_view_find_matching_view_item_but_in_old_block(const uiBlock &new_block, const blender::ui::AbstractViewItem &new_item)
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
VecBase< int32_t, 2 > int2
blender::ui::AbstractViewItem * view_item
struct wmEvent * eventstate
#define UI_TREEVIEW_INDENT