Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/editors/include/UI_interface.h2
-rw-r--r--source/blender/editors/include/UI_tree_view.hh44
-rw-r--r--source/blender/editors/interface/interface_handlers.c22
-rw-r--r--source/blender/editors/interface/tree_view.cc225
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc26
5 files changed, 278 insertions, 41 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index f642895f64e..e8b71a41439 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2770,6 +2770,8 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item,
const struct bContext *C,
const struct wmDrag *drag,
const struct wmEvent *event);
+bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle);
+void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle);
uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y);
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index dbafd1b3a2b..51737067648 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -23,10 +23,13 @@
#pragma once
+#include <array>
#include <functional>
#include <memory>
#include <string>
+#include "DNA_defs.h"
+
#include "BLI_function_ref.hh"
#include "BLI_vector.hh"
@@ -144,8 +147,14 @@ class TreeViewLayoutBuilder {
* \{ */
class AbstractTreeView : public TreeViewItemContainer {
+ friend AbstractTreeViewItem;
friend TreeViewBuilder;
- friend TreeViewLayoutBuilder;
+
+ /**
+ * Only one item can be renamed at a time. So the tree is informed about the renaming state to
+ * enforce that.
+ */
+ std::unique_ptr<std::array<char, MAX_NAME>> rename_buffer_;
bool is_reconstructed_ = false;
@@ -154,6 +163,8 @@ class AbstractTreeView : public TreeViewItemContainer {
void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
+ /** Only one item can be renamed at a time. */
+ bool is_renaming() const;
/**
* Check if the tree is fully (re-)constructed. That means, both #build_tree() and
* #update_from_old() have finished.
@@ -198,6 +209,7 @@ class AbstractTreeView : public TreeViewItemContainer {
*/
class AbstractTreeViewItem : public TreeViewItemContainer {
friend class AbstractTreeView;
+ friend class TreeViewLayoutBuilder;
public:
using IsActiveFn = std::function<bool()>;
@@ -205,12 +217,15 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
private:
bool is_open_ = false;
bool is_active_ = false;
+ bool is_renaming_ = false;
IsActiveFn is_active_fn_;
protected:
/** This label is used for identifying an item (together with its parent's labels). */
std::string label_{};
+ /** Every item gets a button of type during the layout building #UI_BTYPE_TREEROW. */
+ uiButTreeRow *tree_row_but_ = nullptr;
public:
virtual ~AbstractTreeViewItem() = default;
@@ -234,6 +249,19 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
virtual std::string drop_tooltip(const bContext &C,
const wmDrag &drag,
const wmEvent &event) const;
+ /**
+ * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if
+ * another item is already being renamed.
+ */
+ virtual bool can_rename() const;
+ /**
+ * Try renaming the item, or the data it represents. Can assume
+ * #AbstractTreeViewItem::can_rename() returned true. Sub-classes that override this should
+ * usually call this, unless they have a custom #AbstractTreeViewItem.matches().
+ *
+ * \return True if the renaming was successful.
+ */
+ virtual bool rename(StringRefNull new_name);
/**
* Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of
@@ -250,7 +278,11 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
*/
virtual bool matches(const AbstractTreeViewItem &other) const;
+ void begin_renaming();
+ void end_renaming();
+
const AbstractTreeView &get_tree_view() const;
+ AbstractTreeView &get_tree_view();
int count_parents() const;
void deactivate();
/**
@@ -266,6 +298,8 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
bool is_collapsed() const;
void set_collapsed(bool collapsed);
bool is_collapsible() const;
+ bool is_renaming() const;
+
void ensure_parents_uncollapsed();
protected:
@@ -278,8 +312,14 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
void activate();
private:
+ static void rename_button_fn(bContext *, void *, char *);
+ static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but);
+ static void tree_row_click_fn(struct bContext *, void *, void *);
+
/** See #AbstractTreeView::change_state_delayed() */
void change_state_delayed();
+ void add_treerow_button(uiBlock &block);
+ void add_rename_button(uiBlock &block);
};
/** \} */
@@ -304,8 +344,6 @@ class BasicTreeViewItem : public AbstractTreeViewItem {
void on_activate(ActivateFn fn);
protected:
- /** Created in the #build() function. */
- uiButTreeRow *tree_row_but_ = nullptr;
/**
* Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree
* view item. This way users don't have to sub-class #BasicTreeViewItem, just to implement
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 6ee563003ef..f73420b3668 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -4822,6 +4822,24 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
return WM_UI_HANDLER_CONTINUE;
}
+static int ui_do_but_TREEROW(bContext *C,
+ uiBut *but,
+ uiHandleButtonData *data,
+ const wmEvent *event)
+{
+ uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
+ BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW);
+
+ if ((event->type == LEFTMOUSE) && (event->val == KM_DBL_CLICK)) {
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+
+ UI_tree_view_item_begin_rename(tree_row_but->tree_item);
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return ui_do_but_TOG(C, but, data, event);
+}
+
static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -7989,10 +8007,12 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_CHECKBOX:
case UI_BTYPE_CHECKBOX_N:
case UI_BTYPE_ROW:
- case UI_BTYPE_TREEROW:
case UI_BTYPE_DATASETROW:
retval = ui_do_but_TOG(C, but, data, event);
break;
+ case UI_BTYPE_TREEROW:
+ retval = ui_do_but_TREEROW(C, but, data, event);
+ break;
case UI_BTYPE_SCROLL:
retval = ui_do_but_SCROLL(C, block, but, data, event);
break;
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc
index 8bd2be7dc77..8f272143b2c 100644
--- a/source/blender/editors/interface/tree_view.cc
+++ b/source/blender/editors/interface/tree_view.cc
@@ -20,6 +20,8 @@
#include "DNA_userdef_types.h"
+#include "BKE_context.h"
+
#include "BLT_translation.h"
#include "interface_intern.h"
@@ -76,6 +78,11 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con
foreach_item_recursive(iter_fn, options);
}
+bool AbstractTreeView::is_renaming() const
+{
+ return rename_buffer_ != nullptr;
+}
+
void AbstractTreeView::build_layout_from_tree(const TreeViewLayoutBuilder &builder)
{
uiLayout *prev_layout = builder.current_layout();
@@ -103,6 +110,13 @@ void AbstractTreeView::update_from_old(uiBlock &new_block)
BLI_assert(old_view_handle);
AbstractTreeView &old_view = reinterpret_cast<AbstractTreeView &>(*old_view_handle);
+
+ /* Update own persistent data. */
+ /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's
+ * pointer to identify itself over redraws. */
+ rename_buffer_ = std::move(old_view.rename_buffer_);
+ old_view.rename_buffer_ = nullptr;
+
update_children_from_old_recursive(*this, old_view);
/* Finished (re-)constructing the tree. */
@@ -153,6 +167,95 @@ void AbstractTreeView::change_state_delayed()
/* ---------------------------------------------------------------------- */
+void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/,
+ void *but_arg1,
+ void * /*arg2*/)
+{
+ uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1;
+ BasicTreeViewItem &tree_item = reinterpret_cast<BasicTreeViewItem &>(*tree_row_but->tree_item);
+
+ /* Let a click on an opened item activate it, a second click will close it then.
+ * TODO Should this be for asset catalogs only? */
+ if (tree_item.is_collapsed() || tree_item.is_active()) {
+ tree_item.toggle_collapsed();
+ }
+ tree_item.activate();
+}
+
+void AbstractTreeViewItem::add_treerow_button(uiBlock &block)
+{
+ tree_row_but_ = (uiButTreeRow *)uiDefBut(
+ &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, "");
+
+ tree_row_but_->tree_item = reinterpret_cast<uiTreeViewItemHandle *>(this);
+ UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr);
+ UI_but_treerow_indentation_set(&tree_row_but_->but, count_parents());
+}
+
+AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button(
+ const uiBut &rename_but)
+{
+ /* A minimal sanity check, can't do much more here. */
+ BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin);
+
+ LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) {
+ if (but->type != UI_BTYPE_TREEROW) {
+ continue;
+ }
+
+ uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
+ AbstractTreeViewItem *item = reinterpret_cast<AbstractTreeViewItem *>(tree_row_but->tree_item);
+ const AbstractTreeView &tree_view = item->get_tree_view();
+
+ if (item->is_renaming() && (tree_view.rename_buffer_->data() == rename_but.poin)) {
+ return item;
+ }
+ }
+
+ return nullptr;
+}
+
+void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr))
+{
+ const uiBut *rename_but = static_cast<uiBut *>(arg);
+ AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but);
+ BLI_assert(item);
+
+ const AbstractTreeView &tree_view = item->get_tree_view();
+ item->rename(tree_view.rename_buffer_->data());
+ item->end_renaming();
+}
+
+void AbstractTreeViewItem::add_rename_button(uiBlock &block)
+{
+ AbstractTreeView &tree_view = get_tree_view();
+ uiBut *rename_but = uiDefBut(&block,
+ UI_BTYPE_TEXT,
+ 1,
+ "",
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ tree_view.rename_buffer_->data(),
+ 1.0f,
+ tree_view.rename_buffer_->max_size(),
+ 0,
+ 0,
+ "");
+
+ /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the
+ * callback is executed. */
+ UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but);
+
+ const bContext *evil_C = static_cast<bContext *>(block.evil_C);
+ ARegion *region = CTX_wm_region(evil_C);
+ /* Returns false if the button was removed. */
+ if (UI_but_active_only(evil_C, region, &block, rename_but) == false) {
+ end_renaming();
+ }
+}
+
void AbstractTreeViewItem::on_activate()
{
/* Do nothing by default. */
@@ -181,10 +284,25 @@ std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/,
return TIP_("Drop into/onto tree item");
}
+bool AbstractTreeViewItem::can_rename() const
+{
+ /* No renaming by default. */
+ return false;
+}
+
+bool AbstractTreeViewItem::rename(StringRefNull new_name)
+{
+ /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches()
+ * recognizes the item. (It only compares labels by default.) */
+ label_ = new_name;
+ return true;
+}
+
void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old)
{
is_open_ = old.is_open_;
is_active_ = old.is_active_;
+ is_renaming_ = old.is_renaming_;
}
bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const
@@ -192,11 +310,41 @@ bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const
return label_ == other.label_;
}
+void AbstractTreeViewItem::begin_renaming()
+{
+ AbstractTreeView &tree_view = get_tree_view();
+ if (tree_view.is_renaming() || !can_rename()) {
+ return;
+ }
+
+ is_renaming_ = true;
+
+ tree_view.rename_buffer_ = std::make_unique<decltype(tree_view.rename_buffer_)::element_type>();
+ std::copy(std::begin(label_), std::end(label_), std::begin(*tree_view.rename_buffer_));
+}
+
+void AbstractTreeViewItem::end_renaming()
+{
+ if (!is_renaming()) {
+ return;
+ }
+
+ is_renaming_ = false;
+
+ AbstractTreeView &tree_view = get_tree_view();
+ tree_view.rename_buffer_ = nullptr;
+}
+
const AbstractTreeView &AbstractTreeViewItem::get_tree_view() const
{
return static_cast<AbstractTreeView &>(*root_);
}
+AbstractTreeView &AbstractTreeViewItem::get_tree_view()
+{
+ return static_cast<AbstractTreeView &>(*root_);
+}
+
int AbstractTreeViewItem::count_parents() const
{
int i = 0;
@@ -259,6 +407,11 @@ bool AbstractTreeViewItem::is_collapsible() const
return !children_.is_empty();
}
+bool AbstractTreeViewItem::is_renaming() const
+{
+ return is_renaming_;
+}
+
void AbstractTreeViewItem::ensure_parents_uncollapsed()
{
for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) {
@@ -298,9 +451,21 @@ void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const
uiLayout *prev_layout = current_layout();
uiLayout *row = uiLayoutRow(prev_layout, false);
- item.build_row(*row);
+ uiLayoutOverlap(row);
+
+ uiBlock &block_ = block();
+
+ /* Every item gets one! Other buttons can be overlapped on top. */
+ item.add_treerow_button(block_);
+
+ if (item.is_renaming()) {
+ item.add_rename_button(block_);
+ }
+ else {
+ item.build_row(*row);
+ }
- UI_block_layout_set_current(&block(), prev_layout);
+ UI_block_layout_set_current(&block_, prev_layout);
}
uiBlock &TreeViewLayoutBuilder::block() const
@@ -320,42 +485,12 @@ BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(ic
label_ = label;
}
-void BasicTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/)
+void BasicTreeViewItem::build_row(uiLayout & /*row*/)
{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1;
- BasicTreeViewItem &tree_item = reinterpret_cast<BasicTreeViewItem &>(*tree_row_but->tree_item);
-
- /* Let a click on an opened item activate it, a second click will close it then.
- * TODO Should this be for asset catalogs only? */
- if (tree_item.is_collapsed() || tree_item.is_active()) {
- tree_item.toggle_collapsed();
+ if (BIFIconID icon = get_draw_icon()) {
+ ui_def_but_icon(&tree_row_but_->but, icon, UI_HAS_ICON);
}
- tree_item.activate();
-}
-
-void BasicTreeViewItem::build_row(uiLayout &row)
-{
- uiBlock *block = uiLayoutGetBlock(&row);
- tree_row_but_ = (uiButTreeRow *)uiDefIconTextBut(block,
- UI_BTYPE_TREEROW,
- 0,
- /* TODO allow icon besides the chevron icon? */
- get_draw_icon(),
- label_.data(),
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_Y,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
-
- tree_row_but_->tree_item = reinterpret_cast<uiTreeViewItemHandle *>(this);
- UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr);
- UI_but_treerow_indentation_set(&tree_row_but_->but, count_parents());
+ tree_row_but_->but.str = BLI_strdupn(label_.c_str(), label_.length());
}
void BasicTreeViewItem::on_activate()
@@ -437,3 +572,21 @@ bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *
return false;
}
+
+/**
+ * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around
+ * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed,
+ * and returns false if so.
+ */
+bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle)
+{
+ const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
+ const AbstractTreeView &tree_view = item.get_tree_view();
+ return !tree_view.is_renaming() && item.can_rename();
+}
+
+void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle)
+{
+ AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_handle);
+ item.begin_renaming();
+}
diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc
index 291582dac08..85912268286 100644
--- a/source/blender/editors/space_file/asset_catalog_tree_view.cc
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -52,6 +52,7 @@ using namespace blender::bke;
namespace blender::ed::asset_browser {
class AssetCatalogTreeView : public ui::AbstractTreeView {
+ bke::AssetCatalogService *catalog_service_;
/** The asset catalog tree this tree-view represents. */
bke::AssetCatalogTree *catalog_tree_;
FileAssetSelectParams *params_;
@@ -99,6 +100,9 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
const wmDrag &drag,
const wmEvent &event) const override;
bool on_drop(const wmDrag &drag) override;
+
+ bool can_rename() const override;
+ bool rename(StringRefNull new_name) override;
};
/** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level
@@ -124,7 +128,8 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library,
FileAssetSelectParams *params,
SpaceFile &space_file)
- : catalog_tree_(BKE_asset_library_get_catalog_tree(library)),
+ : catalog_service_(BKE_asset_library_get_catalog_service(library)),
+ catalog_tree_(BKE_asset_library_get_catalog_tree(library)),
params_(params),
space_file_(space_file)
{
@@ -309,6 +314,25 @@ bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag)
tree_view, drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name());
}
+bool AssetCatalogTreeViewItem::can_rename() const
+{
+ return true;
+}
+
+bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
+{
+ /* Important to keep state. */
+ BasicTreeViewItem::rename(new_name);
+
+ const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
+ get_tree_view());
+
+ AssetCatalogPath new_path = catalog_item_.catalog_path().parent();
+ new_path = new_path / StringRef(new_name);
+ tree_view.catalog_service_->update_catalog_path(catalog_item_.get_catalog_id(), new_path);
+ return true;
+}
+
/* ---------------------------------------------------------------------- */
void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)