Files
test2/source/blender/nodes/NOD_socket_items_ops.hh
Bastien Montagne 87a4c0d3a8 Refactor: Make Library.runtime an allocated pointer.
Move `Library.runtime` to be a pointer, move the related
`LibraryRuntime` struct to `BKE_library.hh`. Similar to e.g.
Mesh.runtime, that pointer is expected to always be valid, and is
allocated at readtime or when creating a new Library ID.

Related smaller changes:
* Write code now uses standard ID writing codepath for Library IDs too.
  * Runtime pointer is reset to nullptr before writing.
* Looking up a library by its absolute path is now handled through a
  dedicated utils, `search_filepath_abs`, instead of using
  `BLI_findstring`.

Pull Request: https://projects.blender.org/blender/blender/pulls/134188
2025-02-07 17:47:16 +01:00

245 lines
8.2 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "NOD_socket_items.hh"
#include "WM_api.hh"
#include "BKE_context.hh"
#include "BKE_library.hh"
#include "BKE_main_invariants.hh"
#include "BKE_node_tree_update.hh"
#include "BKE_node_tree_zones.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.hh"
#include "ED_node.hh"
#include "DNA_space_types.h"
namespace blender::nodes::socket_items::ops {
inline PointerRNA get_active_node_to_operate_on(bContext *C, const int node_type)
{
SpaceNode *snode = CTX_wm_space_node(C);
if (!snode) {
return PointerRNA_NULL;
}
if (!snode->edittree) {
return PointerRNA_NULL;
}
if (!ID_IS_EDITABLE(snode->edittree)) {
return PointerRNA_NULL;
}
const bke::bNodeTreeZones *zones = snode->edittree->zones();
if (!zones) {
return PointerRNA_NULL;
}
bNode *active_node = bke::node_get_active(snode->edittree);
if (!active_node) {
return PointerRNA_NULL;
}
if (const bke::bNodeTreeZone *zone = zones->get_zone_by_node(active_node->identifier)) {
if (zone->input_node == active_node) {
/* Assume the data is generally stored on the output and not the input node. */
active_node = const_cast<bNode *>(zone->output_node);
}
}
if (active_node->type_legacy != node_type) {
return PointerRNA_NULL;
}
return RNA_pointer_create_discrete(&snode->edittree->id, &RNA_Node, active_node);
}
inline void update_after_node_change(bContext *C, const PointerRNA node_ptr)
{
bNode *node = static_cast<bNode *>(node_ptr.data);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(node_ptr.owner_id);
BKE_ntree_update_tag_node_property(ntree, node);
BKE_main_ensure_invariants(*CTX_data_main(C), ntree->id);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
template<typename Accessor> inline bool editable_node_active_poll(bContext *C)
{
return get_active_node_to_operate_on(C, Accessor::node_type).data != nullptr;
}
template<typename Accessor>
inline void remove_active_item(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator * /*op*/) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
SocketItemsRef ref = Accessor::get_items_from_node(node);
if (*ref.items_num > 0) {
dna::array::remove_index(
ref.items, ref.items_num, ref.active_index, *ref.active_index, Accessor::destruct_item);
update_after_node_change(C, node_ptr);
}
return OPERATOR_FINISHED;
};
}
template<typename Accessor>
inline void remove_item_by_index(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator *op) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
const int index_to_remove = RNA_int_get(op->ptr, "index");
SocketItemsRef ref = Accessor::get_items_from_node(node);
dna::array::remove_index(
ref.items, ref.items_num, ref.active_index, index_to_remove, Accessor::destruct_item);
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
RNA_def_int(ot->srna, "index", 0, 0, INT32_MAX, "Index", "Index to remove", 0, INT32_MAX);
}
template<typename Accessor>
inline void add_item(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator * /*op*/) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
SocketItemsRef ref = Accessor::get_items_from_node(node);
const typename Accessor::ItemT *active_item = nullptr;
int dst_index = *ref.items_num;
if (ref.active_index) {
const int old_active_index = *ref.active_index;
if (old_active_index >= 0 && old_active_index < *ref.items_num) {
active_item = &(*ref.items)[old_active_index];
dst_index = active_item ? old_active_index + 1 : *ref.items_num;
}
}
if constexpr (Accessor::has_type && Accessor::has_name) {
socket_items::add_item_with_socket_type_and_name<Accessor>(
node,
active_item ?
Accessor::get_socket_type(*active_item) :
(Accessor::supports_socket_type(SOCK_GEOMETRY) ? SOCK_GEOMETRY : SOCK_FLOAT),
/* Empty name so it is based on the type. */
active_item ? active_item->name : "");
}
else if constexpr (!Accessor::has_type && Accessor::has_name) {
socket_items::add_item_with_name<Accessor>(node, active_item ? active_item->name : "");
}
else if constexpr (!Accessor::has_type && !Accessor::has_name) {
socket_items::add_item<Accessor>(node);
}
else {
BLI_assert_unreachable();
}
dna::array::move_index(*ref.items, *ref.items_num, *ref.items_num - 1, dst_index);
if (ref.active_index) {
*ref.active_index = dst_index;
}
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
}
enum class MoveDirection {
Up = 0,
Down = 1,
};
template<typename Accessor>
inline void move_active_item(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator *op) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
const MoveDirection direction = MoveDirection(RNA_enum_get(op->ptr, "direction"));
SocketItemsRef ref = Accessor::get_items_from_node(node);
const int old_active_index = *ref.active_index;
if (direction == MoveDirection::Up && old_active_index > 0) {
dna::array::move_index(*ref.items, *ref.items_num, old_active_index, old_active_index - 1);
*ref.active_index -= 1;
}
else if (direction == MoveDirection::Down && old_active_index < *ref.items_num - 1) {
dna::array::move_index(*ref.items, *ref.items_num, old_active_index, old_active_index + 1);
*ref.active_index += 1;
}
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
static const EnumPropertyItem direction_items[] = {
{int(MoveDirection::Up), "UP", 0, "Up", ""},
{int(MoveDirection::Down), "DOWN", 0, "Down", ""},
{0, nullptr, 0, nullptr, nullptr},
};
RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Move direction");
}
/**
* Creates simple operators for adding, removing and moving items.
* The idnames are passed in explicitly, so that they are more searchable compared to when they
* would be computed automatically.
*/
template<typename Accessor> inline void make_common_operators()
{
WM_operatortype_append([](wmOperatorType *ot) {
socket_items::ops::add_item<Accessor>(
ot, "Add Item", Accessor::operator_idnames::add_item, "Add item below active item");
});
WM_operatortype_append([](wmOperatorType *ot) {
socket_items::ops::remove_active_item<Accessor>(
ot, "Remove Item", Accessor::operator_idnames::remove_item, "Remove active item");
});
WM_operatortype_append([](wmOperatorType *ot) {
socket_items::ops::move_active_item<Accessor>(
ot, "Move Item", Accessor::operator_idnames::move_item, "Move active item");
});
}
} // namespace blender::nodes::socket_items::ops