2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2021-09-23 18:56:29 +02:00
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
|
* \ingroup edinterface
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "DNA_userdef_types.h"
|
2021-10-07 14:59:43 +02:00
|
|
|
#include "DNA_windowmanager_types.h"
|
2021-09-23 18:56:29 +02:00
|
|
|
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_context.hh"
|
2021-10-06 14:18:12 +02:00
|
|
|
|
2024-02-09 18:59:42 +01:00
|
|
|
#include "BLT_translation.hh"
|
2021-09-30 16:26:56 +02:00
|
|
|
|
2024-03-23 01:24:18 +01:00
|
|
|
#include "GPU_immediate.hh"
|
2023-07-06 17:00:44 +02:00
|
|
|
|
2022-11-26 00:21:17 -06:00
|
|
|
#include "interface_intern.hh"
|
2021-09-23 18:56:29 +02:00
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "UI_interface.hh"
|
2024-10-02 11:19:49 +02:00
|
|
|
#include "UI_view2d.hh"
|
2021-09-23 18:56:29 +02:00
|
|
|
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "WM_api.hh"
|
|
|
|
|
#include "WM_types.hh"
|
2021-10-07 14:59:43 +02:00
|
|
|
|
2024-09-12 18:12:22 +02:00
|
|
|
#include "BLI_multi_value_map.hh"
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
#include "UI_tree_view.hh"
|
|
|
|
|
|
|
|
|
|
namespace blender::ui {
|
|
|
|
|
|
2024-02-02 19:40:28 +01:00
|
|
|
#define UI_TREEVIEW_INDENT short(0.7f * UI_UNIT_X)
|
|
|
|
|
|
2023-09-22 17:15:12 +02:00
|
|
|
static int unpadded_item_height()
|
|
|
|
|
{
|
|
|
|
|
return UI_UNIT_Y;
|
|
|
|
|
}
|
|
|
|
|
static int padded_item_height()
|
|
|
|
|
{
|
|
|
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
|
|
|
return unpadded_item_height() + style->buttonspacey;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
AbstractTreeViewItem &TreeViewItemContainer::add_tree_item(
|
|
|
|
|
std::unique_ptr<AbstractTreeViewItem> item)
|
|
|
|
|
{
|
|
|
|
|
children_.append(std::move(item));
|
|
|
|
|
|
|
|
|
|
/* The first item that will be added to the root sets this. */
|
|
|
|
|
if (root_ == nullptr) {
|
|
|
|
|
root_ = this;
|
|
|
|
|
}
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
AbstractTreeView &tree_view = static_cast<AbstractTreeView &>(*root_);
|
2021-09-23 18:56:29 +02:00
|
|
|
AbstractTreeViewItem &added_item = *children_.last();
|
|
|
|
|
added_item.root_ = root_;
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
tree_view.register_item(added_item);
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
if (root_ != this) {
|
|
|
|
|
/* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely
|
|
|
|
|
* nice to static_cast this, but well... */
|
|
|
|
|
added_item.parent_ = static_cast<AbstractTreeViewItem *>(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return added_item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const
|
|
|
|
|
{
|
2021-09-24 10:25:16 +02:00
|
|
|
for (const auto &child : children_) {
|
2023-06-12 11:41:00 +02:00
|
|
|
bool skip = false;
|
2024-07-01 20:21:25 +02:00
|
|
|
if (bool(options & IterOptions::SkipFiltered) && !child->is_filtered_visible()) {
|
2023-06-12 11:41:00 +02:00
|
|
|
skip = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!skip) {
|
|
|
|
|
iter_fn(*child);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
child->foreach_item_recursive(iter_fn, options);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-29 20:42:08 +02:00
|
|
|
void TreeViewItemContainer::foreach_parent(ItemIterFn iter_fn) const
|
|
|
|
|
{
|
|
|
|
|
for (ui::AbstractTreeViewItem *item = parent_; item; item = item->parent_) {
|
|
|
|
|
iter_fn(*item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2023-07-26 16:33:28 +02:00
|
|
|
/* Implementation for the base class virtual function. More specialized iterators below. */
|
|
|
|
|
void AbstractTreeView::foreach_view_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const
|
|
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
this->foreach_item_recursive(iter_fn);
|
2023-07-26 16:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) const
|
|
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
this->foreach_item_recursive(iter_fn, options);
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
AbstractTreeViewItem *AbstractTreeView::find_hovered(const ARegion ®ion, const int2 &xy)
|
|
|
|
|
{
|
|
|
|
|
AbstractTreeViewItem *hovered_item = nullptr;
|
2024-03-07 14:45:33 -05:00
|
|
|
this->foreach_item_recursive(
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
[&](AbstractTreeViewItem &item) {
|
|
|
|
|
if (hovered_item) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<rctf> win_rect = item.get_win_rect(region);
|
|
|
|
|
if (win_rect && BLI_rctf_isect_y(&*win_rect, xy[1])) {
|
|
|
|
|
hovered_item = &item;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
IterOptions::SkipCollapsed | IterOptions::SkipFiltered);
|
|
|
|
|
|
|
|
|
|
return hovered_item;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
void AbstractTreeView::set_default_rows(int default_rows)
|
2023-03-28 12:33:35 +02:00
|
|
|
{
|
2024-10-02 11:19:49 +02:00
|
|
|
custom_height_ = std::make_unique<int>(default_rows * padded_item_height());
|
2023-03-28 12:33:35 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
int AbstractTreeView::count_visible_descendants(const AbstractTreeViewItem &parent) const
|
2023-07-06 17:00:44 +02:00
|
|
|
{
|
|
|
|
|
if (parent.is_collapsed()) {
|
2024-10-02 11:19:49 +02:00
|
|
|
return 0;
|
2023-07-06 17:00:44 +02:00
|
|
|
}
|
2024-10-02 11:19:49 +02:00
|
|
|
int count = 0;
|
|
|
|
|
for (const auto &item : parent.children_) {
|
|
|
|
|
if (!item->is_filtered_visible()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
count++;
|
|
|
|
|
count += count_visible_descendants(*item);
|
2023-07-06 17:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
return count;
|
2023-07-06 17:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
void AbstractTreeView::get_hierarchy_lines(const ARegion ®ion,
|
|
|
|
|
const TreeViewOrItem &parent,
|
|
|
|
|
const float aspect,
|
|
|
|
|
Vector<std::pair<int2, int2>> &lines,
|
|
|
|
|
int &visible_item_index) const
|
2023-07-06 17:00:44 +02:00
|
|
|
{
|
2024-10-02 11:19:49 +02:00
|
|
|
const int scroll_ofs = scroll_value_ ? *scroll_value_ : 0;
|
|
|
|
|
const int max_visible_row_count = tot_visible_row_count().value_or(
|
|
|
|
|
std::numeric_limits<int>::max());
|
|
|
|
|
|
2023-07-06 17:00:44 +02:00
|
|
|
for (const auto &item : parent.children_) {
|
2024-10-02 11:19:49 +02:00
|
|
|
if (!item->is_filtered_visible()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int item_index = visible_item_index;
|
|
|
|
|
visible_item_index++;
|
|
|
|
|
|
2023-07-06 17:00:44 +02:00
|
|
|
if (!item->is_collapsible() || item->is_collapsed()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
/* Draw a hierarchy line for the descendants of this item. */
|
2023-07-06 17:00:44 +02:00
|
|
|
|
|
|
|
|
const AbstractTreeViewItem *first_descendant = item->children_.first().get();
|
2024-10-02 11:19:49 +02:00
|
|
|
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;
|
|
|
|
|
|
2023-07-06 17:00:44 +02:00
|
|
|
{
|
2024-10-02 11:19:49 +02:00
|
|
|
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 int x = ((first_descendant->indent_width() + uiLayoutListItemPaddingWidth() -
|
|
|
|
|
(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)));
|
|
|
|
|
|
|
|
|
|
this->get_hierarchy_lines(region, *item, aspect, lines, visible_item_index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<uiButViewItem *>(but);
|
|
|
|
|
if (&view_item_but->view_item->get_view() == &view) {
|
|
|
|
|
return view_item_but;
|
2023-07-06 17:00:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-10-02 11:19:49 +02:00
|
|
|
return nullptr;
|
2023-07-06 17:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
void AbstractTreeView::draw_hierarchy_lines(const ARegion ®ion, const uiBlock &block) const
|
2023-07-06 17:00:44 +02:00
|
|
|
{
|
2024-02-08 00:02:11 +01:00
|
|
|
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;
|
2024-01-23 20:39:15 +01:00
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
uiButViewItem *first_item_but = find_first_view_item_but(block, *this);
|
|
|
|
|
if (!first_item_but) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<std::pair<int2, int2>> lines;
|
|
|
|
|
int index = 0;
|
|
|
|
|
get_hierarchy_lines(region, *this, aspect, lines, index);
|
|
|
|
|
if (lines.is_empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 17:00:44 +02:00
|
|
|
GPUVertFormat *format = immVertexFormat();
|
|
|
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
2024-02-02 19:40:28 +01:00
|
|
|
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
|
|
|
|
immUniformThemeColorAlpha(TH_TEXT, 0.2f);
|
2023-07-06 17:00:44 +02:00
|
|
|
|
2024-01-23 20:39:15 +01:00
|
|
|
GPU_line_width(1.0f / aspect);
|
2023-07-06 17:00:44 +02:00
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
2024-10-02 11:19:49 +02:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
2023-07-06 17:00:44 +02:00
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
|
|
|
|
|
|
|
|
immUnbindProgram();
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
void AbstractTreeView::draw_overlays(const ARegion ®ion, const uiBlock &block) const
|
2023-07-06 17:00:44 +02:00
|
|
|
{
|
2024-10-02 11:19:49 +02:00
|
|
|
this->draw_hierarchy_lines(region, block);
|
2023-07-06 17:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
UI: Add AbstractView base class for views, unify reconstruction in there
No user visible changes expected.
There's plenty of duplicated code in the grid and the tree view, and I expect
this to become more. This starts the process of unifying these parts, which
should also make it easier to add new views. Complexity in the view classes is
reduced, and some type shenanigans for C compatibility and general view
management can be removed, since there is now a common base type.
For the start this ports some of the view reconstruction, where the view and
its items are compared to the version of itself in the previous redraw, so that
state (highlighted, active, renaming, collapsed, ...) can be preserved.
Notifier listening is also ported.
2022-07-02 21:49:21 +02:00
|
|
|
void AbstractTreeView::update_children_from_old(const AbstractView &old_view)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2022-07-02 22:36:50 +02:00
|
|
|
const AbstractTreeView &old_tree_view = dynamic_cast<const AbstractTreeView &>(old_view);
|
2021-10-05 16:01:01 +02:00
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
custom_height_ = old_tree_view.custom_height_;
|
|
|
|
|
scroll_value_ = old_tree_view.scroll_value_;
|
2024-03-07 14:45:33 -05:00
|
|
|
this->update_children_from_old_recursive(*this, old_tree_view);
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-09 12:07:34 +01:00
|
|
|
void AbstractTreeView::update_children_from_old_recursive(const TreeViewOrItem &new_items,
|
|
|
|
|
const TreeViewOrItem &old_items)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2024-09-12 18:12:22 +02:00
|
|
|
/* This map can't find the exact old item for a new item. However, it can drastically reduce the
|
|
|
|
|
* number of items that need to be checked. */
|
|
|
|
|
MultiValueMap<StringRef, AbstractTreeViewItem *> old_children_by_label;
|
|
|
|
|
for (const auto &old_item : old_items.children_) {
|
|
|
|
|
old_children_by_label.add(old_item->label_, old_item.get());
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
for (const auto &new_item : new_items.children_) {
|
2024-09-12 18:12:22 +02:00
|
|
|
const Span<AbstractTreeViewItem *> possible_old_children = old_children_by_label.lookup(
|
|
|
|
|
new_item->label_);
|
|
|
|
|
AbstractTreeViewItem *matching_old_item = find_matching_child(*new_item,
|
|
|
|
|
possible_old_children);
|
2021-09-23 18:56:29 +02:00
|
|
|
if (!matching_old_item) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_item->update_from_old(*matching_old_item);
|
|
|
|
|
|
|
|
|
|
/* Recurse into children of the matched item. */
|
|
|
|
|
update_children_from_old_recursive(*new_item, *matching_old_item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AbstractTreeViewItem *AbstractTreeView::find_matching_child(
|
2024-09-12 18:12:22 +02:00
|
|
|
const AbstractTreeViewItem &lookup_item, const Span<AbstractTreeViewItem *> possible_items)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2024-09-12 18:12:22 +02:00
|
|
|
for (auto *iter_item : possible_items) {
|
2023-06-16 12:28:02 +02:00
|
|
|
if (lookup_item.matches(*iter_item)) {
|
2021-09-23 18:56:29 +02:00
|
|
|
/* We have a matching item! */
|
2024-09-12 18:12:22 +02:00
|
|
|
return iter_item;
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
std::optional<int> 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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2023-10-12 17:06:15 +02:00
|
|
|
TreeViewItemDropTarget::TreeViewItemDropTarget(AbstractTreeViewItem &view_item,
|
|
|
|
|
DropBehavior behavior)
|
|
|
|
|
: view_item_(view_item), behavior_(behavior)
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<DropLocation> TreeViewItemDropTarget::choose_drop_location(
|
|
|
|
|
const ARegion ®ion, const wmEvent &event) const
|
|
|
|
|
{
|
|
|
|
|
if (behavior_ == DropBehavior::Insert) {
|
|
|
|
|
return DropLocation::Into;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-12 17:06:15 +02:00
|
|
|
std::optional<rctf> win_rect = view_item_.get_win_rect(region);
|
|
|
|
|
if (!win_rect) {
|
|
|
|
|
BLI_assert_unreachable();
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
const float item_height = BLI_rctf_size_y(&*win_rect);
|
|
|
|
|
|
|
|
|
|
BLI_assert(ELEM(behavior_, DropBehavior::Reorder, DropBehavior::ReorderAndInsert));
|
|
|
|
|
|
|
|
|
|
const int segment_count =
|
|
|
|
|
(behavior_ == DropBehavior::Reorder) ?
|
|
|
|
|
/* Divide into upper (insert before) and lower (insert after) half. */
|
|
|
|
|
2 :
|
|
|
|
|
/* Upper (insert before), middle (insert into) and lower (insert after) third. */
|
|
|
|
|
3;
|
|
|
|
|
const float segment_height = item_height / segment_count;
|
|
|
|
|
|
|
|
|
|
if (event.xy[1] - win_rect->ymin > (item_height - segment_height)) {
|
|
|
|
|
return DropLocation::Before;
|
|
|
|
|
}
|
|
|
|
|
if (event.xy[1] - win_rect->ymin <= segment_height) {
|
2023-10-12 17:06:15 +02:00
|
|
|
if (behavior_ == DropBehavior::ReorderAndInsert && view_item_.is_collapsible() &&
|
|
|
|
|
!view_item_.is_collapsed())
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
{
|
|
|
|
|
/* Special case: Dropping at the lower 3rd of an uncollapsed item should insert into it, not
|
|
|
|
|
* after. */
|
|
|
|
|
return DropLocation::Into;
|
|
|
|
|
}
|
|
|
|
|
return DropLocation::After;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_assert(behavior_ == DropBehavior::ReorderAndInsert);
|
|
|
|
|
return DropLocation::Into;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2023-07-26 16:47:17 +02:00
|
|
|
void AbstractTreeViewItem::tree_row_click_fn(bContext *C, void *but_arg1, void * /*arg2*/)
|
2021-10-06 14:18:12 +02:00
|
|
|
{
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
uiButViewItem *item_but = (uiButViewItem *)but_arg1;
|
|
|
|
|
AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(*item_but->view_item);
|
2021-10-06 14:18:12 +02:00
|
|
|
|
2023-07-26 16:47:17 +02:00
|
|
|
tree_item.activate(*C);
|
2021-10-06 14:18:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AbstractTreeViewItem::add_treerow_button(uiBlock &block)
|
|
|
|
|
{
|
2021-10-07 14:59:43 +02:00
|
|
|
/* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */
|
2024-04-05 16:15:03 +02:00
|
|
|
view_item_but_ = reinterpret_cast<uiButViewItem *>(uiDefBut(&block,
|
|
|
|
|
UI_BTYPE_VIEW_ITEM,
|
|
|
|
|
0,
|
|
|
|
|
"",
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
UI_UNIT_X * 10,
|
|
|
|
|
padded_item_height(),
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
""));
|
2021-10-06 14:18:12 +02:00
|
|
|
|
2024-03-08 09:16:00 -05:00
|
|
|
view_item_but_->view_item = this;
|
2023-09-22 17:15:12 +02:00
|
|
|
view_item_but_->draw_height = unpadded_item_height();
|
2023-02-03 16:12:14 +01:00
|
|
|
UI_but_func_set(view_item_but_, tree_row_click_fn, view_item_but_, nullptr);
|
2021-10-07 14:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-06 17:00:44 +02:00
|
|
|
int AbstractTreeViewItem::indent_width() const
|
|
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
return this->count_parents() * UI_TREEVIEW_INDENT;
|
2023-07-06 17:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-07 14:59:43 +02:00
|
|
|
void AbstractTreeViewItem::add_indent(uiLayout &row) const
|
|
|
|
|
{
|
|
|
|
|
uiBlock *block = uiLayoutGetBlock(&row);
|
|
|
|
|
uiLayout *subrow = uiLayoutRow(&row, true);
|
|
|
|
|
uiLayoutSetFixedSize(subrow, true);
|
|
|
|
|
|
2024-03-07 14:45:33 -05:00
|
|
|
uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, this->indent_width(), 0, nullptr, 0.0, 0.0, "");
|
2021-10-07 14:59:43 +02:00
|
|
|
|
|
|
|
|
/* Indent items without collapsing icon some more within their parent. Makes it clear that they
|
|
|
|
|
* are actually nested and not just a row at the same level without a chevron. */
|
2024-03-07 14:45:33 -05:00
|
|
|
if (!this->is_collapsible()) {
|
2024-03-01 14:54:01 -05:00
|
|
|
uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, UI_TREEVIEW_INDENT, 0, nullptr, 0.0, 0.0, "");
|
2021-10-07 14:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Restore. */
|
|
|
|
|
UI_block_layout_set_current(block, &row);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-03 08:36:28 +10:00
|
|
|
void AbstractTreeViewItem::collapse_chevron_click_fn(bContext *C,
|
2021-10-07 14:59:43 +02:00
|
|
|
void * /*but_arg1*/,
|
|
|
|
|
void * /*arg2*/)
|
|
|
|
|
{
|
|
|
|
|
/* There's no data we could pass to this callback. It must be either the button itself or a
|
|
|
|
|
* consistent address to match buttons over redraws. So instead of passing it somehow, just
|
|
|
|
|
* lookup the hovered item via context here. */
|
|
|
|
|
|
|
|
|
|
const wmWindow *win = CTX_wm_window(C);
|
2024-05-10 11:25:44 +10:00
|
|
|
const ARegion *region = CTX_wm_region_popup(C) ? CTX_wm_region_popup(C) : CTX_wm_region(C);
|
2024-03-08 09:16:00 -05:00
|
|
|
AbstractViewItem *hovered_abstract_item = UI_region_views_find_item_at(*region,
|
|
|
|
|
win->eventstate->xy);
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
|
2024-03-08 09:16:00 -05:00
|
|
|
auto *hovered_item = reinterpret_cast<AbstractTreeViewItem *>(hovered_abstract_item);
|
2021-10-07 14:59:43 +02:00
|
|
|
BLI_assert(hovered_item != nullptr);
|
|
|
|
|
|
2024-02-02 12:28:22 +01:00
|
|
|
hovered_item->toggle_collapsed_from_view(*C);
|
2021-10-20 11:49:33 +02:00
|
|
|
/* When collapsing an item with an active child, make this collapsed item active instead so the
|
|
|
|
|
* active item stays visible. */
|
|
|
|
|
if (hovered_item->has_active_child()) {
|
2023-07-26 16:47:17 +02:00
|
|
|
hovered_item->activate(*C);
|
2021-10-20 11:49:33 +02:00
|
|
|
}
|
2021-10-07 14:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const
|
|
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
if (!this->is_collapsible()) {
|
2021-10-07 14:59:43 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 14:45:33 -05:00
|
|
|
const BIFIconID icon = this->is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT;
|
2024-02-02 19:40:28 +01:00
|
|
|
uiBut *but = uiDefIconBut(&block,
|
|
|
|
|
UI_BTYPE_BUT_TOGGLE,
|
|
|
|
|
0,
|
|
|
|
|
icon,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
UI_TREEVIEW_INDENT,
|
|
|
|
|
UI_UNIT_Y,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
"");
|
2021-10-07 14:59:43 +02:00
|
|
|
UI_but_func_set(but, collapse_chevron_click_fn, nullptr, nullptr);
|
2021-10-08 12:27:46 +02:00
|
|
|
UI_but_flag_disable(but, UI_BUT_UNDO);
|
2021-10-06 14:18:12 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-07 14:59:43 +02:00
|
|
|
void AbstractTreeViewItem::add_rename_button(uiLayout &row)
|
2021-10-06 14:18:12 +02:00
|
|
|
{
|
2021-10-07 14:59:43 +02:00
|
|
|
uiBlock *block = uiLayoutGetBlock(&row);
|
|
|
|
|
eUIEmbossType previous_emboss = UI_block_emboss_get(block);
|
|
|
|
|
|
|
|
|
|
uiLayoutRow(&row, false);
|
|
|
|
|
/* Enable emboss for the text button. */
|
|
|
|
|
UI_block_emboss_set(block, UI_EMBOSS);
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
AbstractViewItem::add_rename_button(*block);
|
2021-10-07 14:59:43 +02:00
|
|
|
|
|
|
|
|
UI_block_emboss_set(block, previous_emboss);
|
|
|
|
|
UI_block_layout_set_current(block, &row);
|
2021-10-06 14:18:12 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-20 11:49:33 +02:00
|
|
|
bool AbstractTreeViewItem::has_active_child() const
|
|
|
|
|
{
|
|
|
|
|
bool found = false;
|
|
|
|
|
foreach_item_recursive([&found](const AbstractTreeViewItem &item) {
|
|
|
|
|
if (item.is_active()) {
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-19 17:36:11 -05:00
|
|
|
bool AbstractTreeViewItem::supports_collapsing() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
2021-10-05 16:01:01 +02:00
|
|
|
}
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
StringRef AbstractTreeViewItem::get_rename_string() const
|
2021-10-06 14:18:12 +02:00
|
|
|
{
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
return label_;
|
2021-10-06 14:18:12 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-25 16:00:05 +02:00
|
|
|
bool AbstractTreeViewItem::rename(const bContext & /*C*/, StringRefNull new_name)
|
2021-10-06 14:18:12 +02:00
|
|
|
{
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
/* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single()
|
2021-10-06 14:18:12 +02:00
|
|
|
* recognizes the item. (It only compares labels by default.) */
|
|
|
|
|
label_ = new_name;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-18 16:51:57 +02:00
|
|
|
void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2022-07-18 16:51:57 +02:00
|
|
|
AbstractViewItem::update_from_old(old);
|
|
|
|
|
|
|
|
|
|
const AbstractTreeViewItem &old_tree_item = dynamic_cast<const AbstractTreeViewItem &>(old);
|
|
|
|
|
is_open_ = old_tree_item.is_open_;
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const
|
2021-09-29 17:01:13 +02:00
|
|
|
{
|
|
|
|
|
return label_ == other.label_;
|
|
|
|
|
}
|
|
|
|
|
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
std::unique_ptr<DropTargetInterface> AbstractTreeViewItem::create_item_drop_target()
|
|
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
return this->create_drop_target();
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<TreeViewItemDropTarget> AbstractTreeViewItem::create_drop_target()
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 11:50:17 +02:00
|
|
|
std::optional<std::string> AbstractTreeViewItem::debug_name() const
|
|
|
|
|
{
|
|
|
|
|
return label_;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-07 14:59:43 +02:00
|
|
|
AbstractTreeView &AbstractTreeViewItem::get_tree_view() const
|
2021-10-06 14:18:12 +02:00
|
|
|
{
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
return dynamic_cast<AbstractTreeView &>(get_view());
|
2021-10-06 14:18:12 +02:00
|
|
|
}
|
|
|
|
|
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
std::optional<rctf> AbstractTreeViewItem::get_win_rect(const ARegion ®ion) const
|
|
|
|
|
{
|
|
|
|
|
uiButViewItem *item_but = view_item_button();
|
|
|
|
|
if (!item_but) {
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rctf win_rect;
|
|
|
|
|
ui_block_to_window_rctf(®ion, item_but->block, &win_rect, &item_but->rect);
|
|
|
|
|
|
|
|
|
|
return win_rect;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
int AbstractTreeViewItem::count_parents() const
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
2021-12-09 12:07:34 +01:00
|
|
|
for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) {
|
2021-09-23 18:56:29 +02:00
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-26 15:32:26 +02:00
|
|
|
bool AbstractTreeViewItem::set_state_active()
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2023-07-26 16:33:28 +02:00
|
|
|
if (AbstractViewItem::set_state_active()) {
|
|
|
|
|
/* Make sure the active item is always visible. */
|
|
|
|
|
ensure_parents_uncollapsed();
|
|
|
|
|
return true;
|
2023-07-26 15:32:26 +02:00
|
|
|
}
|
2021-10-04 16:17:59 +02:00
|
|
|
|
2023-07-26 16:33:28 +02:00
|
|
|
return false;
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-06 16:29:10 +02:00
|
|
|
bool AbstractTreeViewItem::is_hovered() const
|
|
|
|
|
{
|
|
|
|
|
BLI_assert_msg(get_tree_view().is_reconstructed(),
|
|
|
|
|
"State can't be queried until reconstruction is completed");
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
BLI_assert_msg(view_item_but_ != nullptr,
|
2021-10-06 16:29:10 +02:00
|
|
|
"Hovered state can't be queried before the tree row is being built");
|
|
|
|
|
|
|
|
|
|
/* The new layout hasn't finished construction yet, so the final state of the button is unknown.
|
|
|
|
|
* Get the matching button from the previous redraw instead. */
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block(
|
2024-03-08 09:16:00 -05:00
|
|
|
*view_item_but_->block, *this);
|
2023-10-25 18:36:27 +02:00
|
|
|
return old_item_but && (old_item_but->flag & UI_HOVER);
|
2021-10-06 16:29:10 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
bool AbstractTreeViewItem::is_collapsed() const
|
|
|
|
|
{
|
2021-10-05 16:01:01 +02:00
|
|
|
BLI_assert_msg(get_tree_view().is_reconstructed(),
|
|
|
|
|
"State can't be queried until reconstruction is completed");
|
2024-03-07 14:45:33 -05:00
|
|
|
return this->is_collapsible() && !is_open_;
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-02 12:28:22 +01:00
|
|
|
bool AbstractTreeViewItem::toggle_collapsed()
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
return this->set_collapsed(is_open_);
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-02 12:28:22 +01:00
|
|
|
bool AbstractTreeViewItem::set_collapsed(const bool collapsed)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
if (!this->is_collapsible()) {
|
2024-02-02 12:28:22 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2024-02-05 16:49:42 +01:00
|
|
|
if (collapsed == !is_open_) {
|
2024-02-02 12:28:22 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
is_open_ = !collapsed;
|
2024-02-02 12:28:22 +01:00
|
|
|
return true;
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-18 19:32:53 +01:00
|
|
|
void AbstractTreeViewItem::uncollapse_by_default()
|
|
|
|
|
{
|
|
|
|
|
BLI_assert_msg(this->get_tree_view().is_reconstructed() == false,
|
|
|
|
|
"Default state should only be set while building the tree");
|
|
|
|
|
BLI_assert(this->supports_collapsing());
|
|
|
|
|
/* Set the open state. Note that this may be overridden later by #should_be_collapsed(). */
|
|
|
|
|
is_open_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
bool AbstractTreeViewItem::is_collapsible() const
|
|
|
|
|
{
|
2024-03-18 19:32:53 +01:00
|
|
|
BLI_assert_msg(get_tree_view().is_reconstructed(),
|
|
|
|
|
"State can't be queried until reconstruction is completed");
|
2021-11-19 17:36:11 -05:00
|
|
|
if (children_.is_empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return this->supports_collapsing();
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-02 12:28:22 +01:00
|
|
|
void AbstractTreeViewItem::on_collapse_change(bContext & /*C*/, const bool /*is_collapsed*/)
|
|
|
|
|
{
|
|
|
|
|
/* Do nothing by default. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<bool> AbstractTreeViewItem::should_be_collapsed() const
|
|
|
|
|
{
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AbstractTreeViewItem::toggle_collapsed_from_view(bContext &C)
|
|
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
if (this->toggle_collapsed()) {
|
|
|
|
|
this->on_collapse_change(C, this->is_collapsed());
|
2024-02-02 12:28:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AbstractTreeViewItem::change_state_delayed()
|
|
|
|
|
{
|
|
|
|
|
AbstractViewItem::change_state_delayed();
|
|
|
|
|
|
|
|
|
|
const std::optional<bool> should_be_collapsed = this->should_be_collapsed();
|
|
|
|
|
if (should_be_collapsed.has_value()) {
|
|
|
|
|
/* This reflects an external state change and therefore shouldn't call #on_collapse_change().
|
|
|
|
|
*/
|
2024-03-07 14:45:33 -05:00
|
|
|
this->set_collapsed(*should_be_collapsed);
|
2024-02-02 12:28:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-04 16:17:59 +02:00
|
|
|
void AbstractTreeViewItem::ensure_parents_uncollapsed()
|
|
|
|
|
{
|
|
|
|
|
for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) {
|
|
|
|
|
parent->set_collapsed(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const
|
2021-10-06 16:15:12 +02:00
|
|
|
{
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
const AbstractTreeViewItem &other_tree_item = dynamic_cast<const AbstractTreeViewItem &>(other);
|
|
|
|
|
|
2024-03-07 14:45:33 -05:00
|
|
|
if (!this->matches_single(other_tree_item)) {
|
2021-10-06 16:15:12 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2024-03-07 14:45:33 -05:00
|
|
|
if (this->count_parents() != other_tree_item.count_parents()) {
|
2021-10-06 16:52:16 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2021-10-06 16:15:12 +02:00
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_;
|
2021-10-06 16:15:12 +02:00
|
|
|
parent && other_parent;
|
|
|
|
|
parent = parent->parent_, other_parent = other_parent->parent_)
|
|
|
|
|
{
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
if (!parent->matches_single(*other_parent)) {
|
2021-10-06 16:15:12 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-24 18:02:56 +01:00
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2021-12-09 12:07:34 +01:00
|
|
|
class TreeViewLayoutBuilder {
|
|
|
|
|
uiBlock &block_;
|
2024-07-29 20:42:08 +02:00
|
|
|
bool add_box_ = true;
|
2021-12-09 12:07:34 +01:00
|
|
|
|
|
|
|
|
friend TreeViewBuilder;
|
|
|
|
|
|
|
|
|
|
public:
|
2024-10-02 11:19:49 +02:00
|
|
|
void build_from_tree(AbstractTreeView &tree_view);
|
2021-12-09 12:07:34 +01:00
|
|
|
void build_row(AbstractTreeViewItem &item) const;
|
|
|
|
|
|
|
|
|
|
uiBlock &block() const;
|
2023-03-20 11:35:45 +01:00
|
|
|
uiLayout ¤t_layout() const;
|
2021-12-09 12:07:34 +01:00
|
|
|
|
|
|
|
|
private:
|
2023-03-20 11:35:45 +01:00
|
|
|
/* Created through #TreeViewBuilder (friend class). */
|
|
|
|
|
TreeViewLayoutBuilder(uiLayout &layout);
|
2021-12-09 12:07:34 +01:00
|
|
|
};
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiLayout &layout) : block_(*uiLayoutGetBlock(&layout))
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
static int count_visible_items(AbstractTreeView &tree_view)
|
|
|
|
|
{
|
|
|
|
|
int item_count = 0;
|
|
|
|
|
tree_view.foreach_item([&](AbstractTreeViewItem &) { item_count++; },
|
|
|
|
|
AbstractTreeView::IterOptions::SkipCollapsed |
|
|
|
|
|
AbstractTreeView::IterOptions::SkipFiltered);
|
|
|
|
|
return item_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TreeViewLayoutBuilder::build_from_tree(AbstractTreeView &tree_view)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
uiLayout &parent_layout = this->current_layout();
|
2024-10-02 11:19:49 +02:00
|
|
|
uiBlock *block = uiLayoutGetBlock(&parent_layout);
|
2021-09-23 18:56:29 +02:00
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
uiLayout *col = nullptr;
|
2024-07-29 20:42:08 +02:00
|
|
|
if (add_box_) {
|
|
|
|
|
uiLayout *box = uiLayoutBox(&parent_layout);
|
2024-10-02 11:19:49 +02:00
|
|
|
col = uiLayoutColumn(box, true);
|
2024-07-29 20:42:08 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2024-10-02 11:19:49 +02:00
|
|
|
col = uiLayoutColumn(&parent_layout, true);
|
2024-07-29 20:42:08 +02:00
|
|
|
}
|
2024-10-02 11:19:49 +02:00
|
|
|
/* Row for the tree-view and the scroll bar. */
|
|
|
|
|
uiLayout *row = uiLayoutRow(col, false);
|
2021-09-23 18:56:29 +02:00
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
const std::optional<int> visible_row_count = tree_view.tot_visible_row_count();
|
|
|
|
|
const int tot_items = count_visible_items(tree_view);
|
2021-12-09 12:07:34 +01:00
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
/* 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<int>::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<int>(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (visible_row_count && (tot_items > *visible_row_count)) {
|
|
|
|
|
uiBut *but = 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,
|
|
|
|
|
"");
|
|
|
|
|
uiButScrollBar *but_scroll = reinterpret_cast<uiButScrollBar *>(but);
|
|
|
|
|
but_scroll->visual_height = *visible_row_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
"");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UI_block_layout_set_current(block, &parent_layout);
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-07 14:59:43 +02:00
|
|
|
void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const
|
|
|
|
|
{
|
2021-10-06 14:18:12 +02:00
|
|
|
uiBlock &block_ = block();
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
uiLayout &prev_layout = current_layout();
|
2021-10-07 14:59:43 +02:00
|
|
|
eUIEmbossType previous_emboss = UI_block_emboss_get(&block_);
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
uiLayout *overlap = uiLayoutOverlap(&prev_layout);
|
2021-10-07 14:59:43 +02:00
|
|
|
|
2023-03-28 12:33:35 +02:00
|
|
|
if (!item.is_interactive_) {
|
|
|
|
|
uiLayoutSetActive(overlap, false);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-16 11:42:11 +02:00
|
|
|
uiLayout *row = uiLayoutRow(overlap, false);
|
|
|
|
|
/* Enable emboss for mouse hover highlight. */
|
|
|
|
|
uiLayoutSetEmboss(row, UI_EMBOSS);
|
2021-10-06 14:18:12 +02:00
|
|
|
/* Every item gets one! Other buttons can be overlapped on top. */
|
|
|
|
|
item.add_treerow_button(block_);
|
|
|
|
|
|
2021-10-07 14:59:43 +02:00
|
|
|
/* After adding tree-row button (would disable hover highlighting). */
|
2024-04-05 16:13:04 +02:00
|
|
|
UI_block_emboss_set(&block_, UI_EMBOSS_NONE_OR_STATUS);
|
2021-10-07 14:59:43 +02:00
|
|
|
|
2024-04-05 16:15:03 +02:00
|
|
|
/* Add little margin to align actual contents vertically. */
|
|
|
|
|
uiLayout *content_col = uiLayoutColumn(overlap, true);
|
|
|
|
|
const int margin_top = (padded_item_height() - unpadded_item_height()) / 2;
|
|
|
|
|
if (margin_top > 0) {
|
|
|
|
|
uiDefBut(&block_, UI_BTYPE_LABEL, 0, "", 0, 0, UI_UNIT_X, margin_top, nullptr, 0, 0, "");
|
|
|
|
|
}
|
|
|
|
|
row = uiLayoutRow(content_col, true);
|
2024-07-29 23:52:38 +02:00
|
|
|
|
|
|
|
|
uiLayoutListItemAddPadding(row);
|
2021-10-07 14:59:43 +02:00
|
|
|
item.add_indent(*row);
|
|
|
|
|
item.add_collapse_chevron(block_);
|
|
|
|
|
|
2021-10-06 14:18:12 +02:00
|
|
|
if (item.is_renaming()) {
|
2021-10-07 14:59:43 +02:00
|
|
|
item.add_rename_button(*row);
|
2021-10-06 14:18:12 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
item.build_row(*row);
|
|
|
|
|
}
|
2021-09-23 18:56:29 +02:00
|
|
|
|
2024-07-29 23:52:38 +02:00
|
|
|
uiLayoutListItemAddPadding(row);
|
|
|
|
|
|
2021-10-07 14:59:43 +02:00
|
|
|
UI_block_emboss_set(&block_, previous_emboss);
|
2023-03-20 11:35:45 +01:00
|
|
|
UI_block_layout_set_current(&block_, &prev_layout);
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uiBlock &TreeViewLayoutBuilder::block() const
|
|
|
|
|
{
|
|
|
|
|
return block_;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
uiLayout &TreeViewLayoutBuilder::current_layout() const
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2023-03-20 11:35:45 +01:00
|
|
|
return *block().curlayout;
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2023-03-28 12:33:35 +02:00
|
|
|
void TreeViewBuilder::ensure_min_rows_items(AbstractTreeView &tree_view)
|
|
|
|
|
{
|
2024-10-02 11:19:49 +02:00
|
|
|
const std::optional<int> visible_rows = tree_view.tot_visible_row_count();
|
|
|
|
|
if (!visible_rows) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-28 12:33:35 +02:00
|
|
|
int tot_visible_items = 0;
|
|
|
|
|
tree_view.foreach_item(
|
|
|
|
|
[&tot_visible_items](AbstractTreeViewItem & /*item*/) { tot_visible_items++; },
|
2023-06-12 11:41:00 +02:00
|
|
|
AbstractTreeView::IterOptions::SkipCollapsed | AbstractTreeView::IterOptions::SkipFiltered);
|
2023-03-28 12:33:35 +02:00
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
if (tot_visible_items >= *visible_rows) {
|
2023-03-28 12:33:35 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 11:19:49 +02:00
|
|
|
for (int i = 0; i < (*visible_rows - tot_visible_items); i++) {
|
2023-03-28 12:33:35 +02:00
|
|
|
BasicTreeViewItem &new_item = tree_view.add_tree_item<BasicTreeViewItem>("");
|
|
|
|
|
new_item.disable_interaction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 20:21:25 +02:00
|
|
|
void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view,
|
|
|
|
|
uiLayout &layout,
|
2024-07-29 20:42:08 +02:00
|
|
|
std::optional<StringRef> search_string,
|
|
|
|
|
const bool add_box)
|
2021-12-09 12:07:34 +01:00
|
|
|
{
|
2023-03-20 11:35:45 +01:00
|
|
|
uiBlock &block = *uiLayoutGetBlock(&layout);
|
2021-12-09 12:07:34 +01:00
|
|
|
|
|
|
|
|
tree_view.build_tree();
|
2023-03-20 11:35:45 +01:00
|
|
|
tree_view.update_from_old(block);
|
2021-12-09 12:07:34 +01:00
|
|
|
tree_view.change_state_delayed();
|
2024-07-01 20:21:25 +02:00
|
|
|
tree_view.filter(search_string);
|
2021-12-09 12:07:34 +01:00
|
|
|
|
2023-03-28 12:33:35 +02:00
|
|
|
ensure_min_rows_items(tree_view);
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
/* Ensure the given layout is actually active. */
|
|
|
|
|
UI_block_layout_set_current(&block, &layout);
|
|
|
|
|
|
|
|
|
|
TreeViewLayoutBuilder builder(layout);
|
2024-07-29 20:42:08 +02:00
|
|
|
builder.add_box_ = add_box;
|
2021-12-09 12:07:34 +01:00
|
|
|
builder.build_from_tree(tree_view);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2021-10-05 14:25:40 +02:00
|
|
|
BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(icon_)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
|
|
|
|
label_ = label;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-07 14:59:43 +02:00
|
|
|
void BasicTreeViewItem::build_row(uiLayout &row)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
2024-03-07 14:45:33 -05:00
|
|
|
this->add_label(row);
|
2021-10-27 12:06:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override)
|
|
|
|
|
{
|
|
|
|
|
const StringRefNull label = label_override.is_empty() ? StringRefNull(label_) : label_override;
|
2021-11-02 17:50:18 +01:00
|
|
|
uiItemL(&layout, IFACE_(label.c_str()), icon);
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-26 16:47:17 +02:00
|
|
|
void BasicTreeViewItem::on_activate(bContext &C)
|
2021-09-23 18:56:29 +02:00
|
|
|
{
|
|
|
|
|
if (activate_fn_) {
|
2023-07-26 16:47:17 +02:00
|
|
|
activate_fn_(C, *this);
|
2021-09-23 18:56:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-19 17:36:11 -05:00
|
|
|
void BasicTreeViewItem::set_on_activate_fn(ActivateFn fn)
|
2021-10-05 14:25:40 +02:00
|
|
|
{
|
|
|
|
|
activate_fn_ = fn;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-19 17:36:11 -05:00
|
|
|
void BasicTreeViewItem::set_is_active_fn(IsActiveFn is_active_fn)
|
|
|
|
|
{
|
|
|
|
|
is_active_fn_ = is_active_fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<bool> BasicTreeViewItem::should_be_active() const
|
|
|
|
|
{
|
|
|
|
|
if (is_active_fn_) {
|
|
|
|
|
return is_active_fn_();
|
|
|
|
|
}
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:56:29 +02:00
|
|
|
} // namespace blender::ui
|