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:
Diffstat (limited to 'source/blender/editors/space_node')
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt8
-rw-r--r--source/blender/editors/space_node/add_menu_assets.cc316
-rw-r--r--source/blender/editors/space_node/add_node_search.cc358
-rw-r--r--source/blender/editors/space_node/drawnode.cc142
-rw-r--r--source/blender/editors/space_node/link_drag_search.cc175
-rw-r--r--source/blender/editors/space_node/node_add.cc195
-rw-r--r--source/blender/editors/space_node/node_draw.cc123
-rw-r--r--source/blender/editors/space_node/node_edit.cc182
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc2
-rw-r--r--source/blender/editors/space_node/node_gizmo.cc24
-rw-r--r--source/blender/editors/space_node/node_group.cc99
-rw-r--r--source/blender/editors/space_node/node_intern.hh25
-rw-r--r--source/blender/editors/space_node/node_ops.cc16
-rw-r--r--source/blender/editors/space_node/node_relationships.cc600
-rw-r--r--source/blender/editors/space_node/node_select.cc37
-rw-r--r--source/blender/editors/space_node/node_templates.cc2
-rw-r--r--source/blender/editors/space_node/node_view.cc30
-rw-r--r--source/blender/editors/space_node/space_node.cc135
18 files changed, 1866 insertions, 603 deletions
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index 8a1d47eaa8d..ff9e5352d0a 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -5,6 +5,7 @@ set(INC
../../blenfont
../../blenkernel
../../blenlib
+ ../../blenloader
../../blentranslation
../../compositor
../../depsgraph
@@ -18,12 +19,17 @@ set(INC
../../render
../../windowmanager
../../../../intern/guardedalloc
+
+ # dna_type_offsets.h
+ ${CMAKE_CURRENT_BINARY_DIR}/../../makesdna/intern
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
set(SRC
+ add_node_search.cc
+ add_menu_assets.cc
drawnode.cc
link_drag_search.cc
node_add.cc
@@ -79,5 +85,5 @@ endif()
blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
-# RNA_prototypes.h
+# RNA_prototypes.h dna_type_offsets.h
add_dependencies(bf_editor_space_node bf_rna)
diff --git a/source/blender/editors/space_node/add_menu_assets.cc b/source/blender/editors/space_node/add_menu_assets.cc
new file mode 100644
index 00000000000..4bb98124a3f
--- /dev/null
+++ b/source/blender/editors/space_node/add_menu_assets.cc
@@ -0,0 +1,316 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_multi_value_map.hh"
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_asset.h"
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_library.hh"
+#include "BKE_idprop.h"
+#include "BKE_screen.h"
+
+#include "BLT_translation.h"
+
+#include "RNA_access.h"
+#include "RNA_prototypes.h"
+
+#include "ED_asset.h"
+#include "ED_screen.h"
+
+#include "node_intern.hh"
+
+namespace blender::ed::space_node {
+
+static bool node_add_menu_poll(const bContext *C, MenuType * /*mt*/)
+{
+ return CTX_wm_space_node(C);
+}
+
+static void node_add_menu_assets_listen_fn(const wmRegionListenerParams *params)
+{
+ const wmNotifier *wmn = params->notifier;
+ ARegion *region = params->region;
+
+ switch (wmn->category) {
+ case NC_ASSET:
+ if (wmn->data == ND_ASSET_LIST_READING) {
+ ED_region_tag_refresh_ui(region);
+ }
+ break;
+ }
+}
+
+struct LibraryAsset {
+ AssetLibraryReference library_ref;
+ AssetHandle handle;
+};
+
+struct LibraryCatalog {
+ bke::AssetLibrary *library;
+ const bke::AssetCatalog *catalog;
+};
+
+struct AssetItemTree {
+ bke::AssetCatalogTree catalogs;
+ MultiValueMap<bke::AssetCatalogPath, LibraryAsset> assets_per_path;
+ Map<const bke::AssetCatalogTreeItem *, bke::AssetCatalogPath> full_catalog_per_tree_item;
+};
+
+static bool all_loading_finished()
+{
+ for (const AssetLibraryReference &library : bke::all_valid_asset_library_refs()) {
+ if (!ED_assetlist_is_loaded(&library)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree *node_tree)
+{
+ if (!node_tree) {
+ return {};
+ }
+ const Main &bmain = *CTX_data_main(&C);
+ const Vector<AssetLibraryReference> all_libraries = bke::all_valid_asset_library_refs();
+
+ /* Merge catalogs from all libraries to deduplicate menu items. Also store the catalog and
+ * library for each asset ID in order to use them later when retrieving assets and removing
+ * empty catalogs. */
+ Map<bke::CatalogID, LibraryCatalog> id_to_catalog_map;
+ bke::AssetCatalogTree catalogs_from_all_libraries;
+ for (const AssetLibraryReference &library_ref : all_libraries) {
+ if (bke::AssetLibrary *library = BKE_asset_library_load(&bmain, library_ref)) {
+ if (bke::AssetCatalogTree *tree = library->catalog_service->get_catalog_tree()) {
+ tree->foreach_item([&](bke::AssetCatalogTreeItem &item) {
+ const bke::CatalogID &id = item.get_catalog_id();
+ bke::AssetCatalog *catalog = library->catalog_service->find_catalog(id);
+ catalogs_from_all_libraries.insert_item(*catalog);
+ id_to_catalog_map.add(item.get_catalog_id(), LibraryCatalog{library, catalog});
+ });
+ }
+ }
+ }
+
+ /* Find all the matching node group assets for every catalog path. */
+ MultiValueMap<bke::AssetCatalogPath, LibraryAsset> assets_per_path;
+ for (const AssetLibraryReference &library_ref : all_libraries) {
+ AssetFilterSettings type_filter{};
+ type_filter.id_types = FILTER_ID_NT;
+
+ ED_assetlist_storage_fetch(&library_ref, &C);
+ ED_assetlist_iterate(library_ref, [&](AssetHandle asset) {
+ if (!ED_asset_filter_matches_asset(&type_filter, &asset)) {
+ return true;
+ }
+ const AssetMetaData &meta_data = *ED_asset_handle_get_metadata(&asset);
+ const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
+ if (tree_type == nullptr || IDP_Int(tree_type) != node_tree->type) {
+ return true;
+ }
+ if (BLI_uuid_is_nil(meta_data.catalog_id)) {
+ return true;
+ }
+ const LibraryCatalog *library_catalog = id_to_catalog_map.lookup_ptr(meta_data.catalog_id);
+ if (library_catalog == nullptr) {
+ return true;
+ }
+ assets_per_path.add(library_catalog->catalog->path, LibraryAsset{library_ref, asset});
+ return true;
+ });
+ }
+
+ /* Build the final tree without any of the catalogs that don't have proper node group assets. */
+ bke::AssetCatalogTree catalogs_with_node_assets;
+ catalogs_from_all_libraries.foreach_item([&](bke::AssetCatalogTreeItem &item) {
+ if (!assets_per_path.lookup(item.catalog_path()).is_empty()) {
+ const bke::CatalogID &id = item.get_catalog_id();
+ const LibraryCatalog &library_catalog = id_to_catalog_map.lookup(id);
+ bke::AssetCatalog *catalog = library_catalog.library->catalog_service->find_catalog(id);
+ catalogs_with_node_assets.insert_item(*catalog);
+ }
+ });
+
+ /* Build another map storing full asset paths for each tree item, in order to have stable
+ * pointers to asset catalog paths to use for context pointers. This is necessary because
+ * #bke::AssetCatalogTreeItem doesn't store its full path directly. */
+ Map<const bke::AssetCatalogTreeItem *, bke::AssetCatalogPath> full_catalog_per_tree_item;
+ catalogs_with_node_assets.foreach_item([&](bke::AssetCatalogTreeItem &item) {
+ full_catalog_per_tree_item.add_new(&item, item.catalog_path());
+ });
+
+ return {std::move(catalogs_with_node_assets),
+ std::move(assets_per_path),
+ std::move(full_catalog_per_tree_item)};
+}
+
+static void node_add_catalog_assets_draw(const bContext *C, Menu *menu)
+{
+ bScreen &screen = *CTX_wm_screen(C);
+ const SpaceNode &snode = *CTX_wm_space_node(C);
+ if (!snode.runtime->assets_for_menu) {
+ BLI_assert_unreachable();
+ return;
+ }
+ AssetItemTree &tree = *snode.runtime->assets_for_menu;
+ const bNodeTree *edit_tree = snode.edittree;
+ if (!edit_tree) {
+ return;
+ }
+
+ const PointerRNA menu_path_ptr = CTX_data_pointer_get(C, "asset_catalog_path");
+ if (RNA_pointer_is_null(&menu_path_ptr)) {
+ return;
+ }
+ const bke::AssetCatalogPath &menu_path = *static_cast<const bke::AssetCatalogPath *>(
+ menu_path_ptr.data);
+
+ const Span<LibraryAsset> asset_items = tree.assets_per_path.lookup(menu_path);
+ bke::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(menu_path);
+ BLI_assert(catalog_item != nullptr);
+
+ if (asset_items.is_empty() && !catalog_item->has_children()) {
+ return;
+ }
+
+ uiLayout *layout = menu->layout;
+ uiItemS(layout);
+
+ for (const LibraryAsset &item : asset_items) {
+ uiLayout *col = uiLayoutColumn(layout, false);
+ PointerRNA file{
+ &screen.id, &RNA_FileSelectEntry, const_cast<FileDirEntry *>(item.handle.file_data)};
+ uiLayoutSetContextPointer(col, "active_file", &file);
+
+ PointerRNA library_ptr{&screen.id,
+ &RNA_AssetLibraryReference,
+ const_cast<AssetLibraryReference *>(&item.library_ref)};
+ uiLayoutSetContextPointer(col, "asset_library_ref", &library_ptr);
+
+ uiItemO(col, ED_asset_handle_get_name(&item.handle), ICON_NONE, "NODE_OT_add_group_asset");
+ }
+
+ catalog_item->foreach_child([&](bke::AssetCatalogTreeItem &child_item) {
+ const bke::AssetCatalogPath &path = tree.full_catalog_per_tree_item.lookup(&child_item);
+ PointerRNA path_ptr{
+ &screen.id, &RNA_AssetCatalogPath, const_cast<bke::AssetCatalogPath *>(&path)};
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
+ uiItemM(col, "NODE_MT_node_add_catalog_assets", path.name().c_str(), ICON_NONE);
+ });
+}
+
+static void add_root_catalogs_draw(const bContext *C, Menu *menu)
+{
+ bScreen &screen = *CTX_wm_screen(C);
+ SpaceNode &snode = *CTX_wm_space_node(C);
+ const bNodeTree *edit_tree = snode.edittree;
+ uiLayout *layout = menu->layout;
+
+ snode.runtime->assets_for_menu = std::make_shared<AssetItemTree>(
+ build_catalog_tree(*C, edit_tree));
+
+ const bool loading_finished = all_loading_finished();
+
+ AssetItemTree &tree = *snode.runtime->assets_for_menu;
+ if (tree.catalogs.is_empty() && loading_finished) {
+ return;
+ }
+
+ uiItemS(layout);
+
+ if (!loading_finished) {
+ uiItemL(layout, IFACE_("Loading Asset Libraries"), ICON_INFO);
+ }
+
+ /* Avoid adding a separate root catalog when the assets have already been added to one of the
+ * builtin menus.
+ * TODO: The need to define the builtin menu labels here is completely non-ideal. We don't have
+ * any UI introspection that can do this though. This can be solved in the near future by
+ * removing the need to define the add menu completely, instead using a per-node-type path which
+ * can be merged with catalog tree.
+ */
+ static Set<std::string> all_builtin_menus = []() {
+ Set<std::string> menus;
+ menus.add_new("Attribute");
+ menus.add_new("Color");
+ menus.add_new("Curve");
+ menus.add_new("Curve Primitives");
+ menus.add_new("Curve Topology");
+ menus.add_new("Geometry");
+ menus.add_new("Input");
+ menus.add_new("Instances");
+ menus.add_new("Material");
+ menus.add_new("Mesh");
+ menus.add_new("Mesh Primitives");
+ menus.add_new("Mesh Topology");
+ menus.add_new("Output");
+ menus.add_new("Point");
+ menus.add_new("Text");
+ menus.add_new("Texture");
+ menus.add_new("Utilities");
+ menus.add_new("UV");
+ menus.add_new("Vector");
+ menus.add_new("Volume");
+ menus.add_new("Group");
+ menus.add_new("Layout");
+ return menus;
+ }();
+
+ tree.catalogs.foreach_root_item([&](bke::AssetCatalogTreeItem &item) {
+ if (all_builtin_menus.contains(item.get_name())) {
+ return;
+ }
+ const bke::AssetCatalogPath &path = tree.full_catalog_per_tree_item.lookup(&item);
+ PointerRNA path_ptr{
+ &screen.id, &RNA_AssetCatalogPath, const_cast<bke::AssetCatalogPath *>(&path)};
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
+ uiItemM(col, "NODE_MT_node_add_catalog_assets", path.name().c_str(), ICON_NONE);
+ });
+}
+
+MenuType add_catalog_assets_menu_type()
+{
+ MenuType type{};
+ BLI_strncpy(type.idname, "NODE_MT_node_add_catalog_assets", sizeof(type.idname));
+ type.poll = node_add_menu_poll;
+ type.draw = node_add_catalog_assets_draw;
+ type.listener = node_add_menu_assets_listen_fn;
+ return type;
+}
+
+MenuType add_root_catalogs_menu_type()
+{
+ MenuType type{};
+ BLI_strncpy(type.idname, "NODE_MT_node_add_root_catalogs", sizeof(type.idname));
+ type.poll = node_add_menu_poll;
+ type.draw = add_root_catalogs_draw;
+ type.listener = node_add_menu_assets_listen_fn;
+ return type;
+}
+
+} // namespace blender::ed::space_node
+
+/* Note: This is only necessary because Python can't set an asset catalog path context item. */
+void uiTemplateNodeAssetMenuItems(uiLayout *layout, bContext *C, const char *catalog_path)
+{
+ using namespace blender;
+ using namespace blender::ed::space_node;
+ bScreen &screen = *CTX_wm_screen(C);
+ SpaceNode &snode = *CTX_wm_space_node(C);
+ AssetItemTree &tree = *snode.runtime->assets_for_menu;
+ const bke::AssetCatalogTreeItem *item = tree.catalogs.find_root_item(catalog_path);
+ if (!item) {
+ return;
+ }
+ const bke::AssetCatalogPath &path = tree.full_catalog_per_tree_item.lookup(item);
+ PointerRNA path_ptr{
+ &screen.id, &RNA_AssetCatalogPath, const_cast<bke::AssetCatalogPath *>(&path)};
+ uiItemS(layout);
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
+ uiItemMContents(col, "NODE_MT_node_add_catalog_assets");
+}
diff --git a/source/blender/editors/space_node/add_node_search.cc b/source/blender/editors/space_node/add_node_search.cc
new file mode 100644
index 00000000000..23ec2f2f66e
--- /dev/null
+++ b/source/blender/editors/space_node/add_node_search.cc
@@ -0,0 +1,358 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <optional>
+
+#include "BLI_listbase.h"
+#include "BLI_string_search.h"
+
+#include "DNA_space_types.h"
+
+#include "BKE_asset.h"
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_library.hh"
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_node_tree_update.h"
+#include "BKE_screen.h"
+
+#include "DEG_depsgraph_build.h"
+
+#include "BLT_translation.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+
+#include "ED_asset.h"
+#include "ED_node.h"
+
+#include "node_intern.hh"
+
+struct bContext;
+
+namespace blender::ed::space_node {
+
+struct AddNodeItem {
+ std::string ui_name;
+ std::string identifier;
+ std::string description;
+ std::optional<AssetHandle> asset;
+ std::function<void(const bContext &, bNodeTree &, bNode &)> after_add_fn;
+ int weight = 0;
+};
+
+struct AddNodeSearchStorage {
+ float2 cursor;
+ bool use_transform;
+ Vector<AddNodeItem> search_add_items;
+ char search[256];
+ bool update_items_tag = true;
+};
+
+static void add_node_search_listen_fn(const wmRegionListenerParams *params, void *arg)
+{
+ AddNodeSearchStorage &storage = *static_cast<AddNodeSearchStorage *>(arg);
+ const wmNotifier *wmn = params->notifier;
+
+ switch (wmn->category) {
+ case NC_ASSET:
+ if (wmn->data == ND_ASSET_LIST_READING) {
+ storage.update_items_tag = true;
+ }
+ break;
+ }
+}
+
+static void search_items_for_asset_metadata(const bNodeTree &node_tree,
+ const AssetLibraryReference &library_ref,
+ const AssetHandle asset,
+ Vector<AddNodeItem> &search_items)
+{
+ const AssetMetaData &asset_data = *ED_asset_handle_get_metadata(&asset);
+ const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&asset_data, "type");
+ if (tree_type == nullptr || IDP_Int(tree_type) != node_tree.type) {
+ return;
+ }
+
+ AddNodeItem item{};
+ item.ui_name = ED_asset_handle_get_name(&asset);
+ item.identifier = node_tree.typeinfo->group_idname;
+ item.description = asset_data.description == nullptr ? "" : asset_data.description;
+ item.asset = asset;
+ item.after_add_fn = [asset, library_ref](const bContext &C, bNodeTree &node_tree, bNode &node) {
+ Main &bmain = *CTX_data_main(&C);
+ node.flag &= ~NODE_OPTIONS;
+ node.id = asset::get_local_id_from_asset_or_append_and_reuse(bmain, library_ref, asset);
+ 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));
+}
+
+static void gather_search_items_for_asset_library(const bContext &C,
+ const bNodeTree &node_tree,
+ const AssetLibraryReference &library_ref,
+ Set<std::string> &r_added_assets,
+ Vector<AddNodeItem> &search_items)
+{
+ AssetFilterSettings filter_settings{};
+ filter_settings.id_types = FILTER_ID_NT;
+
+ ED_assetlist_storage_fetch(&library_ref, &C);
+ ED_assetlist_iterate(library_ref, [&](AssetHandle asset) {
+ if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) {
+ return true;
+ }
+ if (!r_added_assets.add(ED_asset_handle_get_name(&asset))) {
+ /* If an asset with the same name has already been added, skip this. */
+ return true;
+ }
+ search_items_for_asset_metadata(node_tree, library_ref, asset, search_items);
+ return true;
+ });
+}
+
+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)
+{
+ int i;
+ LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) {
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = i;
+ library_ref.type = ASSET_LIBRARY_CUSTOM;
+ /* Skip local assets to avoid duplicates when the asset is part of the local file library. */
+ gather_search_items_for_asset_library(C, node_tree, library_ref, r_added_assets, search_items);
+ }
+
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = -1;
+ library_ref.type = ASSET_LIBRARY_LOCAL;
+ gather_search_items_for_asset_library(C, node_tree, library_ref, r_added_assets, search_items);
+}
+
+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)
+{
+ const StringRef group_node_id = node_tree.typeinfo->group_idname;
+
+ Main &bmain = *CTX_data_main(&C);
+ LISTBASE_FOREACH (bNodeTree *, node_group, &bmain.nodetrees) {
+ if (node_group->typeinfo->group_idname != group_node_id) {
+ continue;
+ }
+ if (local_assets.contains(node_group->id.name)) {
+ continue;
+ }
+ if (!nodeGroupPoll(&node_tree, node_group, nullptr)) {
+ continue;
+ }
+ AddNodeItem item{};
+ item.ui_name = node_group->id.name + 2;
+ item.identifier = node_tree.typeinfo->group_idname;
+ item.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));
+ }
+}
+
+static void gather_add_node_operations(const bContext &C,
+ bNodeTree &node_tree,
+ Vector<AddNodeItem> &r_search_items)
+{
+ NODE_TYPES_BEGIN (node_type) {
+ const char *disabled_hint;
+ if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) {
+ continue;
+ }
+ if ((StringRefNull(node_tree.typeinfo->group_idname) == node_type->idname)) {
+ /* Skip the empty group type. */
+ continue;
+ }
+ if (StringRefNull(node_type->ui_name).endswith("(Legacy)")) {
+ continue;
+ }
+
+ AddNodeItem item{};
+ item.ui_name = IFACE_(node_type->ui_name);
+ item.identifier = node_type->idname;
+ item.description = TIP_(node_type->ui_description);
+ r_search_items.append(std::move(item));
+ }
+ NODE_TYPES_END;
+
+ /* Use a set to avoid adding items for node groups that are also assets. Using data-block
+ * names is a crutch, since different assets may have the same name. However, an alternative
+ * using #ED_asset_handle_get_local_id didn't work in this case. */
+ Set<std::string> added_assets;
+ gather_search_items_for_all_assets(C, node_tree, added_assets, r_search_items);
+ gather_search_items_for_node_groups(C, node_tree, added_assets, r_search_items);
+}
+
+static void add_node_search_update_fn(
+ const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
+{
+ AddNodeSearchStorage &storage = *static_cast<AddNodeSearchStorage *>(arg);
+ if (storage.update_items_tag) {
+ bNodeTree *node_tree = CTX_wm_space_node(C)->edittree;
+ storage.search_add_items.clear();
+ gather_add_node_operations(*C, *node_tree, storage.search_add_items);
+ storage.update_items_tag = false;
+ }
+
+ StringSearch *search = BLI_string_search_new();
+
+ for (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;
+ 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.ui_name.c_str(), &item, ICON_NONE, 0, 0)) {
+ break;
+ }
+ }
+
+ MEM_freeN(filtered_items);
+ BLI_string_search_free(search);
+}
+
+static void add_node_search_exec_fn(bContext *C, void *arg1, void *arg2)
+{
+ Main &bmain = *CTX_data_main(C);
+ SpaceNode &snode = *CTX_wm_space_node(C);
+ bNodeTree &node_tree = *snode.edittree;
+ AddNodeSearchStorage &storage = *static_cast<AddNodeSearchStorage *>(arg1);
+ AddNodeItem *item = static_cast<AddNodeItem *>(arg2);
+ if (item == nullptr) {
+ return;
+ }
+
+ node_deselect_all(snode);
+ bNode *new_node = nodeAddNode(C, &node_tree, item->identifier.c_str());
+ BLI_assert(new_node != nullptr);
+
+ if (item->after_add_fn) {
+ item->after_add_fn(*C, node_tree, *new_node);
+ }
+
+ new_node->locx = storage.cursor.x / UI_DPI_FAC;
+ new_node->locy = storage.cursor.y / UI_DPI_FAC + 20 * UI_DPI_FAC;
+
+ nodeSetSelected(new_node, true);
+ nodeSetActive(&node_tree, new_node);
+
+ /* 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. */
+ ED_node_tree_propagate_change(C, &bmain, &node_tree);
+
+ if (storage.use_transform) {
+ wmOperatorType *ot = WM_operatortype_find("NODE_OT_translate_attach_remove_on_cancel", true);
+ BLI_assert(ot);
+ PointerRNA ptr;
+ WM_operator_properties_create_ptr(&ptr, ot);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
+ WM_operator_properties_free(&ptr);
+ }
+}
+
+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);
+
+ uiSearchItemTooltipData tooltip_data{};
+
+ BLI_strncpy(tooltip_data.description,
+ item->asset ? item->description.c_str() : TIP_(item->description.c_str()),
+ sizeof(tooltip_data.description));
+
+ return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data);
+}
+
+static void add_node_search_free_fn(void *arg)
+{
+ AddNodeSearchStorage *storage = static_cast<AddNodeSearchStorage *>(arg);
+ delete storage;
+}
+
+static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *arg_op)
+{
+ AddNodeSearchStorage &storage = *(AddNodeSearchStorage *)arg_op;
+
+ uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
+ UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
+ UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
+
+ uiBut *but = uiDefSearchBut(block,
+ storage.search,
+ 0,
+ ICON_VIEWZOOM,
+ sizeof(storage.search),
+ 10,
+ 10,
+ UI_searchbox_size_x(),
+ UI_UNIT_Y,
+ 0,
+ 0,
+ "");
+ UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
+ UI_but_func_search_set(but,
+ nullptr,
+ add_node_search_update_fn,
+ &storage,
+ false,
+ add_node_search_free_fn,
+ add_node_search_exec_fn,
+ nullptr);
+ UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
+ UI_but_func_search_set_tooltip(but, add_node_search_tooltip_fn);
+ UI_but_func_search_set_listen(but, add_node_search_listen_fn);
+
+ /* Fake button to hold space for the search items. */
+ uiDefBut(block,
+ UI_BTYPE_LABEL,
+ 0,
+ "",
+ 10,
+ 10 - UI_searchbox_size_y(),
+ UI_searchbox_size_x(),
+ UI_searchbox_size_y(),
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+
+ const int offset[2] = {0, -UI_UNIT_Y};
+ UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, offset);
+ return block;
+}
+
+void invoke_add_node_search_menu(bContext &C, const float2 &cursor, const bool use_transform)
+{
+ AddNodeSearchStorage *storage = new AddNodeSearchStorage{cursor, use_transform};
+ /* Use the "_ex" variant with `can_refresh` false to avoid a double free when closing Blender. */
+ UI_popup_block_invoke_ex(&C, create_search_popup_block, storage, nullptr, false);
+}
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index fbbdd40e92e..c66b8ad4ff0 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -73,10 +73,10 @@ namespace blender::ed::space_node {
/* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */
-static void node_socket_button_label(bContext *UNUSED(C),
+static void node_socket_button_label(bContext * /*C*/,
uiLayout *layout,
- PointerRNA *UNUSED(ptr),
- PointerRNA *UNUSED(node_ptr),
+ PointerRNA * /*ptr*/,
+ PointerRNA * /*node_ptr*/,
const char *text)
{
uiItemL(layout, text, 0);
@@ -84,7 +84,7 @@ static void node_socket_button_label(bContext *UNUSED(C),
/* ****************** BUTTON CALLBACKS FOR ALL TREES ***************** */
-static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_value(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
/* first output stores value */
@@ -95,7 +95,7 @@ static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *p
uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE);
}
-static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_rgb(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
/* first output stores value */
@@ -109,7 +109,7 @@ static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr
uiItemR(col, &sockptr, "default_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE);
}
-static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_mix_rgb(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
@@ -123,7 +123,7 @@ static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
-static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_time(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiTemplateCurveMapping(layout, ptr, "curve", 's', false, false, false, false);
@@ -132,17 +132,17 @@ static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt
uiItemR(col, ptr, "frame_end", DEFAULT_FLAGS, IFACE_("End"), ICON_NONE);
}
-static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_colorramp(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiTemplateColorRamp(layout, ptr, "color_ramp", false);
}
-static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_curvevec(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false);
}
-static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_curvefloat(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiTemplateCurveMapping(layout, ptr, "mapping", 0, false, false, false, false);
}
@@ -164,7 +164,7 @@ void ED_node_sample_set(const float col[4])
namespace blender::ed::space_node {
-static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_curvecol(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
CurveMapping *cumap = (CurveMapping *)node->storage;
@@ -183,7 +183,7 @@ static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA
layout, ptr, "mapping", 'c', false, false, false, (ntree->type == NTREE_COMPOSIT));
}
-static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_normal(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
/* first output stores normal */
@@ -194,7 +194,7 @@ static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *
uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE);
}
-static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_texture(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
@@ -209,13 +209,13 @@ static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA
}
}
-static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_math(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
-static void node_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_combsep_color(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE);
}
@@ -283,7 +283,7 @@ static void node_draw_buttons_group(uiLayout *layout, bContext *C, PointerRNA *p
layout, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr);
}
-static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_frame_ex(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "label_size", DEFAULT_FLAGS, IFACE_("Label Size"), ICON_NONE);
uiItemR(layout, ptr, "shrink", DEFAULT_FLAGS, IFACE_("Shrink"), ICON_NONE);
@@ -441,17 +441,17 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P
uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, IFACE_("Projection"), ICON_NONE);
}
-static void node_shader_buts_displacement(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_shader_buts_displacement(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", 0);
}
-static void node_shader_buts_glossy(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_shader_buts_glossy(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE);
}
-static void node_buts_output_shader(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_buts_output_shader(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "target", DEFAULT_FLAGS, "", ICON_NONE);
}
@@ -519,7 +519,7 @@ static void node_shader_set_butfunc(bNodeType *ntype)
/* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */
static void node_buts_image_views(uiLayout *layout,
- bContext *UNUSED(C),
+ bContext * /*C*/,
PointerRNA *ptr,
PointerRNA *imaptr)
{
@@ -579,7 +579,7 @@ static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRN
uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, true);
}
-static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_composit_buts_huecorrect(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
CurveMapping *cumap = (CurveMapping *)node->storage;
@@ -595,14 +595,12 @@ static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C),
uiTemplateCurveMapping(layout, ptr, "mapping", 'h', false, false, false, false);
}
-static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_composit_buts_ycc(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE);
}
-static void node_composit_buts_combsep_color(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_composit_buts_combsep_color(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage;
@@ -733,7 +731,7 @@ static void node_composit_backdrop_ellipsemask(
}
static void node_composit_buts_cryptomatte_legacy(uiLayout *layout,
- bContext *UNUSED(C),
+ bContext * /*C*/,
PointerRNA *ptr)
{
uiLayout *col = uiLayoutColumn(layout, true);
@@ -748,8 +746,8 @@ static void node_composit_buts_cryptomatte_legacy(uiLayout *layout,
}
static void node_composit_buts_cryptomatte_legacy_ex(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr))
+ bContext * /*C*/,
+ PointerRNA * /*ptr*/)
{
uiItemO(layout, IFACE_("Add Crypto Layer"), ICON_ADD, "NODE_OT_cryptomatte_layer_add");
uiItemO(layout, IFACE_("Remove Crypto Layer"), ICON_REMOVE, "NODE_OT_cryptomatte_layer_remove");
@@ -874,7 +872,7 @@ static void node_composit_set_butfunc(bNodeType *ntype)
/* ****************** BUTTON CALLBACKS FOR TEXTURE NODES ***************** */
-static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_texture_buts_bricks(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiLayout *col;
@@ -887,7 +885,7 @@ static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemR(col, ptr, "squash_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE);
}
-static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_texture_buts_proc(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
PointerRNA tex_ptr;
bNode *node = (bNode *)ptr->data;
@@ -938,7 +936,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
uiItemR(
row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
- uiLayoutSetActive(row, !(ELEM(tex->stype, TEX_BAND, TEX_RING)));
+ uiLayoutSetActive(row, !ELEM(tex->stype, TEX_BAND, TEX_RING));
uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
break;
@@ -998,12 +996,12 @@ static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA
uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false);
}
-static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_texture_buts_output(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE);
}
-static void node_texture_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_texture_buts_combsep_color(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE);
}
@@ -1068,7 +1066,7 @@ static void node_texture_set_butfunc(bNodeType *ntype)
* Only called on node initialization, once.
* \{ */
-static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+static void node_property_update_default(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
@@ -1102,18 +1100,18 @@ static void node_template_properties_update(bNodeType *ntype)
}
}
-static void node_socket_undefined_draw(bContext *UNUSED(C),
+static void node_socket_undefined_draw(bContext * /*C*/,
uiLayout *layout,
- PointerRNA *UNUSED(ptr),
- PointerRNA *UNUSED(node_ptr),
- const char *UNUSED(text))
+ PointerRNA * /*ptr*/,
+ PointerRNA * /*node_ptr*/,
+ const char * /*text*/)
{
uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR);
}
-static void node_socket_undefined_draw_color(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
- PointerRNA *UNUSED(node_ptr),
+static void node_socket_undefined_draw_color(bContext * /*C*/,
+ PointerRNA * /*ptr*/,
+ PointerRNA * /*node_ptr*/,
float *r_color)
{
r_color[0] = 1.0f;
@@ -1122,15 +1120,15 @@ static void node_socket_undefined_draw_color(bContext *UNUSED(C),
r_color[3] = 1.0f;
}
-static void node_socket_undefined_interface_draw(bContext *UNUSED(C),
+static void node_socket_undefined_interface_draw(bContext * /*C*/,
uiLayout *layout,
- PointerRNA *UNUSED(ptr))
+ PointerRNA * /*ptr*/)
{
uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR);
}
-static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
+static void node_socket_undefined_interface_draw_color(bContext * /*C*/,
+ PointerRNA * /*ptr*/,
float *r_color)
{
r_color[0] = 1.0f;
@@ -1173,7 +1171,7 @@ void ED_node_init_butfuncs()
NODE_TYPES_END;
}
-void ED_init_custom_node_type(bNodeType *UNUSED(ntype))
+void ED_init_custom_node_type(bNodeType * /*ntype*/)
{
}
@@ -1205,18 +1203,16 @@ static const float std_node_socket_colors[][4] = {
};
/* common color callbacks for standard types */
-static void std_node_socket_draw_color(bContext *UNUSED(C),
+static void std_node_socket_draw_color(bContext * /*C*/,
PointerRNA *ptr,
- PointerRNA *UNUSED(node_ptr),
+ PointerRNA * /*node_ptr*/,
float *r_color)
{
bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
copy_v4_v4(r_color, std_node_socket_colors[type]);
}
-static void std_node_socket_interface_draw_color(bContext *UNUSED(C),
- PointerRNA *ptr,
- float *r_color)
+static void std_node_socket_interface_draw_color(bContext * /*C*/, PointerRNA *ptr, float *r_color)
{
bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
@@ -1425,7 +1421,7 @@ static void std_node_socket_draw(
}
}
-static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout, PointerRNA *ptr)
+static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, PointerRNA *ptr)
{
bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
@@ -1469,9 +1465,9 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0);
}
-static void node_socket_virtual_draw_color(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
- PointerRNA *UNUSED(node_ptr),
+static void node_socket_virtual_draw_color(bContext * /*C*/,
+ PointerRNA * /*ptr*/,
+ PointerRNA * /*node_ptr*/,
float *r_color)
{
copy_v4_v4(r_color, virtual_node_socket_color);
@@ -1585,10 +1581,12 @@ void draw_nodespace_back_pix(const bContext &C,
GPU_matrix_pop();
}
-static float2 socket_link_connection_location(const bNodeSocket &socket, const bNodeLink &link)
+static float2 socket_link_connection_location(const bNode &node,
+ const bNodeSocket &socket,
+ const bNodeLink &link)
{
const float2 socket_location(socket.locx, socket.locy);
- if (socket.flag & SOCK_MULTI_INPUT && socket.in_out == SOCK_IN) {
+ if (socket.is_multi_input() && socket.is_input() && !(node.flag & NODE_HIDDEN)) {
return node_link_calculate_multi_input_position(
socket_location, link.multi_input_socket_index, socket.total_inputs);
}
@@ -1604,12 +1602,19 @@ static void calculate_inner_link_bezier_points(std::array<float2, 4> &points)
points[2] = math::interpolate(points[0], points[3], 2.0f / 3.0f);
}
else {
- const float dist = curving * 0.1f * math::distance(points[0].x, points[3].x);
+ const float dist_x = math::distance(points[0].x, points[3].x);
+ const float dist_y = math::distance(points[0].y, points[3].y);
- points[1].x = points[0].x + dist;
+ /* Reduce the handle offset when the link endpoints are close to horizontal. */
+ const float slope = safe_divide(dist_y, dist_x);
+ const float clamp_factor = math::min(1.0f, slope * (4.5f - 0.25f * float(curving)));
+
+ const float handle_offset = curving * 0.1f * dist_x * clamp_factor;
+
+ points[1].x = points[0].x + handle_offset;
points[1].y = points[0].y;
- points[2].x = points[3].x - dist;
+ points[2].x = points[3].x - handle_offset;
points[2].y = points[3].y;
}
}
@@ -1617,8 +1622,8 @@ static void calculate_inner_link_bezier_points(std::array<float2, 4> &points)
static std::array<float2, 4> node_link_bezier_points(const bNodeLink &link)
{
std::array<float2, 4> points;
- points[0] = socket_link_connection_location(*link.fromsock, link);
- points[3] = socket_link_connection_location(*link.tosock, link);
+ points[0] = socket_link_connection_location(*link.fromnode, *link.fromsock, link);
+ points[3] = socket_link_connection_location(*link.tonode, *link.tosock, link);
calculate_inner_link_bezier_points(points);
return points;
}
@@ -1757,7 +1762,7 @@ static void nodelink_batch_init()
/* curve strip */
for (int i = 0; i < LINK_RESOL; i++) {
- uv[0] = 255 * (i / (float)(LINK_RESOL - 1));
+ uv[0] = 255 * (i / float(LINK_RESOL - 1));
uv[1] = 0;
set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
uv[1] = 255;
@@ -1901,7 +1906,7 @@ static void nodelink_batch_draw(const SpaceNode &snode)
GPU_blend(GPU_BLEND_NONE);
}
-void nodelink_batch_start(SpaceNode &UNUSED(snode))
+void nodelink_batch_start(SpaceNode & /*snode*/)
{
g_batch_link.enabled = true;
}
@@ -1976,7 +1981,7 @@ static void node_draw_link_end_marker(const float2 center,
UI_draw_roundbox_corner_set(UI_CNR_ALL);
UI_draw_roundbox_4fv(&rect, true, radius, color);
- /* Roundbox disables alpha. Reenable it for node links that are drawn after this one. */
+ /* Round-box disables alpha. Re-enable it for node links that are drawn after this one. */
GPU_blend(GPU_BLEND_ALPHA);
}
@@ -2209,8 +2214,11 @@ static std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &sn
{
const float2 cursor = snode.runtime->cursor * UI_DPI_FAC;
std::array<float2, 4> points;
- points[0] = link.fromsock ? socket_link_connection_location(*link.fromsock, link) : cursor;
- points[3] = link.tosock ? socket_link_connection_location(*link.tosock, link) : cursor;
+ points[0] = link.fromsock ?
+ socket_link_connection_location(*link.fromnode, *link.fromsock, link) :
+ cursor;
+ points[3] = link.tosock ? socket_link_connection_location(*link.tonode, *link.tosock, link) :
+ cursor;
calculate_inner_link_bezier_points(points);
return points;
}
diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc
index f1387da97b5..b2dfbd47aeb 100644
--- a/source/blender/editors/space_node/link_drag_search.cc
+++ b/source/blender/editors/space_node/link_drag_search.cc
@@ -5,7 +5,12 @@
#include "DNA_space_types.h"
+#include "BKE_asset.h"
#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_lib_id.h"
+#include "BKE_node_tree_update.h"
+#include "BKE_screen.h"
#include "NOD_socket_search_link.hh"
@@ -15,6 +20,9 @@
#include "WM_api.h"
+#include "DEG_depsgraph_build.h"
+
+#include "ED_asset.h"
#include "ED_node.h"
#include "node_intern.hh"
@@ -29,6 +37,7 @@ struct LinkDragSearchStorage {
float2 cursor;
Vector<SocketLinkOperation> search_link_ops;
char search[256];
+ bool update_items_tag = true;
eNodeSocketInOut in_out() const
{
@@ -36,6 +45,20 @@ struct LinkDragSearchStorage {
}
};
+static void link_drag_search_listen_fn(const wmRegionListenerParams *params, void *arg)
+{
+ LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg);
+ const wmNotifier *wmn = params->notifier;
+
+ switch (wmn->category) {
+ case NC_ASSET:
+ if (wmn->data == ND_ASSET_LIST_READING) {
+ storage.update_items_tag = true;
+ }
+ break;
+ }
+}
+
static void add_reroute_node_fn(nodes::LinkSearchOpParams &params)
{
bNode &reroute = params.add_node("NodeReroute");
@@ -112,11 +135,136 @@ static void add_existing_group_input_fn(nodes::LinkSearchOpParams &params,
}
/**
+ * \note This could use #search_link_ops_for_socket_templates, but we have to store the inputs and
+ * outputs as IDProperties for assets because of asset indexing, so that's all we have without
+ * loading the file.
+ */
+static void search_link_ops_for_asset_metadata(const bNodeTree &node_tree,
+ const bNodeSocket &socket,
+ const AssetLibraryReference &library_ref,
+ const AssetHandle asset,
+ Vector<SocketLinkOperation> &search_link_ops)
+{
+ const AssetMetaData &asset_data = *ED_asset_handle_get_metadata(&asset);
+ const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&asset_data, "type");
+ if (tree_type == nullptr || IDP_Int(tree_type) != node_tree.type) {
+ return;
+ }
+
+ const bNodeTreeType &node_tree_type = *node_tree.typeinfo;
+ const eNodeSocketInOut in_out = socket.in_out == SOCK_OUT ? SOCK_IN : SOCK_OUT;
+
+ const IDProperty *sockets = BKE_asset_metadata_idprop_find(
+ &asset_data, in_out == SOCK_IN ? "inputs" : "outputs");
+
+ int weight = -1;
+ Set<StringRef> socket_names;
+ LISTBASE_FOREACH (IDProperty *, socket_property, &sockets->data.group) {
+ if (socket_property->type != IDP_STRING) {
+ continue;
+ }
+ const char *socket_idname = IDP_String(socket_property);
+ const bNodeSocketType *socket_type = nodeSocketTypeFind(socket_idname);
+ if (socket_type == nullptr) {
+ continue;
+ }
+ eNodeSocketDatatype from = (eNodeSocketDatatype)socket.type;
+ eNodeSocketDatatype to = (eNodeSocketDatatype)socket_type->type;
+ if (socket.in_out == SOCK_OUT) {
+ std::swap(from, to);
+ }
+ if (node_tree_type.validate_link && !node_tree_type.validate_link(from, to)) {
+ continue;
+ }
+ if (!socket_names.add(socket_property->name)) {
+ /* See comment in #search_link_ops_for_declarations. */
+ continue;
+ }
+
+ const StringRef asset_name = ED_asset_handle_get_name(&asset);
+ const StringRef socket_name = socket_property->name;
+
+ search_link_ops.append(
+ {asset_name + " " + UI_MENU_ARROW_SEP + socket_name,
+ [library_ref, asset, socket_property, in_out](nodes::LinkSearchOpParams &params) {
+ Main &bmain = *CTX_data_main(&params.C);
+
+ bNode &node = params.add_node(params.node_tree.typeinfo->group_idname);
+ node.flag &= ~NODE_OPTIONS;
+
+ node.id = asset::get_local_id_from_asset_or_append_and_reuse(bmain, library_ref, asset);
+ id_us_plus(node.id);
+ BKE_ntree_update_tag_node_property(&params.node_tree, &node);
+ DEG_relations_tag_update(&bmain);
+
+ /* Create the inputs and outputs on the new node. */
+ node.typeinfo->group_update_func(&params.node_tree, &node);
+
+ bNodeSocket *new_node_socket = bke::node_find_enabled_socket(
+ node, in_out, socket_property->name);
+ if (new_node_socket != nullptr) {
+ /* Rely on the way #nodeAddLink switches in/out if necessary. */
+ nodeAddLink(&params.node_tree, &params.node, &params.socket, &node, new_node_socket);
+ }
+ },
+ weight});
+
+ weight--;
+ }
+}
+
+static void gather_search_link_ops_for_asset_library(const bContext &C,
+ const bNodeTree &node_tree,
+ const bNodeSocket &socket,
+ const AssetLibraryReference &library_ref,
+ const bool skip_local,
+ Vector<SocketLinkOperation> &search_link_ops)
+{
+ AssetFilterSettings filter_settings{};
+ filter_settings.id_types = FILTER_ID_NT;
+
+ ED_assetlist_storage_fetch(&library_ref, &C);
+ ED_assetlist_iterate(library_ref, [&](AssetHandle asset) {
+ if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) {
+ return true;
+ }
+ if (skip_local && ED_asset_handle_get_local_id(&asset) != nullptr) {
+ return true;
+ }
+ search_link_ops_for_asset_metadata(node_tree, socket, library_ref, asset, search_link_ops);
+ return true;
+ });
+}
+
+static void gather_search_link_ops_for_all_assets(const bContext &C,
+ const bNodeTree &node_tree,
+ const bNodeSocket &socket,
+ Vector<SocketLinkOperation> &search_link_ops)
+{
+ int i;
+ LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) {
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = i;
+ library_ref.type = ASSET_LIBRARY_CUSTOM;
+ /* Skip local assets to avoid duplicates when the asset is part of the local file library. */
+ gather_search_link_ops_for_asset_library(
+ C, node_tree, socket, library_ref, true, search_link_ops);
+ }
+
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = -1;
+ library_ref.type = ASSET_LIBRARY_LOCAL;
+ gather_search_link_ops_for_asset_library(
+ C, node_tree, socket, library_ref, false, search_link_ops);
+}
+
+/**
* Call the callback to gather compatible socket connections for all node types, and the operations
* that will actually make the connections. Also add some custom operations like connecting a group
* output node.
*/
-static void gather_socket_link_operations(bNodeTree &node_tree,
+static void gather_socket_link_operations(const bContext &C,
+ bNodeTree &node_tree,
const bNodeSocket &socket,
Vector<SocketLinkOperation> &search_link_ops)
{
@@ -125,7 +273,9 @@ static void gather_socket_link_operations(bNodeTree &node_tree,
if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) {
continue;
}
-
+ if (StringRefNull(node_type->ui_name).endswith("(Legacy)")) {
+ continue;
+ }
if (node_type->gather_link_search_ops) {
nodes::GatherLinkSearchOpParams params{*node_type, node_tree, socket, search_link_ops};
node_type->gather_link_search_ops(params);
@@ -156,15 +306,20 @@ static void gather_socket_link_operations(bNodeTree &node_tree,
weight--;
}
}
+
+ gather_search_link_ops_for_all_assets(C, node_tree, socket, search_link_ops);
}
-static void link_drag_search_update_fn(const bContext *UNUSED(C),
- void *arg,
- const char *str,
- uiSearchItems *items,
- const bool is_first)
+static void link_drag_search_update_fn(
+ const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
{
LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg);
+ if (storage.update_items_tag) {
+ bNodeTree *node_tree = CTX_wm_space_node(C)->edittree;
+ storage.search_link_ops.clear();
+ gather_socket_link_operations(*C, *node_tree, storage.from_socket, storage.search_link_ops);
+ storage.update_items_tag = false;
+ }
StringSearch *search = BLI_string_search_new();
@@ -214,7 +369,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
bNode *new_node = new_nodes.first();
new_node->locx = storage.cursor.x / UI_DPI_FAC;
- new_node->locy = storage.cursor.y / UI_DPI_FAC + 20 * UI_DPI_FAC;
+ new_node->locy = storage.cursor.y / UI_DPI_FAC + 20;
if (storage.in_out() == SOCK_IN) {
new_node->locx -= new_node->width;
}
@@ -245,9 +400,6 @@ static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *ar
{
LinkDragSearchStorage &storage = *(LinkDragSearchStorage *)arg_op;
- bNodeTree &node_tree = *CTX_wm_space_node(C)->edittree;
- gather_socket_link_operations(node_tree, storage.from_socket, storage.search_link_ops);
-
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
@@ -265,6 +417,7 @@ static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *ar
0,
"");
UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
+ UI_but_func_search_set_listen(but, link_drag_search_listen_fn);
UI_but_func_search_set(but,
nullptr,
link_drag_search_update_fn,
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index 9949037479e..41f70a6d8cf 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -30,6 +30,7 @@
#include "DEG_depsgraph_build.h"
+#include "ED_asset.h"
#include "ED_node.h" /* own include */
#include "ED_render.h"
#include "ED_screen.h"
@@ -194,15 +195,16 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
}
/* Place the new reroute at the average location of all connected cuts. */
- const float2 loc = std::accumulate(cuts.values().begin(), cuts.values().end(), float2(0)) /
- cuts.size() / UI_DPI_FAC;
- reroute->locx = loc.x;
- reroute->locy = loc.y;
+ const float2 insert_point = std::accumulate(
+ cuts.values().begin(), cuts.values().end(), float2(0)) /
+ cuts.size();
+ reroute->locx = insert_point.x / UI_DPI_FAC;
+ reroute->locy = insert_point.y / UI_DPI_FAC;
/* Attach the reroute node to frame nodes behind it. */
for (const int i : frame_nodes.index_range()) {
bNode *frame_node = frame_nodes.last(i);
- if (BLI_rctf_isect_pt_v(&frame_node->totr, loc)) {
+ if (BLI_rctf_isect_pt_v(&frame_node->totr, insert_point)) {
nodeAttachNode(reroute, frame_node);
break;
}
@@ -243,38 +245,36 @@ void NODE_OT_add_reroute(wmOperatorType *ot)
/** \name Add Node Group Operator
* \{ */
-static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain,
- wmOperator *op,
- bNodeTree *ntree)
+static bool node_group_add_poll(const bNodeTree &node_tree,
+ const bNodeTree &node_group,
+ ReportList &reports)
{
- bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
- WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT));
- if (!node_group) {
- return nullptr;
+ if (node_group.type != node_tree.type) {
+ return false;
}
const char *disabled_hint = nullptr;
- if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) {
+ if (!nodeGroupPoll(&node_tree, &node_group, &disabled_hint)) {
if (disabled_hint) {
- BKE_reportf(op->reports,
+ BKE_reportf(&reports,
RPT_ERROR,
"Can not add node group '%s' to '%s':\n %s",
- node_group->id.name + 2,
- ntree->id.name + 2,
+ node_group.id.name + 2,
+ node_tree.id.name + 2,
disabled_hint);
}
else {
- BKE_reportf(op->reports,
+ BKE_reportf(&reports,
RPT_ERROR,
"Can not add node group '%s' to '%s'",
- node_group->id.name + 2,
- ntree->id.name + 2);
+ node_group.id.name + 2,
+ node_tree.id.name + 2);
}
- return nullptr;
+ return false;
}
- return node_group;
+ return true;
}
static int node_add_group_exec(bContext *C, wmOperator *op)
@@ -283,10 +283,14 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNodeTree *node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree);
+ bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
+ WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT));
if (!node_group) {
return OPERATOR_CANCELLED;
}
+ if (!node_group_add_poll(*ntree, *node_group, *op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
@@ -319,9 +323,8 @@ static bool node_add_group_poll(bContext *C)
}
const SpaceNode *snode = CTX_wm_space_node(C);
if (snode->edittree->type == NTREE_CUSTOM) {
- CTX_wm_operator_poll_msg_set(C,
- "This node editor displays a custom (Python defined) node tree. "
- "Dropping node groups isn't supported for this");
+ CTX_wm_operator_poll_msg_set(
+ C, "Adding node groups isn't supported for custom (Python defined) node trees");
return false;
}
return true;
@@ -366,6 +369,105 @@ void NODE_OT_add_group(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Add Node Group Asset Operator
+ * \{ */
+
+static bool add_node_group_asset(const bContext &C,
+ const AssetLibraryReference &library_ref,
+ const AssetHandle asset,
+ ReportList &reports)
+{
+ Main &bmain = *CTX_data_main(&C);
+ SpaceNode &snode = *CTX_wm_space_node(&C);
+ bNodeTree &edit_tree = *snode.edittree;
+
+ bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
+ asset::get_local_id_from_asset_or_append_and_reuse(bmain, library_ref, asset));
+ if (!node_group) {
+ return false;
+ }
+ if (!node_group_add_poll(edit_tree, *node_group, reports)) {
+ /* Remove the node group if it was newly appended but can't be added to the tree. */
+ id_us_plus(&node_group->id);
+ BKE_id_free_us(&bmain, node_group);
+ return false;
+ }
+
+ ED_preview_kill_jobs(CTX_wm_manager(&C), CTX_data_main(&C));
+
+ bNode *group_node = add_node(
+ C, ntreeTypeFind(node_group->idname)->group_idname, snode.runtime->cursor);
+ if (!group_node) {
+ BKE_report(&reports, RPT_WARNING, "Could not add node group");
+ return false;
+ }
+ /* By default, don't show the data-block selector since it's not usually necessary for assets. */
+ group_node->flag &= ~NODE_OPTIONS;
+
+ group_node->id = &node_group->id;
+ id_us_plus(group_node->id);
+ BKE_ntree_update_tag_node_property(&edit_tree, group_node);
+
+ nodeSetActive(&edit_tree, group_node);
+ ED_node_tree_propagate_change(&C, &bmain, nullptr);
+ DEG_relations_tag_update(&bmain);
+
+ return true;
+}
+
+static int node_add_group_asset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion &region = *CTX_wm_region(C);
+ SpaceNode &snode = *CTX_wm_space_node(C);
+
+ const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
+ if (!library_ref) {
+ return OPERATOR_CANCELLED;
+ }
+ bool is_valid;
+ const AssetHandle handle = CTX_wm_asset_handle(C, &is_valid);
+ if (!is_valid) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Convert mouse coordinates to v2d space. */
+ UI_view2d_region_to_view(&region.v2d,
+ event->mval[0],
+ event->mval[1],
+ &snode.runtime->cursor[0],
+ &snode.runtime->cursor[1]);
+
+ snode.runtime->cursor /= UI_DPI_FAC;
+
+ if (!add_node_group_asset(*C, *library_ref, handle, *op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ wmOperatorType *ot = WM_operatortype_find("NODE_OT_translate_attach_remove_on_cancel", true);
+ BLI_assert(ot);
+ PointerRNA ptr;
+ WM_operator_properties_create_ptr(&ptr, ot);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
+ WM_operator_properties_free(&ptr);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_add_group_asset(wmOperatorType *ot)
+{
+ ot->name = "Add Node Group Asset";
+ ot->description = "Add a node group asset to the active node tree";
+ ot->idname = "NODE_OT_add_group_asset";
+
+ ot->invoke = node_add_group_asset_invoke;
+ ot->poll = node_add_group_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Add Node Object Operator
* \{ */
@@ -392,7 +494,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
bNodeSocket *sock = nodeFindSocket(object_node, SOCK_IN, "Object");
if (!sock) {
- BKE_report(op->reports, RPT_WARNING, "Could not find node object socket");
+ BLI_assert_unreachable();
return OPERATOR_CANCELLED;
}
@@ -777,12 +879,14 @@ static int new_node_tree_exec(bContext *C, wmOperator *op)
ED_node_tree_update(C);
}
+ WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
+
return OPERATOR_FINISHED;
}
-static const EnumPropertyItem *new_node_tree_type_itemf(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
+static const EnumPropertyItem *new_node_tree_type_itemf(bContext * /*C*/,
+ PointerRNA * /*ptr*/,
+ PropertyRNA * /*prop*/,
bool *r_free)
{
return rna_node_tree_type_itemf(nullptr, nullptr, r_free);
@@ -810,4 +914,37 @@ void NODE_OT_new_node_tree(wmOperatorType *ot)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Add Node Search
+ * \{ */
+
+static int node_add_search_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const ARegion &region = *CTX_wm_region(C);
+
+ float2 cursor;
+ UI_view2d_region_to_view(&region.v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
+
+ invoke_add_node_search_menu(*C, cursor, RNA_boolean_get(op->ptr, "use_transform"));
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_add_search(wmOperatorType *ot)
+{
+ ot->name = "Search and Add Node";
+ ot->idname = "NODE_OT_add_search";
+ ot->description = "Search for nodes and add one to the active tree";
+
+ ot->invoke = node_add_search_invoke;
+ ot->poll = ED_operator_node_editable;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(
+ ot->srna, "use_transform", true, "Use Transform", "Start moving the node after adding it");
+}
+
+/** \} */
+
} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 3a8e5d0aed6..5ae6573df7c 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -60,6 +60,7 @@
#include "ED_node.h"
#include "ED_screen.h"
#include "ED_space_api.h"
+#include "ED_viewer_path.hh"
#include "UI_interface.hh"
#include "UI_resources.h"
@@ -95,6 +96,14 @@ extern void ui_draw_dropshadow(
*/
struct TreeDrawContext {
/**
+ * Whether a viewer node is active in geometry nodes can not be determined by a flag on the node
+ * alone. That's because if the node group with the viewer is used multiple times, it's only
+ * active in one of these cases.
+ * The active node is cached here to avoid doing the more expensive check for every viewer node
+ * in the tree.
+ */
+ const bNode *active_geometry_nodes_viewer = nullptr;
+ /**
* Geometry nodes logs various data during execution. The logged data that corresponds to the
* currently drawn node tree can be retrieved from the log below.
*/
@@ -432,7 +441,7 @@ static void node_update_basis(const bContext &C,
float aspect = 1.0f;
if (node.preview_xsize && node.preview_ysize) {
- aspect = (float)node.preview_ysize / (float)node.preview_xsize;
+ aspect = float(node.preview_ysize) / float(node.preview_xsize);
}
dy -= NODE_DYS / 2;
@@ -597,7 +606,7 @@ static void node_update_hidden(bNode &node, uiBlock &block)
float hiddenrad = HIDDEN_RAD;
float tot = MAX2(totin, totout);
if (tot > 4) {
- hiddenrad += 5.0f * (float)(tot - 4);
+ hiddenrad += 5.0f * float(tot - 4);
}
node.totr.xmin = loc.x;
@@ -606,7 +615,7 @@ static void node_update_hidden(bNode &node, uiBlock &block)
node.totr.ymin = node.totr.ymax - 2 * hiddenrad;
/* Output sockets. */
- float rad = (float)M_PI / (1.0f + (float)totout);
+ float rad = float(M_PI) / (1.0f + float(totout));
float drad = rad;
LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
@@ -619,7 +628,7 @@ static void node_update_hidden(bNode &node, uiBlock &block)
}
/* Input sockets. */
- rad = drad = -(float)M_PI / (1.0f + (float)totin);
+ rad = drad = -float(M_PI) / (1.0f + float(totin));
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
if (!nodeSocketIsHidden(socket)) {
@@ -639,15 +648,19 @@ static void node_update_hidden(bNode &node, uiBlock &block)
node.totr.ymax);
}
-static int node_get_colorid(const bNode &node)
+static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
{
const int nclass = (node.typeinfo->ui_class == nullptr) ? node.typeinfo->nclass :
node.typeinfo->ui_class(&node);
switch (nclass) {
case NODE_CLASS_INPUT:
return TH_NODE_INPUT;
- case NODE_CLASS_OUTPUT:
+ case NODE_CLASS_OUTPUT: {
+ if (node.type == GEO_NODE_VIEWER) {
+ return &node == tree_draw_ctx.active_geometry_nodes_viewer ? TH_NODE_OUTPUT : TH_NODE;
+ }
return (node.flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE;
+ }
case NODE_CLASS_CONVERTER:
return TH_NODE_CONVERTER;
case NODE_CLASS_OP_COLOR:
@@ -1066,7 +1079,7 @@ static bool node_socket_has_tooltip(const bNodeTree &ntree, const bNodeSocket &s
static char *node_socket_get_tooltip(const bContext *C,
const bNodeTree *ntree,
- const bNode *UNUSED(node),
+ const bNode * /*node*/,
const bNodeSocket *socket)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1108,7 +1121,7 @@ static char *node_socket_get_tooltip(const bContext *C,
return BLI_strdup(output.str().c_str());
}
-static void node_socket_add_tooltip_in_node_editor(TreeDrawContext *UNUSED(tree_draw_ctx),
+static void node_socket_add_tooltip_in_node_editor(TreeDrawContext * /*tree_draw_ctx*/,
const bNodeTree *ntree,
const bNode *node,
const bNodeSocket *sock,
@@ -1125,7 +1138,7 @@ static void node_socket_add_tooltip_in_node_editor(TreeDrawContext *UNUSED(tree_
uiLayoutSetTooltipFunc(
layout,
- [](bContext *C, void *argN, const char *UNUSED(tip)) {
+ [](bContext *C, void *argN, const char * /*tip*/) {
SocketTooltipData *data = static_cast<SocketTooltipData *>(argN);
return node_socket_get_tooltip(C, data->ntree, data->node, data->socket);
},
@@ -1204,7 +1217,7 @@ static void node_socket_draw_nested(const bContext &C,
UI_but_func_tooltip_set(
but,
- [](bContext *C, void *argN, const char *UNUSED(tip)) {
+ [](bContext *C, void *argN, const char * /*tip*/) {
SocketTooltipData *data = (SocketTooltipData *)argN;
return node_socket_get_tooltip(C, data->ntree, data->node, data->socket);
},
@@ -1293,20 +1306,20 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv)
{
float xrect = BLI_rctf_size_x(prv);
float yrect = BLI_rctf_size_y(prv);
- float xscale = xrect / ((float)preview->xsize);
- float yscale = yrect / ((float)preview->ysize);
+ float xscale = xrect / float(preview->xsize);
+ float yscale = yrect / float(preview->ysize);
float scale;
/* Uniform scale and offset. */
rctf draw_rect = *prv;
if (xscale < yscale) {
- float offset = 0.5f * (yrect - ((float)preview->ysize) * xscale);
+ float offset = 0.5f * (yrect - float(preview->ysize) * xscale);
draw_rect.ymin += offset;
draw_rect.ymax -= offset;
scale = xscale;
}
else {
- float offset = 0.5f * (xrect - ((float)preview->xsize) * yscale);
+ float offset = 0.5f * (xrect - float(preview->xsize) * yscale);
draw_rect.xmin += offset;
draw_rect.xmax -= offset;
scale = yscale;
@@ -1613,7 +1626,7 @@ struct NodeErrorsTooltipData {
Span<geo_log::NodeWarning> warnings;
};
-static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
+static char *node_errors_tooltip_fn(bContext * /*C*/, void *argN, const char * /*tip*/)
{
NodeErrorsTooltipData &data = *(NodeErrorsTooltipData *)argN;
@@ -1770,10 +1783,10 @@ struct NodeExtraInfoRow {
};
struct NamedAttributeTooltipArg {
- Map<std::string, geo_log::NamedAttributeUsage> usage_by_attribute;
+ Map<StringRefNull, geo_log::NamedAttributeUsage> usage_by_attribute;
};
-static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
+static char *named_attribute_tooltip(bContext * /*C*/, void *argN, const char * /*tip*/)
{
NamedAttributeTooltipArg &arg = *static_cast<NamedAttributeTooltipArg *>(argN);
@@ -1824,7 +1837,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char
}
static NodeExtraInfoRow row_from_used_named_attribute(
- const Map<std::string, geo_log::NamedAttributeUsage> &usage_by_attribute_name)
+ const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute_name)
{
const int attributes_num = usage_by_attribute_name.size();
@@ -1930,8 +1943,8 @@ static void node_draw_extra_info_row(const bNode &node,
UI_BTYPE_BUT,
0,
extra_info_row.icon,
- (int)but_icon_left,
- (int)(rect.ymin + row * (20.0f * U.dpi_fac)),
+ int(but_icon_left),
+ int(rect.ymin + row * (20.0f * U.dpi_fac)),
but_icon_width,
UI_UNIT_Y,
nullptr,
@@ -1956,10 +1969,10 @@ static void node_draw_extra_info_row(const bNode &node,
UI_BTYPE_LABEL,
0,
extra_info_row.text.c_str(),
- (int)but_text_left,
- (int)(rect.ymin + row * (20.0f * U.dpi_fac)),
- (short)but_text_width,
- (short)NODE_DY,
+ int(but_text_left),
+ int(rect.ymin + row * (20.0f * U.dpi_fac)),
+ short(but_text_width),
+ short(NODE_DY),
nullptr,
0,
0,
@@ -2055,7 +2068,7 @@ static void node_draw_basis(const bContext &C,
const rctf &rct = node.totr;
float color[4];
- int color_id = node_get_colorid(node);
+ int color_id = node_get_colorid(tree_draw_ctx, node);
GPU_line_width(1.0f);
@@ -2132,6 +2145,9 @@ static void node_draw_basis(const bContext &C,
0,
"");
UI_but_func_set(but, node_toggle_button_cb, &node, (void *)"NODE_OT_group_edit");
+ if (node.id) {
+ UI_but_icon_indicator_number_set(but, ID_REAL_USERS(node.id));
+ }
UI_block_emboss_set(&block, UI_EMBOSS);
}
if (node.type == NODE_CUSTOM && node.typeinfo->ui_icon != ICON_NONE) {
@@ -2153,6 +2169,29 @@ static void node_draw_basis(const bContext &C,
"");
UI_block_emboss_set(&block, UI_EMBOSS);
}
+ if (node.type == GEO_NODE_VIEWER) {
+ const bool is_active = &node == tree_draw_ctx.active_geometry_nodes_viewer;
+ iconofs -= iconbutw;
+ UI_block_emboss_set(&block, UI_EMBOSS_NONE);
+ uiBut *but = uiDefIconBut(&block,
+ UI_BTYPE_BUT,
+ 0,
+ is_active ? ICON_HIDE_OFF : ICON_HIDE_ON,
+ iconofs,
+ rct.ymax - NODE_DY,
+ iconbutw,
+ UI_UNIT_Y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ /* Selection implicitly activates the node. */
+ const char *operator_idname = is_active ? "NODE_OT_deactivate_viewer" : "NODE_OT_select";
+ UI_but_func_set(but, node_toggle_button_cb, &node, (void *)operator_idname);
+ UI_block_emboss_set(&block, UI_EMBOSS);
+ }
node_add_error_message_button(tree_draw_ctx, node, block, rct, iconofs);
@@ -2195,10 +2234,10 @@ static void node_draw_basis(const bContext &C,
UI_BTYPE_LABEL,
0,
showname,
- (int)(rct.xmin + NODE_MARGIN_X + 0.4f),
- (int)(rct.ymax - NODE_DY),
- (short)(iconofs - rct.xmin - (18.0f * U.dpi_fac)),
- (short)NODE_DY,
+ int(rct.xmin + NODE_MARGIN_X + 0.4f),
+ int(rct.ymax - NODE_DY),
+ short(iconofs - rct.xmin - (18.0f * U.dpi_fac)),
+ short(NODE_DY),
nullptr,
0,
0,
@@ -2341,7 +2380,7 @@ static void node_draw_hidden(const bContext &C,
float scale;
UI_view2d_scale_get(&v2d, &scale, nullptr);
- const int color_id = node_get_colorid(node);
+ const int color_id = node_get_colorid(tree_draw_ctx, node);
node_draw_extra_info_panel(tree_draw_ctx, snode, node, block);
@@ -2425,8 +2464,8 @@ static void node_draw_hidden(const bContext &C,
showname,
round_fl_to_int(rct.xmin + NODE_MARGIN_X),
round_fl_to_int(centy - NODE_DY * 0.5f),
- (short)(BLI_rctf_size_x(&rct) - ((18.0f + 12.0f) * U.dpi_fac)),
- (short)NODE_DY,
+ short(BLI_rctf_size_x(&rct) - ((18.0f + 12.0f) * U.dpi_fac)),
+ short(NODE_DY),
nullptr,
0,
0,
@@ -2698,7 +2737,8 @@ static void node_update_nodetree(const bContext &C,
}
}
-static void frame_node_draw_label(const bNodeTree &ntree,
+static void frame_node_draw_label(TreeDrawContext &tree_draw_ctx,
+ const bNodeTree &ntree,
const bNode &node,
const SpaceNode &snode)
{
@@ -2714,15 +2754,15 @@ static void frame_node_draw_label(const bNodeTree &ntree,
BLF_enable(fontid, BLF_ASPECT);
BLF_aspect(fontid, aspect, aspect, 1.0f);
/* clamp otherwise it can suck up a LOT of memory */
- BLF_size(fontid, MIN2(24.0f, font_size) * U.pixelsize, U.dpi);
+ BLF_size(fontid, MIN2(24.0f, font_size) * U.dpi_fac);
/* title color */
- int color_id = node_get_colorid(node);
+ int color_id = node_get_colorid(tree_draw_ctx, node);
uchar color[3];
UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color);
BLF_color3ubv(fontid, color);
- const float margin = (float)(NODE_DY / 4);
+ const float margin = float(NODE_DY / 4);
const float width = BLF_width(fontid, label, sizeof(label));
const float ascender = BLF_ascender(fontid);
const int label_height = ((margin / aspect) + (ascender * aspect));
@@ -2831,7 +2871,7 @@ static void frame_node_draw(const bContext &C,
}
/* label and text */
- frame_node_draw_label(ntree, node, snode);
+ frame_node_draw_label(tree_draw_ctx, ntree, node, snode);
node_draw_extra_info_panel(tree_draw_ctx, snode, node, block);
@@ -2866,7 +2906,7 @@ static void reroute_node_draw(
x,
y,
width,
- (short)NODE_DY,
+ short(NODE_DY),
nullptr,
0,
0,
@@ -2974,7 +3014,7 @@ static void node_draw_nodetree(const bContext &C,
}
}
-/* Draw the breadcrumb on the bottom of the editor. */
+/* Draw the breadcrumb on the top of the editor. */
static void draw_tree_path(const bContext &C, ARegion &region)
{
using namespace blender;
@@ -3013,7 +3053,7 @@ static void snode_setup_v2d(SpaceNode &snode, ARegion &region, const float2 &cen
UI_view2d_view_ortho(&v2d);
/* Aspect + font, set each time. */
- snode.runtime->aspect = BLI_rctf_size_x(&v2d.cur) / (float)region.winx;
+ snode.runtime->aspect = BLI_rctf_size_x(&v2d.cur) / float(region.winx);
// XXX snode->curfont = uiSetCurFont_ext(snode->aspect);
}
@@ -3036,6 +3076,9 @@ static void draw_nodetree(const bContext &C,
tree_draw_ctx.geo_tree_log->ensure_node_warnings();
tree_draw_ctx.geo_tree_log->ensure_node_run_time();
}
+ WorkSpace *workspace = CTX_wm_workspace(&C);
+ tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer(
+ workspace->viewer_path, *snode);
}
node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks);
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index f07a1205c6b..7982b47f363 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -43,7 +43,7 @@
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
-#include "ED_spreadsheet.h"
+#include "ED_viewer_path.hh"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -89,8 +89,8 @@ struct CompoJob {
Depsgraph *compositor_depsgraph;
bNodeTree *localtree;
/* Jon system integration. */
- const short *stop;
- short *do_update;
+ const bool *stop;
+ bool *do_update;
float *progress;
};
@@ -166,7 +166,7 @@ static int compo_get_recalc_flags(const bContext *C)
}
/* called by compo, only to check job 'stop' value */
-static int compo_breakjob(void *cjv)
+static bool compo_breakjob(void *cjv)
{
CompoJob *cj = (CompoJob *)cjv;
@@ -179,7 +179,7 @@ static int compo_breakjob(void *cjv)
}
/* called by compo, wmJob sends notifier */
-static void compo_statsdrawjob(void *cjv, const char *UNUSED(str))
+static void compo_statsdrawjob(void *cjv, const char * /*str*/)
{
CompoJob *cj = (CompoJob *)cjv;
@@ -234,7 +234,7 @@ static void compo_initjob(void *cjv)
}
/* called before redraw notifiers, it moves finished previews over */
-static void compo_updatejob(void *UNUSED(cjv))
+static void compo_updatejob(void * /*cjv*/)
{
WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, nullptr);
}
@@ -250,8 +250,8 @@ static void compo_progressjob(void *cjv, float progress)
static void compo_startjob(void *cjv,
/* Cannot be const, this function implements wm_jobs_start_callback.
* NOLINTNEXTLINE: readability-non-const-parameter. */
- short *stop,
- short *do_update,
+ bool *stop,
+ bool *do_update,
float *progress)
{
CompoJob *cj = (CompoJob *)cjv;
@@ -443,11 +443,11 @@ void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *ro
}
NodeTreeUpdateExtraParams params = {nullptr};
- params.tree_changed_fn = [](ID *id, bNodeTree *ntree, void *UNUSED(user_data)) {
+ params.tree_changed_fn = [](ID *id, bNodeTree *ntree, void * /*user_data*/) {
blender::ed::space_node::send_notifiers_after_tree_change(id, ntree);
DEG_id_tag_update(&ntree->id, ID_RECALC_COPY_ON_WRITE);
};
- params.tree_output_changed_fn = [](ID *UNUSED(id), bNodeTree *ntree, void *UNUSED(user_data)) {
+ params.tree_output_changed_fn = [](ID * /*id*/, bNodeTree *ntree, void * /*user_data*/) {
DEG_id_tag_update(&ntree->id, ID_RECALC_NTREE_OUTPUT);
};
@@ -502,6 +502,7 @@ void ED_node_shader_default(const bContext *C, ID *id)
}
ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree);
+ ma->nodetree->owner_id = &ma->id;
BKE_ntree_update_main_tree(bmain, ma->nodetree, nullptr);
}
else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
@@ -713,10 +714,12 @@ void ED_node_set_active(
/* Sync to active texpaint slot, otherwise we can end up painting on a different slot
* than we are looking at. */
if (ma->texpaintslot) {
- Image *image = (Image *)node->id;
- for (int i = 0; i < ma->tot_slots; i++) {
- if (ma->texpaintslot[i].ima == image) {
- ma->paint_active_slot = i;
+ if (node->id != nullptr && GS(node->id->name) == ID_IM) {
+ Image *image = (Image *)node->id;
+ for (int i = 0; i < ma->tot_slots; i++) {
+ if (ma->texpaintslot[i].ima == image) {
+ ma->paint_active_slot = i;
+ }
}
}
}
@@ -732,25 +735,9 @@ void ED_node_set_active(
/* Sync to Image Editor under the following conditions:
* - current image is not pinned
* - current image is not a Render Result or ViewerNode (want to keep looking at these) */
- Image *image = (Image *)node->id;
- wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
- LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
- const bScreen *screen = WM_window_get_active_screen(win);
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
- if (sl->spacetype != SPACE_IMAGE) {
- continue;
- }
- SpaceImage *sima = (SpaceImage *)sl;
- if (sima->pin) {
- continue;
- }
- if (sima->image && ELEM(sima->image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
- continue;
- }
- ED_space_image_set(bmain, sima, image, true);
- }
- }
+ if (node->id != nullptr && GS(node->id->name) == ID_IM) {
+ Image *image = (Image *)node->id;
+ ED_space_image_sync(bmain, image, true);
}
if (r_active_texture_changed) {
@@ -816,14 +803,14 @@ void ED_node_set_active(
}
}
node->flag |= NODE_DO_OUTPUT;
- ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, node);
}
+ blender::ed::viewer_path::activate_geometry_node(*bmain, *snode, *node);
}
}
}
}
-void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree))
+void ED_node_post_apply_transform(bContext * /*C*/, bNodeTree * /*ntree*/)
{
/* XXX This does not work due to layout functions relying on node->block,
* which only exists during actual drawing. Can we rely on valid totr rects?
@@ -1356,12 +1343,15 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
+ bool linked = RNA_boolean_get(op->ptr, "linked") || ((U.dupflag & USER_DUP_NTREE) == 0);
+ const bool dupli_node_tree = !linked;
bool changed = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
+ Map<const ID *, ID *> duplicated_node_groups;
bNode *lastnode = (bNode *)ntree->nodes.last;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -1369,6 +1359,18 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
bNode *new_node = bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node);
+
+ if (node->id && dupli_node_tree) {
+ ID *new_group = duplicated_node_groups.lookup_or_add_cb(node->id, [&]() {
+ ID *new_group = BKE_id_copy(bmain, node->id);
+ /* Remove user added by copying. */
+ id_us_min(new_group);
+ return new_group;
+ });
+ id_us_plus(new_group);
+ id_us_min(new_node->id);
+ new_node->id = new_group;
+ }
changed = true;
}
@@ -1457,6 +1459,8 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
void NODE_OT_duplicate(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Duplicate Nodes";
ot->description = "Duplicate selected nodes";
@@ -1471,12 +1475,19 @@ void NODE_OT_duplicate(wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes");
+
+ prop = RNA_def_boolean(ot->srna,
+ "linked",
+ true,
+ "Linked",
+ "Duplicate node but not node trees, linking to the original data");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* XXX: some code needing updating to operators. */
/* goes over all scenes, reads render layers */
-static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_read_viewlayers_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1524,7 +1535,7 @@ void NODE_OT_read_viewlayers(wmOperatorType *ot)
ot->flag = 0;
}
-int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
+int node_render_changed_exec(bContext *C, wmOperator * /*op*/)
{
Scene *sce = CTX_data_scene(C);
@@ -1631,7 +1642,7 @@ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
}
}
-static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_hide_toggle_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1662,7 +1673,7 @@ void NODE_OT_hide_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_preview_toggle_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1695,7 +1706,47 @@ void NODE_OT_preview_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_deactivate_viewer_exec(bContext *C, wmOperator * /*op*/)
+{
+ SpaceNode &snode = *CTX_wm_space_node(C);
+ WorkSpace &workspace = *CTX_wm_workspace(C);
+
+ bNode *active_viewer = viewer_path::find_geometry_nodes_viewer(workspace.viewer_path, snode);
+
+ LISTBASE_FOREACH (bNode *, node, &snode.edittree->nodes) {
+ if (node->type != GEO_NODE_VIEWER) {
+ continue;
+ }
+ if (!(node->flag & SELECT)) {
+ continue;
+ }
+ if (node == active_viewer) {
+ node->flag &= ~NODE_DO_OUTPUT;
+ BKE_ntree_update_tag_node_property(snode.edittree, node);
+ }
+ }
+
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_deactivate_viewer(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Deactivate Viewer Node";
+ ot->description = "Deactivate selected viewer node in geometry nodes";
+ ot->idname = __func__;
+
+ /* callbacks */
+ ot->exec = node_deactivate_viewer_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int node_options_toggle_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1726,7 +1777,7 @@ void NODE_OT_options_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_socket_toggle_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1782,7 +1833,7 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
/** \name Node Mute Operator
* \{ */
-static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_mute_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1822,7 +1873,7 @@ void NODE_OT_mute_toggle(wmOperatorType *ot)
/** \name Node Delete Operator
* \{ */
-static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_delete_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1872,7 +1923,7 @@ static bool node_switch_view_poll(bContext *C)
return false;
}
-static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_switch_view_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1909,7 +1960,7 @@ void NODE_OT_switch_view_update(wmOperatorType *ot)
/** \name Node Delete with Reconnect Operator
* \{ */
-static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_delete_reconnect_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
@@ -2003,7 +2054,7 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot)
/** \name Node Multi File Output Remove Socket Operator
* \{ */
-static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
@@ -2129,7 +2180,7 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
/** \name Node Copy Node Color Operator
* \{ */
-static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_copy_color_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
@@ -2177,7 +2228,7 @@ void NODE_OT_node_copy_color(wmOperatorType *ot)
/** \name Node Copy to Clipboard Operator
* \{ */
-static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_clipboard_copy_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
@@ -2230,6 +2281,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
newlink->tosock = socket_map.lookup(link->tosock);
newlink->fromnode = node_map.lookup(link->fromnode);
newlink->fromsock = socket_map.lookup(link->fromsock);
+ newlink->multi_input_socket_index = link->multi_input_socket_index;
BKE_node_clipboard_add_link(newlink);
}
@@ -2351,11 +2403,19 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) {
- nodeAddLink(ntree,
- node_map.lookup(link->fromnode),
- socket_map.lookup(link->fromsock),
- node_map.lookup(link->tonode),
- socket_map.lookup(link->tosock));
+ bNodeLink *new_link = nodeAddLink(ntree,
+ node_map.lookup(link->fromnode),
+ socket_map.lookup(link->fromsock),
+ node_map.lookup(link->tonode),
+ socket_map.lookup(link->tosock));
+ new_link->multi_input_socket_index = link->multi_input_socket_index;
+ }
+
+ ntree->ensure_topology_cache();
+
+ for (bNode *new_node : node_map.values()) {
+ /* Update multi input socket indices in case all connected nodes weren't copied. */
+ update_multi_input_indices_for_removed_links(*new_node);
}
Main *bmain = CTX_data_main(C);
@@ -2532,7 +2592,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- /* Don't handle subtypes for now. */
+ /* Don't handle sub-types for now. */
nodeModifySocketType(ntree, nullptr, iosock, socket_type->idname);
/* Need the extra update here because the loop above does not check for valid links in the node
@@ -2570,8 +2630,8 @@ static bool socket_change_poll_type(void *userdata, bNodeSocketType *socket_type
}
static const EnumPropertyItem *socket_change_type_itemf(bContext *C,
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
+ PointerRNA * /*ptr*/,
+ PropertyRNA * /*prop*/,
bool *r_free)
{
if (!C) {
@@ -2854,8 +2914,8 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode,
float bufx = backdrop_width * snode->zoom;
float bufy = backdrop_height * snode->zoom;
- *fx = (bufx > 0.0f ? ((float)x - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f);
- *fy = (bufy > 0.0f ? ((float)y - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f);
+ *fx = (bufx > 0.0f ? (float(x) - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f);
+ *fy = (bufy > 0.0f ? (float(y) - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f);
}
static int viewer_border_exec(bContext *C, wmOperator *op)
@@ -2935,7 +2995,7 @@ void NODE_OT_viewer_border(wmOperatorType *ot)
WM_operator_properties_gesture_box(ot);
}
-static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op))
+static int clear_viewer_border_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *btree = snode->nodetree;
@@ -2968,7 +3028,7 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot)
/** \name Cryptomatte Add Socket
* \{ */
-static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
@@ -3016,7 +3076,7 @@ void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot)
/** \name Cryptomatte Remove Socket
* \{ */
-static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index 809c4b2fe59..a1a8fd0dfdc 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -213,7 +213,7 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
ED_undo_push(C, "Assign Attribute Name");
}
-void node_geometry_add_attribute_search_button(const bContext &UNUSED(C),
+void node_geometry_add_attribute_search_button(const bContext & /*C*/,
const bNode &node,
PointerRNA &socket_ptr,
uiLayout &layout)
diff --git a/source/blender/editors/space_node/node_gizmo.cc b/source/blender/editors/space_node/node_gizmo.cc
index f9126556b71..7a77d523f43 100644
--- a/source/blender/editors/space_node/node_gizmo.cc
+++ b/source/blender/editors/space_node/node_gizmo.cc
@@ -65,7 +65,7 @@ static void node_gizmo_calc_matrix_space_with_image_dims(const SpaceNode *snode,
/** \name Backdrop Gizmo
* \{ */
-static void gizmo_node_backdrop_prop_matrix_get(const wmGizmo *UNUSED(gz),
+static void gizmo_node_backdrop_prop_matrix_get(const wmGizmo * /*gz*/,
wmGizmoProperty *gz_prop,
void *value_p)
{
@@ -78,7 +78,7 @@ static void gizmo_node_backdrop_prop_matrix_get(const wmGizmo *UNUSED(gz),
matrix[3][1] = snode->yof;
}
-static void gizmo_node_backdrop_prop_matrix_set(const wmGizmo *UNUSED(gz),
+static void gizmo_node_backdrop_prop_matrix_set(const wmGizmo * /*gz*/,
wmGizmoProperty *gz_prop,
const void *value_p)
{
@@ -90,7 +90,7 @@ static void gizmo_node_backdrop_prop_matrix_set(const wmGizmo *UNUSED(gz),
snode->yof = matrix[3][1];
}
-static bool WIDGETGROUP_node_transform_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+static bool WIDGETGROUP_node_transform_poll(const bContext *C, wmGizmoGroupType * /*gzgt*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -109,7 +109,7 @@ static bool WIDGETGROUP_node_transform_poll(const bContext *C, wmGizmoGroupType
return false;
}
-static void WIDGETGROUP_node_transform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+static void WIDGETGROUP_node_transform_setup(const bContext * /*C*/, wmGizmoGroup *gzgroup)
{
wmGizmoWrapper *wwrapper = (wmGizmoWrapper *)MEM_mallocN(sizeof(wmGizmoWrapper), __func__);
@@ -252,7 +252,7 @@ static void gizmo_node_crop_prop_matrix_get(const wmGizmo *gz,
const float *dims = crop_group->state.dims;
const bNode *node = (const bNode *)gz_prop->custom_func.user_data;
const NodeTwoXYs *nxy = (const NodeTwoXYs *)node->storage;
- bool is_relative = (bool)node->custom2;
+ bool is_relative = bool(node->custom2);
rctf rct;
two_xy_to_rect(nxy, &rct, dims, is_relative);
matrix[0][0] = fabsf(BLI_rctf_size_x(&rct));
@@ -271,7 +271,7 @@ static void gizmo_node_crop_prop_matrix_set(const wmGizmo *gz,
const float *dims = crop_group->state.dims;
bNode *node = (bNode *)gz_prop->custom_func.user_data;
NodeTwoXYs *nxy = (NodeTwoXYs *)node->storage;
- bool is_relative = (bool)node->custom2;
+ bool is_relative = bool(node->custom2);
rctf rct;
two_xy_to_rect(nxy, &rct, dims, is_relative);
const bool nx = rct.xmin > rct.xmax;
@@ -294,7 +294,7 @@ static void gizmo_node_crop_prop_matrix_set(const wmGizmo *gz,
gizmo_node_crop_update(crop_group);
}
-static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmGizmoGroupType * /*gzgt*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -316,7 +316,7 @@ static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmGizmoGroupType *UNUS
return false;
}
-static void WIDGETGROUP_node_crop_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+static void WIDGETGROUP_node_crop_setup(const bContext * /*C*/, wmGizmoGroup *gzgroup)
{
NodeCropWidgetGroup *crop_group = MEM_new<NodeCropWidgetGroup>(__func__);
crop_group->border = WM_gizmo_new("GIZMO_GT_cage_2d", gzgroup, nullptr);
@@ -406,7 +406,7 @@ struct NodeSunBeamsWidgetGroup {
} state;
};
-static bool WIDGETGROUP_node_sbeam_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+static bool WIDGETGROUP_node_sbeam_poll(const bContext *C, wmGizmoGroupType * /*gzgt*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -425,7 +425,7 @@ static bool WIDGETGROUP_node_sbeam_poll(const bContext *C, wmGizmoGroupType *UNU
return false;
}
-static void WIDGETGROUP_node_sbeam_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+static void WIDGETGROUP_node_sbeam_setup(const bContext * /*C*/, wmGizmoGroup *gzgroup)
{
NodeSunBeamsWidgetGroup *sbeam_group = (NodeSunBeamsWidgetGroup *)MEM_mallocN(
sizeof(NodeSunBeamsWidgetGroup), __func__);
@@ -511,7 +511,7 @@ struct NodeCornerPinWidgetGroup {
} state;
};
-static bool WIDGETGROUP_node_corner_pin_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+static bool WIDGETGROUP_node_corner_pin_poll(const bContext *C, wmGizmoGroupType * /*gzgt*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -530,7 +530,7 @@ static bool WIDGETGROUP_node_corner_pin_poll(const bContext *C, wmGizmoGroupType
return false;
}
-static void WIDGETGROUP_node_corner_pin_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+static void WIDGETGROUP_node_corner_pin_setup(const bContext * /*C*/, wmGizmoGroup *gzgroup)
{
NodeCornerPinWidgetGroup *cpin_group = (NodeCornerPinWidgetGroup *)MEM_mallocN(
sizeof(NodeCornerPinWidgetGroup), __func__);
diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc
index 21def1bd9d7..329f78860a4 100644
--- a/source/blender/editors/space_node/node_group.cc
+++ b/source/blender/editors/space_node/node_group.cc
@@ -14,6 +14,7 @@
#include "BLI_linklist.h"
#include "BLI_listbase.h"
+#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_string.h"
#include "BLI_vector.hh"
@@ -603,9 +604,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int node_group_separate_invoke(bContext *C,
- wmOperator *UNUSED(op),
- const wmEvent *UNUSED(event))
+static int node_group_separate_invoke(bContext *C, wmOperator * /*op*/, const wmEvent * /*event*/)
{
uiPopupMenu *pup = UI_popup_menu_begin(
C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE);
@@ -737,6 +736,74 @@ static int node_get_selected_minmax(
return totselect;
}
+/**
+ * Redirect a link that are connecting a non-selected node to selected one.
+ * Create new socket or reuse an existing one that was connected from the same input.
+ * The output sockets of group nodes usually have consciously given names so they have
+ * precedence over socket names the link points to.
+ *
+ * \param ntree: The node tree that the node group is being created from.
+ * \param ngroup: The node tree of the new node group.
+ * \param gnode: The new group node in the original tree.
+ * \param input_node: The input node of the new node group.
+ * \param link: The incoming link that needs to be altered.
+ * \param reusable_sockets: Map for input socket interface lookup.
+ */
+static void node_group_make_redirect_incoming_link(
+ bNodeTree &ntree,
+ bNodeTree *ngroup,
+ bNode *gnode,
+ bNode *input_node,
+ bNodeLink *link,
+ Map<bNodeSocket *, bNodeSocket *> &reusable_sockets)
+{
+ bNodeSocket *input_socket = reusable_sockets.lookup_default(link->fromsock, nullptr);
+ if (input_socket) {
+ /* The incoming link is from a socket that has already been linked to
+ * a socket interface of the input node.
+ * Change the source of the link to the previously created socket interface.
+ * Move the link into the node tree of the new group. */
+ link->fromnode = input_node;
+ link->fromsock = input_socket;
+ BLI_remlink(&ntree.links, link);
+ BLI_addtail(&ngroup->links, link);
+ }
+ else {
+ bNode *node_for_typeinfo = nullptr;
+ bNodeSocket *socket_for_typeinfo = nullptr;
+ /* Find a socket where typeinfo and name may come from. */
+ node_socket_skip_reroutes(
+ &ntree.links, link->tonode, link->tosock, &node_for_typeinfo, &socket_for_typeinfo);
+ bNodeSocket *socket_for_naming = socket_for_typeinfo;
+
+ /* Use the name of group node output sockets. */
+ if (ELEM(link->fromnode->type, NODE_GROUP_INPUT, NODE_GROUP, NODE_CUSTOM_GROUP)) {
+ socket_for_naming = link->fromsock;
+ }
+
+ bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocketWithName(ngroup,
+ node_for_typeinfo,
+ socket_for_typeinfo,
+ socket_for_naming->idname,
+ socket_for_naming->name);
+
+ /* Update the group node and interface sockets so the new interface socket can be linked. */
+ node_group_update(&ntree, gnode);
+ node_group_input_update(ngroup, input_node);
+
+ /* Create new internal link. */
+ bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
+ nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock);
+
+ /* Redirect external link. */
+ link->tonode = gnode;
+ link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
+
+ /* Remember which interface socket the link has been redirected to. */
+ reusable_sockets.add_new(link->fromsock, input_sock);
+ }
+}
+
static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode)
{
Main *bmain = CTX_data_main(&C);
@@ -836,6 +903,10 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
output_node->locy = -offsety;
/* relink external sockets */
+
+ /* A map from link sources to input sockets already connected. */
+ Map<bNodeSocket *, bNodeSocket *> reusable_sockets;
+
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
const bool fromselect = node_group_make_use_node(*link->fromnode, gnode);
const bool toselect = node_group_make_use_node(*link->tonode, gnode);
@@ -853,24 +924,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
continue;
}
- bNodeSocket *link_sock;
- bNode *link_node;
- node_socket_skip_reroutes(&ntree.links, link->tonode, link->tosock, &link_node, &link_sock);
- bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock);
-
- /* update the group node and interface node sockets,
- * so the new interface socket can be linked.
- */
- node_group_update(&ntree, gnode);
- node_group_input_update(ngroup, input_node);
-
- /* create new internal link */
- bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
- nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock);
-
- /* redirect external link */
- link->tonode = gnode;
- link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
+ node_group_make_redirect_incoming_link(
+ ntree, ngroup, gnode, input_node, link, reusable_sockets);
}
else if (fromselect && !toselect) {
/* Remove hidden links to not create unconnected sockets in the interface. */
@@ -1047,6 +1102,8 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
ED_node_tree_propagate_change(C, bmain, nullptr);
+ WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
+
/* We broke relations in node tree, need to rebuild them in the graphs. */
DEG_relations_tag_update(bmain);
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 456cbf5064d..88be9a52c61 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -38,6 +38,8 @@ extern const char *node_context_dir[];
namespace blender::ed::space_node {
+struct AssetItemTree;
+
/** Temporary data used in node link drag modal operator. */
struct bNodeLinkDrag {
/** Links dragged by the operator. */
@@ -96,6 +98,15 @@ struct SpaceNode_Runtime {
/* XXX hack for translate_attach op-macros to pass data from transform op to insert_offset op */
/** Temporary data for node insert offset (in UI called Auto-offset). */
struct NodeInsertOfsData *iofsd;
+
+ /**
+ * Temporary data for node add menu in order to provide longer-term storage for context pointers.
+ * Recreated every time the root menu is opened. In the future this will be replaced with an "all
+ * libraries" cache in the asset system itself.
+ *
+ * Stored with a shared pointer so that it can be forward declared.
+ */
+ std::shared_ptr<AssetItemTree> assets_for_menu;
};
enum NodeResizeDirection {
@@ -251,7 +262,9 @@ bNode *add_node(const bContext &C, StringRef idname, const float2 &location);
bNode *add_static_node(const bContext &C, int type, const float2 &location);
void NODE_OT_add_reroute(wmOperatorType *ot);
+void NODE_OT_add_search(wmOperatorType *ot);
void NODE_OT_add_group(wmOperatorType *ot);
+void NODE_OT_add_group_asset(wmOperatorType *ot);
void NODE_OT_add_object(wmOperatorType *ot);
void NODE_OT_add_collection(wmOperatorType *ot);
void NODE_OT_add_file(wmOperatorType *ot);
@@ -269,6 +282,8 @@ void NODE_OT_group_edit(wmOperatorType *ot);
/* node_relationships.cc */
+void update_multi_input_indices_for_removed_links(bNode &node);
+
void NODE_OT_link(wmOperatorType *ot);
void NODE_OT_link_make(wmOperatorType *ot);
void NODE_OT_links_cut(wmOperatorType *ot);
@@ -321,6 +336,7 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot);
void NODE_OT_preview_toggle(wmOperatorType *ot);
void NODE_OT_options_toggle(wmOperatorType *ot);
void NODE_OT_node_copy_color(wmOperatorType *ot);
+void NODE_OT_deactivate_viewer(wmOperatorType *ot);
void NODE_OT_read_viewlayers(wmOperatorType *ot);
void NODE_OT_render_changed(wmOperatorType *ot);
@@ -375,4 +391,13 @@ void invoke_node_link_drag_add_menu(bContext &C,
bNodeSocket &socket,
const float2 &cursor);
+/* add_node_search.cc */
+
+void invoke_add_node_search_menu(bContext &C, const float2 &cursor, bool use_transform);
+
+/* add_menu_assets.cc */
+
+MenuType add_catalog_assets_menu_type();
+MenuType add_root_catalogs_menu_type();
+
} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc
index a208370a6f9..104d1acf3b4 100644
--- a/source/blender/editors/space_node/node_ops.cc
+++ b/source/blender/editors/space_node/node_ops.cc
@@ -44,6 +44,7 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_options_toggle);
WM_operatortype_append(NODE_OT_hide_socket_toggle);
WM_operatortype_append(NODE_OT_node_copy_color);
+ WM_operatortype_append(NODE_OT_deactivate_viewer);
WM_operatortype_append(NODE_OT_duplicate);
WM_operatortype_append(NODE_OT_delete);
@@ -75,7 +76,9 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_backimage_fit);
WM_operatortype_append(NODE_OT_backimage_sample);
+ WM_operatortype_append(NODE_OT_add_search);
WM_operatortype_append(NODE_OT_add_group);
+ WM_operatortype_append(NODE_OT_add_group_asset);
WM_operatortype_append(NODE_OT_add_object);
WM_operatortype_append(NODE_OT_add_collection);
WM_operatortype_append(NODE_OT_add_file);
@@ -134,6 +137,7 @@ void ED_operatormacros_node()
mot = WM_operatortype_macro_define(ot, "NODE_OT_select");
RNA_boolean_set(mot->ptr, "extend", false);
RNA_boolean_set(mot->ptr, "socket_select", true);
+ RNA_boolean_set(mot->ptr, "clear_viewer", true);
WM_operatortype_macro_define(ot, "NODE_OT_link_viewer");
ot = WM_operatortype_append_macro("NODE_OT_translate_attach",
@@ -171,7 +175,17 @@ void ED_operatormacros_node()
"Duplicate",
"Duplicate selected nodes and move them",
OPTYPE_UNDO | OPTYPE_REGISTER);
- WM_operatortype_macro_define(ot, "NODE_OT_duplicate");
+ mot = WM_operatortype_macro_define(ot, "NODE_OT_duplicate");
+ RNA_boolean_set(mot->ptr, "linked", false);
+ WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
+
+ ot = WM_operatortype_append_macro(
+ "NODE_OT_duplicate_move_linked",
+ "Duplicate Linked",
+ "Duplicate selected nodes, but not their node trees, and move them",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ mot = WM_operatortype_macro_define(ot, "NODE_OT_duplicate");
+ RNA_boolean_set(mot->ptr, "linked", true);
WM_operatortype_macro_define(ot, "NODE_OT_translate_attach");
/* modified operator call for duplicating with input links */
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index 929fb64bd70..8eeba8727dc 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -27,8 +27,8 @@
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_space_api.h"
-#include "ED_spreadsheet.h"
#include "ED_util.h"
+#include "ED_viewer_path.hh"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -64,6 +64,10 @@ struct NodeInsertOfsData {
float offset_x; /* offset to apply to node chain */
};
+namespace blender::ed::space_node {
+
+bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out);
+
static void clear_picking_highlight(ListBase *links)
{
LISTBASE_FOREACH (bNodeLink *, link, links) {
@@ -71,10 +75,6 @@ static void clear_picking_highlight(ListBase *links)
}
}
-namespace blender::ed::space_node {
-
-void update_multi_input_indices_for_removed_links(bNode &node);
-
/* -------------------------------------------------------------------- */
/** \name Add Node
* \{ */
@@ -170,7 +170,7 @@ static void pick_input_link_by_link_intersect(const bContext &C,
}
}
-static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
+static bool socket_is_available(bNodeTree * /*ntree*/, bNodeSocket *sock, const bool allow_used)
{
if (nodeSocketIsHidden(sock)) {
return false;
@@ -495,17 +495,6 @@ static bool is_viewer_node(const bNode &node)
return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
}
-static Vector<const bNode *> find_viewer_nodes(const bNodeTree &tree)
-{
- Vector<const bNode *> viewer_nodes;
- for (const bNode *node : tree.all_nodes()) {
- if (is_viewer_node(*node)) {
- viewer_nodes.append(node);
- }
- }
- return viewer_nodes;
-}
-
static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
{
const bNode &node = socket.owner_node();
@@ -516,18 +505,10 @@ static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
return socket.index() == 0;
}
-static bool is_linked_to_viewer(const bNodeSocket &socket, const bNode &viewer_node)
+static bool is_viewer_socket(const bNodeSocket &socket)
{
- for (const bNodeSocket *target_socket : socket.directly_linked_sockets()) {
- if (&target_socket->owner_node() != &viewer_node) {
- continue;
- }
- if (!target_socket->is_available()) {
- continue;
- }
- if (is_viewer_socket_in_viewer(*target_socket)) {
- return true;
- }
+ if (is_viewer_node(socket.owner_node())) {
+ return is_viewer_socket_in_viewer(socket);
}
return false;
}
@@ -549,137 +530,165 @@ static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &
}
}
-static const bNode *get_existing_viewer(const bNodeTree &tree)
+static bNodeSocket *determine_socket_to_view(bNode &node_to_view)
{
- Vector<const bNode *> viewer_nodes = find_viewer_nodes(tree);
-
- /* Check if there is already an active viewer node that should be used. */
- for (const bNode *viewer_node : viewer_nodes) {
- if (viewer_node->flag & NODE_DO_OUTPUT) {
- return viewer_node;
+ int last_linked_socket_index = -1;
+ for (bNodeSocket *socket : node_to_view.output_sockets()) {
+ if (!socket_can_be_viewed(*socket)) {
+ continue;
}
- }
-
- /* If no active but non-active viewers exist, make one active. */
- if (!viewer_nodes.is_empty()) {
- const_cast<bNode *>(viewer_nodes[0])->flag |= NODE_DO_OUTPUT;
- return viewer_nodes[0];
- }
- return nullptr;
-}
-
-static const bNodeSocket *find_output_socket_to_be_viewed(const bNode *active_viewer_node,
- const bNode &node_to_view)
-{
- /* Check if any of the output sockets is selected, which is the case when the user just clicked
- * on the socket. */
- for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
- if (output_socket->flag & SELECT) {
- return output_socket;
+ for (bNodeLink *link : socket->directly_linked_links()) {
+ bNodeSocket &target_socket = *link->tosock;
+ bNode &target_node = *link->tonode;
+ if (is_viewer_socket(target_socket)) {
+ if (link->is_muted() || !(target_node.flag & NODE_DO_OUTPUT)) {
+ /* This socket is linked to a deactivated viewer, the viewer should be activated. */
+ return socket;
+ }
+ last_linked_socket_index = socket->index();
+ }
}
}
- const bNodeSocket *last_socket_linked_to_viewer = nullptr;
- if (active_viewer_node != nullptr) {
- for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
- if (!socket_can_be_viewed(*output_socket)) {
- continue;
- }
- if (is_linked_to_viewer(*output_socket, *active_viewer_node)) {
- last_socket_linked_to_viewer = output_socket;
+ if (last_linked_socket_index == -1) {
+ /* Return the first socket that can be viewed. */
+ for (bNodeSocket *socket : node_to_view.output_sockets()) {
+ if (socket_can_be_viewed(*socket)) {
+ return socket;
}
}
+ return nullptr;
}
- if (last_socket_linked_to_viewer == nullptr) {
- /* If no output is connected to a viewer, use the first output that can be viewed. */
- for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
- if (socket_can_be_viewed(*output_socket)) {
- return output_socket;
- }
+
+ /* Pick the next socket to be linked to the viewer. */
+ const int tot_outputs = node_to_view.output_sockets().size();
+ for (const int offset : IndexRange(1, tot_outputs)) {
+ const int index = (last_linked_socket_index + offset) % tot_outputs;
+ bNodeSocket &output_socket = node_to_view.output_socket(index);
+ if (!socket_can_be_viewed(output_socket)) {
+ continue;
}
- }
- else {
- /* Pick the next socket to be linked to the viewer. */
- const int tot_outputs = node_to_view.output_sockets().size();
- for (const int offset : IndexRange(1, tot_outputs - 1)) {
- const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs;
- const bNodeSocket &output_socket = node_to_view.output_socket(index);
- if (!socket_can_be_viewed(output_socket)) {
+ bool is_currently_viewed = false;
+ for (const bNodeLink *link : output_socket.directly_linked_links()) {
+ bNodeSocket &target_socket = *link->tosock;
+ bNode &target_node = *link->tonode;
+ if (!is_viewer_socket(target_socket)) {
continue;
}
- if (is_linked_to_viewer(output_socket, *active_viewer_node)) {
+ if (link->is_muted()) {
continue;
}
- return &output_socket;
+ if (!(target_node.flag & NODE_DO_OUTPUT)) {
+ continue;
+ }
+ is_currently_viewed = true;
+ break;
+ }
+ if (is_currently_viewed) {
+ continue;
}
+ return &output_socket;
}
return nullptr;
}
-static int link_socket_to_viewer(const bContext &C,
- bNode *viewer_bnode,
- bNode &bnode_to_view,
- bNodeSocket &bsocket_to_view)
+static void finalize_viewer_link(const bContext &C,
+ SpaceNode &snode,
+ bNode &viewer_node,
+ bNodeLink &viewer_link)
{
- SpaceNode &snode = *CTX_wm_space_node(&C);
- bNodeTree &btree = *snode.edittree;
+ Main *bmain = CTX_data_main(&C);
+ remove_links_to_unavailable_viewer_sockets(*snode.edittree, viewer_node);
+ viewer_link.flag &= ~NODE_LINK_MUTED;
+ viewer_node.flag &= ~NODE_MUTED;
+ viewer_node.flag |= NODE_DO_OUTPUT;
+ if (snode.edittree->type == NTREE_GEOMETRY) {
+ viewer_path::activate_geometry_node(*bmain, snode, viewer_node);
+ }
+ ED_node_tree_propagate_change(&C, bmain, snode.edittree);
+}
+
+static int view_socket(const bContext &C,
+ SpaceNode &snode,
+ bNodeTree &btree,
+ bNode &bnode_to_view,
+ bNodeSocket &bsocket_to_view)
+{
+ bNode *viewer_node = nullptr;
+ /* Try to find a viewer that is already active. */
+ LISTBASE_FOREACH (bNode *, node, &btree.nodes) {
+ if (is_viewer_node(*node)) {
+ if (node->flag & NODE_DO_OUTPUT) {
+ viewer_node = node;
+ break;
+ }
+ }
+ }
+
+ /* Try to reactivate existing viewer connection. */
+ for (bNodeLink *link : bsocket_to_view.directly_linked_links()) {
+ bNodeSocket &target_socket = *link->tosock;
+ bNode &target_node = *link->tonode;
+ if (is_viewer_socket(target_socket) && ELEM(viewer_node, nullptr, &target_node)) {
+ finalize_viewer_link(C, snode, target_node, *link);
+ return OPERATOR_FINISHED;
+ }
+ }
- if (viewer_bnode == nullptr) {
- /* Create a new viewer node if none exists. */
+ if (viewer_node == nullptr) {
+ LISTBASE_FOREACH (bNode *, node, &btree.nodes) {
+ if (is_viewer_node(*node)) {
+ viewer_node = node;
+ break;
+ }
+ }
+ }
+ if (viewer_node == nullptr) {
const int viewer_type = get_default_viewer_type(&C);
const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100,
bsocket_to_view.locy / UI_DPI_FAC};
- viewer_bnode = add_static_node(C, viewer_type, location);
- if (viewer_bnode == nullptr) {
- return OPERATOR_CANCELLED;
- }
+ viewer_node = add_static_node(C, viewer_type, location);
}
- bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_bnode, bsocket_to_view);
+ bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_node, bsocket_to_view);
if (viewer_bsocket == nullptr) {
return OPERATOR_CANCELLED;
}
-
- bNodeLink *link_to_change = nullptr;
- LISTBASE_FOREACH (bNodeLink *, link, &btree.links) {
+ bNodeLink *viewer_link = nullptr;
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
if (link->tosock == viewer_bsocket) {
- link_to_change = link;
+ viewer_link = link;
break;
}
}
-
- if (link_to_change == nullptr) {
- nodeAddLink(&btree, &bnode_to_view, &bsocket_to_view, viewer_bnode, viewer_bsocket);
+ if (viewer_link == nullptr) {
+ viewer_link = nodeAddLink(
+ &btree, &bnode_to_view, &bsocket_to_view, viewer_node, viewer_bsocket);
}
else {
- link_to_change->fromnode = &bnode_to_view;
- link_to_change->fromsock = &bsocket_to_view;
+ viewer_link->fromnode = &bnode_to_view;
+ viewer_link->fromsock = &bsocket_to_view;
BKE_ntree_update_tag_link_changed(&btree);
}
-
- remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode);
-
- if (btree.type == NTREE_GEOMETRY) {
- ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode);
- }
-
- ED_node_tree_propagate_change(&C, CTX_data_main(&C), &btree);
- return OPERATOR_FINISHED;
+ finalize_viewer_link(C, snode, *viewer_node, *viewer_link);
+ return OPERATOR_CANCELLED;
}
-static int node_link_viewer(const bContext &C, bNode &bnode_to_view)
+static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree *btree = snode.edittree;
btree->ensure_topology_cache();
- bNode *active_viewer_bnode = const_cast<bNode *>(get_existing_viewer(*btree));
- bNodeSocket *bsocket_to_view = const_cast<bNodeSocket *>(
- find_output_socket_to_be_viewed(active_viewer_bnode, bnode_to_view));
if (bsocket_to_view == nullptr) {
- return OPERATOR_FINISHED;
+ bsocket_to_view = determine_socket_to_view(bnode_to_view);
+ }
+
+ if (bsocket_to_view == nullptr) {
+ return OPERATOR_CANCELLED;
}
- return link_socket_to_viewer(C, active_viewer_bnode, bnode_to_view, *bsocket_to_view);
+
+ return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view);
}
/** \} */
@@ -690,7 +699,7 @@ static int node_link_viewer(const bContext &C, bNode &bnode_to_view)
/** \name Link to Viewer Node Operator
* \{ */
-static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_active_link_viewer_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNode *node = nodeGetActive(snode.edittree);
@@ -701,7 +710,15 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- if (viewer_linking::node_link_viewer(*C, *node) == OPERATOR_CANCELLED) {
+ bNodeSocket *socket_to_view = nullptr;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ if (socket->flag & SELECT) {
+ socket_to_view = socket;
+ break;
+ }
+ }
+
+ if (viewer_linking::node_link_viewer(*C, *node, socket_to_view) == OPERATOR_CANCELLED) {
return OPERATOR_CANCELLED;
}
@@ -788,7 +805,7 @@ static bool should_create_drag_link_search_menu(const bNodeTree &node_tree,
return true;
}
-static void draw_draglink_tooltip(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
+static void draw_draglink_tooltip(const bContext * /*C*/, ARegion * /*region*/, void *arg)
{
bNodeLinkDrag *nldrag = static_cast<bNodeLinkDrag *>(arg);
@@ -798,7 +815,8 @@ static void draw_draglink_tooltip(const bContext *UNUSED(C), ARegion *UNUSED(reg
nldrag->cursor[0];
const float y = nldrag->cursor[1] - 2.0f * UI_DPI_FAC;
- UI_icon_draw_ex(x, y, ICON_ADD, U.inv_dpi_fac, 1.0f, 0.0f, text_col, false);
+ UI_icon_draw_ex(
+ x, y, ICON_ADD, U.inv_dpi_fac, 1.0f, 0.0f, text_col, false, UI_NO_ICON_OVERLAY_TEXT);
}
static void draw_draglink_tooltip_activate(const ARegion &region, bNodeLinkDrag &nldrag)
@@ -817,7 +835,7 @@ static void draw_draglink_tooltip_deactivate(const ARegion &region, bNodeLinkDra
}
}
-static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
+static void node_link_update_header(bContext *C, bNodeLinkDrag * /*nldrag*/)
{
char header[UI_MAX_DRAW_STR];
@@ -915,7 +933,7 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links)
ED_node_tree_propagate_change(&C, bmain, &ntree);
- /* Ensure draglink tooltip is disabled. */
+ /* Ensure drag-link tool-tip is disabled. */
draw_draglink_tooltip_deactivate(*CTX_wm_region(&C), *nldrag);
ED_workspace_status_text(&C, nullptr);
@@ -1532,7 +1550,7 @@ void NODE_OT_links_mute(wmOperatorType *ot)
/** \name Detach Links Operator
* \{ */
-static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
+static int detach_links_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
@@ -1569,7 +1587,7 @@ void NODE_OT_links_detach(wmOperatorType *ot)
/** \name Set Parent Operator
* \{ */
-static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_parent_set_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
@@ -1651,7 +1669,7 @@ static void node_join_attach_recursive(bNode *node,
}
}
-static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_join_exec(bContext *C, wmOperator * /*op*/)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
@@ -1660,6 +1678,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
const Set<bNode *> selected_nodes = get_selected_nodes(ntree);
bNode *frame_node = nodeAddStaticNode(C, &ntree, NODE_FRAME);
+ nodeSetActive(&ntree, frame_node);
/* reset tags */
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
@@ -1721,7 +1740,7 @@ static bNode *node_find_frame_to_attach(ARegion &region,
return nullptr;
}
-static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static int node_attach_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
{
ARegion &region = *CTX_wm_region(C);
SpaceNode &snode = *CTX_wm_space_node(C);
@@ -1733,29 +1752,31 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
}
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) {
- if (node->flag & NODE_SELECT) {
- if (node->parent == nullptr) {
- /* disallow moving a parent into its child */
- if (nodeAttachNodeCheck(frame, node) == false) {
- /* attach all unparented nodes */
- nodeAttachNode(node, frame);
- }
+ if (!(node->flag & NODE_SELECT)) {
+ continue;
+ }
+
+ if (node->parent == nullptr) {
+ /* disallow moving a parent into its child */
+ if (nodeAttachNodeCheck(frame, node) == false) {
+ /* attach all unparented nodes */
+ nodeAttachNode(node, frame);
}
- else {
- /* attach nodes which share parent with the frame */
- bNode *parent;
- for (parent = frame->parent; parent; parent = parent->parent) {
- if (parent == node->parent) {
- break;
- }
+ }
+ else {
+ /* attach nodes which share parent with the frame */
+ bNode *parent;
+ for (parent = frame->parent; parent; parent = parent->parent) {
+ if (parent == node->parent) {
+ break;
}
+ }
- if (parent) {
- /* disallow moving a parent into its child */
- if (nodeAttachNodeCheck(frame, node) == false) {
- nodeDetachNode(node);
- nodeAttachNode(node, frame);
- }
+ if (parent) {
+ /* disallow moving a parent into its child */
+ if (nodeAttachNodeCheck(frame, node) == false) {
+ nodeDetachNode(node);
+ nodeAttachNode(node, frame);
}
}
}
@@ -1819,7 +1840,7 @@ static void node_detach_recursive(bNode *node)
}
/* detach the root nodes in the current selection */
-static int node_detach_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_detach_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
@@ -1864,100 +1885,55 @@ void NODE_OT_detach(wmOperatorType *ot)
/** \name Automatic Node Insert on Dragging
* \{ */
-/* prevent duplicate testing code below */
-static bool ed_node_link_conditions(ScrArea *area,
- bool test,
- SpaceNode **r_snode,
- bNode **r_select)
+static bNode *get_selected_node_for_insertion(bNodeTree &node_tree)
{
- SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr;
-
- *r_snode = snode;
- *r_select = nullptr;
-
- /* no unlucky accidents */
- if (area == nullptr || area->spacetype != SPACE_NODE) {
- return false;
- }
-
- if (!test) {
- /* no need to look for a node */
- return true;
- }
-
- bNode *node;
- bNode *select = nullptr;
- for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
+ bNode *selected_node = nullptr;
+ int selected_node_count = 0;
+ for (bNode *node : node_tree.all_nodes()) {
if (node->flag & SELECT) {
- if (select) {
- break;
- }
- select = node;
+ selected_node = node;
+ selected_node_count++;
+ }
+ if (selected_node_count > 1) {
+ return nullptr;
}
}
- /* only one selected */
- if (node || select == nullptr) {
- return false;
+ if (!selected_node) {
+ return nullptr;
}
-
- /* correct node */
- if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) {
- return false;
+ if (selected_node->input_sockets().is_empty() || selected_node->output_sockets().is_empty()) {
+ return nullptr;
}
-
- ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
-
- /* test node for links */
- LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
- if (node_link_is_hidden_or_dimmed(region->v2d, *link)) {
- continue;
- }
-
- if (link->tonode == select || link->fromnode == select) {
- return false;
- }
+ if (std::any_of(selected_node->input_sockets().begin(),
+ selected_node->input_sockets().end(),
+ [&](const bNodeSocket *socket) { return socket->is_directly_linked(); })) {
+ return nullptr;
}
-
- *r_select = select;
- return true;
+ if (std::any_of(selected_node->output_sockets().begin(),
+ selected_node->output_sockets().end(),
+ [&](const bNodeSocket *socket) { return socket->is_directly_linked(); })) {
+ return nullptr;
+ };
+ return selected_node;
}
-/** \} */
-
-} // namespace blender::ed::space_node
-
-/* -------------------------------------------------------------------- */
-/** \name Node Line Intersection Test
- * \{ */
-
-void ED_node_link_intersect_test(ScrArea *area, int test)
+void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion &region)
{
- using namespace blender;
- using namespace blender::ed::space_node;
-
- bNode *select;
- SpaceNode *snode;
- if (!ed_node_link_conditions(area, test, &snode, &select)) {
- return;
- }
+ bNodeTree &node_tree = *snode.edittree;
+ node_tree.ensure_topology_cache();
- /* clear flags */
- LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
- link->flag &= ~NODE_LINKFLAG_HILITE;
- }
+ node_insert_on_link_flags_clear(node_tree);
- if (test == 0) {
+ bNode *node_to_insert = get_selected_node_for_insertion(node_tree);
+ if (!node_to_insert) {
return;
}
- ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
-
/* find link to select/highlight */
bNodeLink *selink = nullptr;
float dist_best = FLT_MAX;
- LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
-
- if (node_link_is_hidden_or_dimmed(region->v2d, *link)) {
+ LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
+ if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue;
}
@@ -1969,10 +1945,10 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
* upper left node edge of a intersected line segment */
for (int i = 0; i < NODE_LINK_RESOL; i++) {
/* Check if the node rectangle intersects the line from this point to next one. */
- if (BLI_rctf_isect_segment(&select->totr, coords[i], coords[i + 1])) {
+ if (BLI_rctf_isect_segment(&node_to_insert->totr, coords[i], coords[i + 1])) {
/* store the shortest distance to the upper left edge
* of all intersections found so far */
- const float node_xy[] = {select->totr.xmin, select->totr.ymax};
+ const float node_xy[] = {node_to_insert->totr.xmin, node_to_insert->totr.ymax};
/* to be precise coords should be clipped by select->totr,
* but not done since there's no real noticeable difference */
@@ -1992,9 +1968,89 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
}
-/** \} */
+void node_insert_on_link_flags_clear(bNodeTree &node_tree)
+{
+ LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
+ link->flag &= ~NODE_LINKFLAG_HILITE;
+ }
+}
-namespace blender::ed::space_node {
+void node_insert_on_link_flags(Main &bmain, SpaceNode &snode)
+{
+ bNodeTree &node_tree = *snode.edittree;
+ node_tree.ensure_topology_cache();
+ bNode *node_to_insert = get_selected_node_for_insertion(node_tree);
+ if (!node_to_insert) {
+ return;
+ }
+
+ /* Find link to insert on. */
+ bNodeTree &ntree = *snode.edittree;
+ bNodeLink *old_link = nullptr;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
+ if (link->flag & NODE_LINKFLAG_HILITE) {
+ old_link = link;
+ break;
+ }
+ }
+ if (old_link == nullptr) {
+ return;
+ }
+
+ old_link->flag &= ~NODE_LINKFLAG_HILITE;
+
+ bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN);
+ bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT);
+
+ if (node_to_insert->type != NODE_REROUTE) {
+ /* Ignore main sockets when the types don't match. */
+ if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
+ !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type),
+ static_cast<eNodeSocketDatatype>(best_input->type))) {
+ best_input = nullptr;
+ }
+ if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
+ !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type),
+ static_cast<eNodeSocketDatatype>(old_link->tosock->type))) {
+ best_output = nullptr;
+ }
+ }
+
+ bNode *from_node = old_link->fromnode;
+ bNodeSocket *from_socket = old_link->fromsock;
+ bNode *to_node = old_link->tonode;
+
+ if (best_output != nullptr) {
+ /* Relink the "start" of the existing link to the newly inserted node. */
+ old_link->fromnode = node_to_insert;
+ old_link->fromsock = best_output;
+ BKE_ntree_update_tag_link_changed(&ntree);
+ }
+ else {
+ nodeRemLink(&ntree, old_link);
+ }
+
+ if (best_input != nullptr) {
+ /* Add a new link that connects the node on the left to the newly inserted node. */
+ nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input);
+ }
+
+ /* Set up insert offset data, it needs stuff from here. */
+ if ((snode.flag & SNODE_SKIP_INSOFFSET) == 0) {
+ BLI_assert(snode.runtime->iofsd == nullptr);
+ NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__);
+
+ iofsd->insert = node_to_insert;
+ iofsd->prev = from_node;
+ iofsd->next = to_node;
+
+ snode.runtime->iofsd = iofsd;
+ }
+
+ ED_node_tree_propagate_change(nullptr, &bmain, &ntree);
+}
+
+/** \} */
/* -------------------------------------------------------------------- */
/** \name Node Insert Offset Operator
@@ -2031,7 +2087,7 @@ static int get_main_socket_priority(const bNodeSocket *socket)
}
/** Get the "main" socket based on the node declaration or an heuristic. */
-static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out)
+bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out)
{
ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs;
@@ -2087,7 +2143,7 @@ static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketIn
return nullptr;
}
-static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
+static bool node_parents_offset_flag_enable_cb(bNode *parent, void * /*userdata*/)
{
/* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
parent->flag |= NODE_TEST;
@@ -2312,10 +2368,10 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
/**
* Modal handler for insert offset animation
*/
-static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static int node_insert_offset_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
SpaceNode *snode = CTX_wm_space_node(C);
- NodeInsertOfsData *iofsd = snode->runtime->iofsd;
+ NodeInsertOfsData *iofsd = static_cast<NodeInsertOfsData *>(op->customdata);
bool redraw = false;
if (!snode || event->type != TIMER || iofsd == nullptr ||
@@ -2323,7 +2379,7 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
return OPERATOR_PASS_THROUGH;
}
- const float duration = (float)iofsd->anim_timer->duration;
+ const float duration = float(iofsd->anim_timer->duration);
/* handle animation - do this before possibly aborting due to duration, since
* main thread might be so busy that node hasn't reached final position yet */
@@ -2355,7 +2411,6 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
node->anim_init_locx = node->anim_ofsx = 0.0f;
}
- snode->runtime->iofsd = nullptr;
MEM_freeN(iofsd);
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
@@ -2370,6 +2425,8 @@ static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent
{
const SpaceNode *snode = CTX_wm_space_node(C);
NodeInsertOfsData *iofsd = snode->runtime->iofsd;
+ snode->runtime->iofsd = nullptr;
+ op->customdata = iofsd;
if (!iofsd || !iofsd->insert) {
return OPERATOR_CANCELLED;
@@ -2408,84 +2465,3 @@ void NODE_OT_insert_offset(wmOperatorType *ot)
/** \} */
} // namespace blender::ed::space_node
-
-/* -------------------------------------------------------------------- */
-/** \name Note Link Insert
- * \{ */
-
-void ED_node_link_insert(Main *bmain, ScrArea *area)
-{
- using namespace blender::ed::space_node;
-
- bNode *node_to_insert;
- SpaceNode *snode;
- if (!ed_node_link_conditions(area, true, &snode, &node_to_insert)) {
- return;
- }
-
- /* Find link to insert on. */
- bNodeTree &ntree = *snode->edittree;
- bNodeLink *old_link = nullptr;
- LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
- if (link->flag & NODE_LINKFLAG_HILITE) {
- old_link = link;
- break;
- }
- }
- if (old_link == nullptr) {
- return;
- }
-
- old_link->flag &= ~NODE_LINKFLAG_HILITE;
-
- bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN);
- bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT);
-
- if (node_to_insert->type != NODE_REROUTE) {
- /* Ignore main sockets when the types don't match. */
- if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
- !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type),
- static_cast<eNodeSocketDatatype>(best_input->type))) {
- best_input = nullptr;
- }
- if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
- !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type),
- static_cast<eNodeSocketDatatype>(old_link->tosock->type))) {
- best_output = nullptr;
- }
- }
-
- bNode *from_node = old_link->fromnode;
- bNodeSocket *from_socket = old_link->fromsock;
- bNode *to_node = old_link->tonode;
-
- if (best_output != nullptr) {
- /* Relink the "start" of the existing link to the newly inserted node. */
- old_link->fromnode = node_to_insert;
- old_link->fromsock = best_output;
- BKE_ntree_update_tag_link_changed(&ntree);
- }
- else {
- nodeRemLink(&ntree, old_link);
- }
-
- if (best_input != nullptr) {
- /* Add a new link that connects the node on the left to the newly inserted node. */
- nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input);
- }
-
- /* Set up insert offset data, it needs stuff from here. */
- if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
- NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__);
-
- iofsd->insert = node_to_insert;
- iofsd->prev = from_node;
- iofsd->next = to_node;
-
- snode->runtime->iofsd = iofsd;
- }
-
- ED_node_tree_propagate_change(nullptr, bmain, snode->edittree);
-}
-
-/** \} */
diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc
index d93b205b1b7..7c39169415b 100644
--- a/source/blender/editors/space_node/node_select.cc
+++ b/source/blender/editors/space_node/node_select.cc
@@ -23,13 +23,14 @@
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
+#include "BKE_node_tree_update.h"
#include "BKE_workspace.h"
#include "ED_node.h" /* own include */
#include "ED_screen.h"
#include "ED_select_utils.h"
-#include "ED_spreadsheet.h"
#include "ED_view3d.h"
+#include "ED_viewer_path.hh"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -644,6 +645,15 @@ static bool node_mouse_select(bContext *C,
}
}
+ if (RNA_boolean_get(op->ptr, "clear_viewer")) {
+ if (node == nullptr) {
+ /* Disable existing active viewer. */
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ BKE_viewer_path_clear(&workspace->viewer_path);
+ WM_event_add_notifier(C, NC_VIEWER_PATH, nullptr);
+ }
+ }
+
if (!(changed || found)) {
return false;
}
@@ -655,7 +665,7 @@ static bool node_mouse_select(bContext *C,
ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
}
else if (node != nullptr && node->type == GEO_NODE_VIEWER) {
- ED_spreadsheet_context_paths_set_geometry_node(&bmain, &snode, node);
+ viewer_path::activate_geometry_node(bmain, snode, *node);
}
ED_node_set_active_viewer_key(&snode);
node_sort(*snode.edittree);
@@ -731,6 +741,12 @@ void NODE_OT_select(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", "");
+
+ RNA_def_boolean(ot->srna,
+ "clear_viewer",
+ false,
+ "Clear Viewer",
+ "Deactivate geometry nodes viewer when clicking in empty space");
}
/** \} */
@@ -843,8 +859,7 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
int x, y, radius;
float2 offset;
- float zoom = (float)(BLI_rcti_size_x(&region->winrct)) /
- (float)(BLI_rctf_size_x(&region->v2d.cur));
+ float zoom = float(BLI_rcti_size_x(&region->winrct)) / float(BLI_rctf_size_x(&region->v2d.cur));
const eSelectOp sel_op = ED_select_op_modal(
(eSelectOp)RNA_enum_get(op->ptr, "mode"),
@@ -867,7 +882,7 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
/* Frame nodes are selectable by their borders (including their whole rect - as for other
* nodes - would prevent selection of _only_ other nodes inside that frame. */
rctf frame_inside = node_frame_rect_inside(*node);
- const float radius_adjusted = (float)radius / zoom;
+ const float radius_adjusted = float(radius) / zoom;
BLI_rctf_pad(&frame_inside, -2.0f * radius_adjusted, -2.0f * radius_adjusted);
if (BLI_rctf_isect_circle(&node->totr, offset, radius_adjusted) &&
!BLI_rctf_isect_circle(&frame_inside, offset, radius_adjusted)) {
@@ -1119,7 +1134,7 @@ void NODE_OT_select_all(wmOperatorType *ot)
/** \name Select Linked To Operator
* \{ */
-static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_select_linked_to_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
@@ -1169,7 +1184,7 @@ void NODE_OT_select_linked_to(wmOperatorType *ot)
/** \name Select Linked From Operator
* \{ */
-static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_select_linked_from_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
@@ -1337,10 +1352,10 @@ static void node_find_create_label(const bNode *node, char *str, int maxlen)
/* Generic search invoke. */
static void node_find_update_fn(const bContext *C,
- void *UNUSED(arg),
+ void * /*arg*/,
const char *str,
uiSearchItems *items,
- const bool UNUSED(is_first))
+ const bool /*is_first*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
@@ -1368,7 +1383,7 @@ static void node_find_update_fn(const bContext *C,
BLI_string_search_free(search);
}
-static void node_find_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2)
+static void node_find_exec_fn(bContext *C, void * /*arg1*/, void *arg2)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNode *active = (bNode *)arg2;
@@ -1435,7 +1450,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op)
return block;
}
-static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
UI_popup_block_invoke(C, node_find_menu, op, nullptr);
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index 5fc194e02a4..1b7eadd336a 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -494,7 +494,7 @@ static int ui_node_item_name_compare(const void *a, const void *b)
return BLI_strcasecmp_natural(type_a->ui_name, type_b->ui_name);
}
-static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), const bNodeType *ntype)
+static bool ui_node_item_special_poll(const bNodeTree * /*ntree*/, const bNodeType *ntype)
{
if (STREQ(ntype->idname, "ShaderNodeUVAlongStroke")) {
/* TODO(sergey): Currently we don't have Freestyle nodes edited from
diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc
index 00756083580..f9b019e12ea 100644
--- a/source/blender/editors/space_node/node_view.cc
+++ b/source/blender/editors/space_node/node_view.cc
@@ -240,7 +240,7 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
NodeViewMove *nvm;
Image *ima;
ImBuf *ibuf;
- const float pad = 32.0f; /* better be bigger than scrollbars */
+ const float pad = 32.0f; /* Better be bigger than scroll-bars. */
void *lock;
@@ -273,7 +273,7 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
return OPERATOR_RUNNING_MODAL;
}
-static void snode_bg_viewmove_cancel(bContext *UNUSED(C), wmOperator *op)
+static void snode_bg_viewmove_cancel(bContext * /*C*/, wmOperator *op)
{
MEM_freeN(op->customdata);
op->customdata = nullptr;
@@ -341,7 +341,7 @@ void NODE_OT_backimage_zoom(wmOperatorType *ot)
/** \name Background Image Fit
* \{ */
-static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op))
+static int backimage_fit_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
@@ -464,9 +464,9 @@ bool ED_space_node_get_position(
/* map the mouse coords to the backdrop image space */
float bufx = ibuf->x * snode->zoom;
float bufy = ibuf->y * snode->zoom;
- fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f :
+ fpos[0] = (bufx > 0.0f ? (float(mval[0]) - 0.5f * region->winx - snode->xof) / bufx + 0.5f :
0.0f);
- fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f :
+ fpos[1] = (bufy > 0.0f ? (float(mval[1]) - 0.5f * region->winy - snode->yof) / bufy + 0.5f :
0.0f);
BKE_image_release_ibuf(ima, ibuf, lock);
@@ -498,13 +498,13 @@ bool ED_space_node_color_sample(
/* map the mouse coords to the backdrop image space */
bufx = ibuf->x * snode->zoom;
bufy = ibuf->y * snode->zoom;
- fx = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f);
- fy = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f);
+ fx = (bufx > 0.0f ? (float(mval[0]) - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f);
+ fy = (bufy > 0.0f ? (float(mval[1]) - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f);
if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
const float *fp;
uchar *cp;
- int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
+ int x = int(fx * ibuf->x), y = int(fy * ibuf->y);
CLAMP(x, 0, ibuf->x - 1);
CLAMP(y, 0, ibuf->y - 1);
@@ -555,15 +555,15 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
/* map the mouse coords to the backdrop image space */
bufx = ibuf->x * snode->zoom;
bufy = ibuf->y * snode->zoom;
- fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f :
+ fx = (bufx > 0.0f ? (float(event->mval[0]) - 0.5f * region->winx - snode->xof) / bufx + 0.5f :
0.0f);
- fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f :
+ fy = (bufy > 0.0f ? (float(event->mval[1]) - 0.5f * region->winy - snode->yof) / bufy + 0.5f :
0.0f);
if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
const float *fp;
uchar *cp;
- int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
+ int x = int(fx * ibuf->x), y = int(fy * ibuf->y);
CLAMP(x, 0, ibuf->x - 1);
CLAMP(y, 0, ibuf->y - 1);
@@ -584,10 +584,10 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
info->col[2] = cp[2];
info->col[3] = cp[3];
- info->colf[0] = (float)cp[0] / 255.0f;
- info->colf[1] = (float)cp[1] / 255.0f;
- info->colf[2] = (float)cp[2] / 255.0f;
- info->colf[3] = (float)cp[3] / 255.0f;
+ info->colf[0] = float(cp[0]) / 255.0f;
+ info->colf[1] = float(cp[1]) / 255.0f;
+ info->colf[2] = float(cp[2]) / 255.0f;
+ info->colf[3] = float(cp[3]) / 255.0f;
copy_v4_v4(info->linearcol, info->colf);
IMB_colormanagement_colorspace_to_scene_linear_v4(
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index fae3eb1a143..c993fa57d76 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -5,6 +5,7 @@
* \ingroup spnode
*/
+#include "DNA_ID.h"
#include "DNA_gpencil_types.h"
#include "DNA_light_types.h"
#include "DNA_material_types.h"
@@ -14,6 +15,7 @@
#include "MEM_guardedalloc.h"
#include "BKE_context.h"
+#include "BKE_gpencil.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_node.h"
@@ -27,6 +29,10 @@
#include "UI_resources.h"
#include "UI_view2d.h"
+#include "DEG_depsgraph.h"
+
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -178,7 +184,7 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value)
value += strlen(path->display_name);
}
else {
- sprintf(value, "/%s", path->display_name);
+ BLI_sprintf(value, "/%s", path->display_name);
value += strlen(path->display_name) + 1;
}
}
@@ -188,6 +194,13 @@ void ED_node_set_active_viewer_key(SpaceNode *snode)
{
bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last;
if (snode->nodetree && path) {
+ /* A change in active viewer may result in the change of the output node used by the
+ * compositor, so we need to get notified about such changes. */
+ if (snode->nodetree->active_viewer_key.value != path->parent_key.value) {
+ DEG_id_tag_update(&snode->nodetree->id, ID_RECALC_NTREE_OUTPUT);
+ WM_main_add_notifier(NC_NODE, nullptr);
+ }
+
snode->nodetree->active_viewer_key = path->parent_key;
}
}
@@ -216,7 +229,7 @@ float2 space_node_group_offset(const SpaceNode &snode)
/* ******************** default callbacks for node space ***************** */
-static SpaceLink *node_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
+static SpaceLink *node_create(const ScrArea * /*area*/, const Scene * /*scene*/)
{
SpaceNode *snode = MEM_cnew<SpaceNode>("initnode");
snode->spacetype = SPACE_NODE;
@@ -297,12 +310,12 @@ static void node_free(SpaceLink *sl)
if (snode->runtime) {
snode->runtime->linkdrag.reset();
- MEM_freeN(snode->runtime);
+ MEM_delete(snode->runtime);
}
}
/* spacetype; init callback */
-static void node_init(wmWindowManager *UNUSED(wm), ScrArea *area)
+static void node_init(wmWindowManager * /*wm*/, ScrArea *area)
{
SpaceNode *snode = (SpaceNode *)area->spacedata.first;
@@ -637,57 +650,55 @@ static void node_main_region_draw(const bContext *C, ARegion *region)
/* ************* dropboxes ************* */
-static bool node_group_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
+static bool node_group_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return WM_drag_is_ID_type(drag, ID_NT);
}
-static bool node_object_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
+static bool node_object_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return WM_drag_is_ID_type(drag, ID_OB);
}
-static bool node_collection_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event))
+static bool node_collection_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return WM_drag_is_ID_type(drag, ID_GR);
}
-static bool node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
+static bool node_ima_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
if (drag->type == WM_DRAG_PATH) {
/* rule might not work? */
- return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE));
+ return ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE);
}
return WM_drag_is_ID_type(drag, ID_IM);
}
-static bool node_mask_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
+static bool node_mask_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
{
return WM_drag_is_ID_type(drag, ID_MSK);
}
-static void node_group_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
+static void node_group_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
- RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid);
+ RNA_int_set(drop->ptr, "session_uuid", int(id->session_uuid));
}
-static void node_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
+static void node_id_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
- RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid);
+ RNA_int_set(drop->ptr, "session_uuid", int(id->session_uuid));
}
-static void node_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
+static void node_id_path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
if (id) {
- RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid);
+ RNA_int_set(drop->ptr, "session_uuid", int(id->session_uuid));
RNA_struct_property_unset(drop->ptr, "filepath");
}
else if (drag->path[0]) {
@@ -736,7 +747,7 @@ static void node_dropboxes()
/* ************* end drop *********** */
/* add handlers, stuff you only do once or on area/region changes */
-static void node_header_region_init(wmWindowManager *UNUSED(wm), ARegion *region)
+static void node_header_region_init(wmWindowManager * /*wm*/, ARegion *region)
{
ED_region_header_init(region);
}
@@ -820,6 +831,9 @@ static void node_region_listener(const wmRegionListenerParams *params)
ED_region_tag_redraw(region);
}
break;
+ case NC_VIEWER_PATH:
+ ED_region_tag_redraw(region);
+ break;
}
}
@@ -973,7 +987,7 @@ static void node_id_remap_cb(ID *old_id, ID *new_id, void *user_data)
}
}
-static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, const IDRemapper *mappings)
+static void node_id_remap(ScrArea * /*area*/, SpaceLink *slink, const IDRemapper *mappings)
{
/* Although we should be able to perform all the mappings in a single go this lead to issues when
* running the python test cases. Somehow the nodetree/edittree weren't updated to the new
@@ -1011,6 +1025,81 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
}
}
+static void node_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
+{
+ SpaceNode *snode = (SpaceNode *)sl;
+
+ if (snode->gpd) {
+ BLO_read_data_address(reader, &snode->gpd);
+ BKE_gpencil_blend_read_data(reader, snode->gpd);
+ }
+
+ BLO_read_list(reader, &snode->treepath);
+ snode->edittree = nullptr;
+ snode->runtime = nullptr;
+}
+
+static void node_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
+{
+ SpaceNode *snode = (SpaceNode *)sl;
+
+ /* node tree can be stored locally in id too, link this first */
+ BLO_read_id_address(reader, parent_id->lib, &snode->id);
+ BLO_read_id_address(reader, parent_id->lib, &snode->from);
+
+ bNodeTree *ntree = snode->id ? ntreeFromID(snode->id) : nullptr;
+ if (ntree) {
+ snode->nodetree = ntree;
+ }
+ else {
+ BLO_read_id_address(reader, parent_id->lib, &snode->nodetree);
+ }
+
+ bNodeTreePath *path;
+ for (path = static_cast<bNodeTreePath *>(snode->treepath.first); path; path = path->next) {
+ if (path == snode->treepath.first) {
+ /* first nodetree in path is same as snode->nodetree */
+ path->nodetree = snode->nodetree;
+ }
+ else {
+ BLO_read_id_address(reader, parent_id->lib, &path->nodetree);
+ }
+
+ if (!path->nodetree) {
+ break;
+ }
+ }
+
+ /* remaining path entries are invalid, remove */
+ bNodeTreePath *path_next;
+ for (; path; path = path_next) {
+ path_next = path->next;
+
+ BLI_remlink(&snode->treepath, path);
+ MEM_freeN(path);
+ }
+
+ /* edittree is just the last in the path,
+ * set this directly since the path may have been shortened above */
+ if (snode->treepath.last) {
+ path = static_cast<bNodeTreePath *>(snode->treepath.last);
+ snode->edittree = path->nodetree;
+ }
+ else {
+ snode->edittree = nullptr;
+ }
+}
+
+static void node_blend_write(BlendWriter *writer, SpaceLink *sl)
+{
+ SpaceNode *snode = (SpaceNode *)sl;
+ BLO_write_struct(writer, SpaceNode, snode);
+
+ LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) {
+ BLO_write_struct(writer, bNodeTreePath, path);
+ }
+}
+
} // namespace blender::ed::space_node
void ED_spacetype_node()
@@ -1038,6 +1127,9 @@ void ED_spacetype_node()
st->space_subtype_item_extend = node_space_subtype_item_extend;
st->space_subtype_get = node_space_subtype_get;
st->space_subtype_set = node_space_subtype_set;
+ st->blend_read_data = node_blend_read_data;
+ st->blend_read_lib = node_blend_read_lib;
+ st->blend_write = node_blend_write;
/* regions: main window */
art = MEM_cnew<ARegionType>("spacetype node region");
@@ -1088,5 +1180,8 @@ void ED_spacetype_node()
art->draw = node_toolbar_region_draw;
BLI_addhead(&st->regiontypes, art);
+ WM_menutype_add(MEM_new<MenuType>(__func__, add_catalog_assets_menu_type()));
+ WM_menutype_add(MEM_new<MenuType>(__func__, add_root_catalogs_menu_type()));
+
BKE_spacetype_register(st);
}