Nodes: Support adding multiple nodes from search menu

Needed for the simulation zone addition to the add menu search.
Link-drag search isn't supported yet, but a similar change should be
possible there.

Now the search item exposed through the public nodes namespace returns
several items directly, and the "after add function" is just handled as
a sub-case of that for convenience.
This commit is contained in:
Hans Goudey
2023-04-26 11:40:54 -04:00
parent f6931a9ead
commit 45cecb101c
4 changed files with 91 additions and 90 deletions

View File

@@ -37,16 +37,10 @@ struct bContext;
namespace blender::ed::space_node {
struct AddNodeItem {
nodes::AddNodeInfo info;
std::string identifier;
const AssetRepresentation *asset;
};
struct AddNodeSearchStorage {
float2 cursor;
bool use_transform;
Vector<AddNodeItem> search_add_items;
Vector<nodes::AddNodeItem> search_add_items;
char search[256];
bool update_items_tag = true;
};
@@ -67,7 +61,7 @@ static void add_node_search_listen_fn(const wmRegionListenerParams *params, void
static void search_items_for_asset_metadata(const bNodeTree &node_tree,
const AssetHandle asset_handle,
Vector<AddNodeItem> &search_items)
nodes::GatherAddNodeSearchParams &params)
{
const AssetMetaData &asset_data = *ED_asset_handle_get_metadata(&asset_handle);
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&asset_data, "type");
@@ -76,28 +70,27 @@ static void search_items_for_asset_metadata(const bNodeTree &node_tree,
}
const AssetRepresentation *asset = ED_asset_handle_get_representation(&asset_handle);
AddNodeItem item{};
item.info.ui_name = ED_asset_handle_get_name(&asset_handle);
item.identifier = node_tree.typeinfo->group_idname;
item.info.description = asset_data.description == nullptr ? "" : asset_data.description;
item.asset = asset;
item.info.after_add_fn = [asset](const bContext &C, bNodeTree &node_tree, bNode &node) {
Main &bmain = *CTX_data_main(&C);
node.flag &= ~NODE_OPTIONS;
node.id = ED_asset_get_local_id_from_asset_or_append_and_reuse(&bmain, asset, ID_NT);
id_us_plus(node.id);
BKE_ntree_update_tag_node_property(&node_tree, &node);
DEG_relations_tag_update(&bmain);
};
search_items.append(std::move(item));
params.add_single_node_item(ED_asset_handle_get_name(&asset_handle),
asset_data.description == nullptr ? "" : asset_data.description,
[asset](const bContext &C, bNodeTree &node_tree, bNode &node) {
Main &bmain = *CTX_data_main(&C);
node.flag &= ~NODE_OPTIONS;
node.id = ED_asset_get_local_id_from_asset_or_append_and_reuse(
&bmain, asset, ID_NT);
id_us_plus(node.id);
BKE_ntree_update_tag_node_property(&node_tree, &node);
DEG_relations_tag_update(&bmain);
});
}
static void gather_search_items_for_all_assets(const bContext &C,
const bNodeTree &node_tree,
Set<std::string> &r_added_assets,
Vector<AddNodeItem> &search_items)
Vector<nodes::AddNodeItem> &search_items)
{
const bNodeType &group_node_type = *nodeTypeFind(node_tree.typeinfo->group_idname);
nodes::GatherAddNodeSearchParams params(C, group_node_type, node_tree, search_items);
AssetLibraryReference library_ref{};
library_ref.custom_library_index = -1;
library_ref.type = ASSET_LIBRARY_ALL;
@@ -115,7 +108,7 @@ static void gather_search_items_for_all_assets(const bContext &C,
/* If an asset with the same name has already been added, skip this. */
return true;
}
search_items_for_asset_metadata(node_tree, asset, search_items);
search_items_for_asset_metadata(node_tree, asset, params);
return true;
});
}
@@ -123,9 +116,11 @@ static void gather_search_items_for_all_assets(const bContext &C,
static void gather_search_items_for_node_groups(const bContext &C,
const bNodeTree &node_tree,
const Set<std::string> &local_assets,
Vector<AddNodeItem> &search_items)
Vector<nodes::AddNodeItem> &search_items)
{
const StringRef group_node_id = node_tree.typeinfo->group_idname;
const StringRefNull group_node_id = node_tree.typeinfo->group_idname;
const bNodeType &group_node_type = *nodeTypeFind(group_node_id.c_str());
nodes::GatherAddNodeSearchParams params(C, group_node_type, node_tree, search_items);
Main &bmain = *CTX_data_main(&C);
LISTBASE_FOREACH (bNodeTree *, node_group, &bmain.nodetrees) {
@@ -138,23 +133,22 @@ static void gather_search_items_for_node_groups(const bContext &C,
if (!nodeGroupPoll(&node_tree, node_group, nullptr)) {
continue;
}
AddNodeItem item{};
item.info.ui_name = node_group->id.name + 2;
item.identifier = node_tree.typeinfo->group_idname;
item.info.after_add_fn = [node_group](const bContext &C, bNodeTree &node_tree, bNode &node) {
Main &bmain = *CTX_data_main(&C);
node.id = &node_group->id;
id_us_plus(node.id);
BKE_ntree_update_tag_node_property(&node_tree, &node);
DEG_relations_tag_update(&bmain);
};
search_items.append(std::move(item));
params.add_single_node_item(
node_group->id.name + 2,
"",
[node_group](const bContext &C, bNodeTree &node_tree, bNode &node) {
Main &bmain = *CTX_data_main(&C);
node.id = &node_group->id;
id_us_plus(node.id);
BKE_ntree_update_tag_node_property(&node_tree, &node);
DEG_relations_tag_update(&bmain);
});
}
}
static void gather_add_node_operations(const bContext &C,
bNodeTree &node_tree,
Vector<AddNodeItem> &r_search_items)
Vector<nodes::AddNodeItem> &r_search_items)
{
NODE_TYPES_BEGIN (node_type) {
const char *disabled_hint;
@@ -167,15 +161,8 @@ static void gather_add_node_operations(const bContext &C,
if (!node_type->gather_add_node_search_ops) {
continue;
}
Vector<nodes::AddNodeInfo> info_items;
nodes::GatherAddNodeSearchParams params(C, *node_type, node_tree, info_items);
nodes::GatherAddNodeSearchParams params(C, *node_type, node_tree, r_search_items);
node_type->gather_add_node_search_ops(params);
for (nodes::AddNodeInfo &info : info_items) {
AddNodeItem item{};
item.info = std::move(info);
item.identifier = node_type->idname;
r_search_items.append(item);
}
}
NODE_TYPES_END;
@@ -200,19 +187,19 @@ static void add_node_search_update_fn(
StringSearch *search = BLI_string_search_new();
for (AddNodeItem &item : storage.search_add_items) {
BLI_string_search_add(search, item.info.ui_name.c_str(), &item, item.info.weight);
for (nodes::AddNodeItem &item : storage.search_add_items) {
BLI_string_search_add(search, item.ui_name.c_str(), &item, item.weight);
}
/* Don't filter when the menu is first opened, but still run the search
* so the items are in the same order they will appear in while searching. */
const char *string = is_first ? "" : str;
AddNodeItem **filtered_items;
nodes::AddNodeItem **filtered_items;
const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items);
for (const int i : IndexRange(filtered_amount)) {
AddNodeItem &item = *filtered_items[i];
if (!UI_search_item_add(items, item.info.ui_name.c_str(), &item, ICON_NONE, 0, 0)) {
nodes::AddNodeItem &item = *filtered_items[i];
if (!UI_search_item_add(items, item.ui_name.c_str(), &item, ICON_NONE, 0, 0)) {
break;
}
}
@@ -227,24 +214,13 @@ static void add_node_search_exec_fn(bContext *C, void *arg1, void *arg2)
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
AddNodeSearchStorage &storage = *static_cast<AddNodeSearchStorage *>(arg1);
AddNodeItem *item = static_cast<AddNodeItem *>(arg2);
nodes::AddNodeItem *item = static_cast<nodes::AddNodeItem *>(arg2);
if (item == nullptr) {
return;
}
node_deselect_all(node_tree);
bNode *new_node = nodeAddNode(C, &node_tree, item->identifier.c_str());
BLI_assert(new_node != nullptr);
if (item->info.after_add_fn) {
item->info.after_add_fn(*C, node_tree, *new_node);
}
new_node->locx = storage.cursor.x / UI_SCALE_FAC;
new_node->locy = storage.cursor.y / UI_SCALE_FAC + 20;
nodeSetSelected(new_node, true);
nodeSetActive(&node_tree, new_node);
Vector<bNode *> new_nodes = item->add_fn(*C, node_tree, storage.cursor);
/* Ideally it would be possible to tag the node tree in some way so it updates only after the
* translate operation is finished, but normally moving nodes around doesn't cause updates. */
@@ -263,13 +239,12 @@ static void add_node_search_exec_fn(bContext *C, void *arg1, void *arg2)
static ARegion *add_node_search_tooltip_fn(
bContext *C, ARegion *region, const rcti *item_rect, void * /*arg*/, void *active)
{
const AddNodeItem *item = static_cast<const AddNodeItem *>(active);
const nodes::AddNodeItem *item = static_cast<const nodes::AddNodeItem *>(active);
uiSearchItemTooltipData tooltip_data{};
BLI_strncpy(tooltip_data.description,
item->asset ? item->info.description.c_str() : TIP_(item->info.description.c_str()),
sizeof(tooltip_data.description));
BLI_strncpy(
tooltip_data.description, TIP_(item->description.c_str()), sizeof(tooltip_data.description));
return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data);
}

View File

@@ -5,6 +5,7 @@
#include <functional>
#include "BLI_function_ref.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
@@ -16,25 +17,27 @@ struct bContext;
namespace blender::nodes {
struct AddNodeInfo {
using AfterAddFn = std::function<void(const bContext &C, bNodeTree &node_tree, bNode &node)>;
struct AddNodeItem {
using AddFn =
std::function<Vector<bNode *>(const bContext &C, bNodeTree &node_tree, float2 cursor)>;
std::string ui_name;
std::string description;
AfterAddFn after_add_fn;
int weight = 0;
AddFn add_fn;
};
class GatherAddNodeSearchParams {
using AfterAddFn = std::function<void(const bContext &C, bNodeTree &node_tree, bNode &node)>;
const bContext &C_;
const bNodeType &node_type_;
const bNodeTree &node_tree_;
Vector<AddNodeInfo> &r_items;
Vector<AddNodeItem> &r_items;
public:
GatherAddNodeSearchParams(const bContext &C,
const bNodeType &node_type,
const bNodeTree &node_tree,
Vector<AddNodeInfo> &r_items)
Vector<AddNodeItem> &r_items)
: C_(C), node_type_(node_type), node_tree_(node_tree), r_items(r_items)
{
}
@@ -57,10 +60,11 @@ class GatherAddNodeSearchParams {
/**
* \param weight: Used to customize the order when multiple search items match.
*/
void add_item(std::string ui_name,
std::string description,
AddNodeInfo::AfterAddFn fn = {},
int weight = 0);
void add_single_node_item(std::string ui_name,
std::string description,
AfterAddFn after_add_fn = {},
int weight = 0);
void add_item(AddNodeItem item);
};
void search_node_add_ops_for_basic_node(GatherAddNodeSearchParams &params);

View File

@@ -9,17 +9,39 @@
namespace blender::nodes {
void GatherAddNodeSearchParams::add_item(std::string ui_name,
std::string description,
AddNodeInfo::AfterAddFn fn,
int weight)
void GatherAddNodeSearchParams::add_single_node_item(std::string ui_name,
std::string description,
AfterAddFn after_add_fn,
int weight)
{
r_items.append(AddNodeInfo{std::move(ui_name), std::move(description), std::move(fn), weight});
AddNodeItem item;
item.ui_name = std::move(ui_name);
item.description = std::move(description);
item.weight = weight;
item.add_fn = [after_add_fn = std::move(after_add_fn), node_type = node_type_](
const bContext &C, bNodeTree &node_tree, const float2 cursor) {
bNode *new_node = nodeAddNode(&C, &node_tree, node_type.idname);
new_node->locx = cursor.x / UI_SCALE_FAC;
new_node->locy = cursor.y / UI_SCALE_FAC + 20;
nodeSetSelected(new_node, true);
nodeSetActive(&node_tree, new_node);
if (after_add_fn) {
after_add_fn(C, node_tree, *new_node);
}
return Vector<bNode *>{new_node};
};
r_items.append(std::move(item));
}
void GatherAddNodeSearchParams::add_item(AddNodeItem item)
{
r_items.append(std::move(item));
}
void search_node_add_ops_for_basic_node(GatherAddNodeSearchParams &params)
{
params.add_item(IFACE_(params.node_type().ui_name), TIP_(params.node_type().ui_description));
params.add_single_node_item(IFACE_(params.node_type().ui_name),
TIP_(params.node_type().ui_description));
}
} // namespace blender::nodes

View File

@@ -227,12 +227,12 @@ static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
static void gather_add_node_searches(GatherAddNodeSearchParams &params)
{
params.add_item(IFACE_("Mix"), params.node_type().ui_description);
params.add_item(IFACE_("Mix Color"),
params.node_type().ui_description,
[](const bContext & /*C*/, bNodeTree & /*node_tree*/, bNode &node) {
node_storage(node).data_type = SOCK_RGBA;
});
params.add_single_node_item(IFACE_("Mix"), params.node_type().ui_description);
params.add_single_node_item(IFACE_("Mix Color"),
params.node_type().ui_description,
[](const bContext & /*C*/, bNodeTree & /*node_tree*/, bNode &node) {
node_storage(node).data_type = SOCK_RGBA;
});
}
static void node_mix_init(bNodeTree * /*tree*/, bNode *node)