Nodes: Add PanelDeclarationBuilder for panels in builtin nodes
Node groups already have panels, but they modify the node declaration
directly, which is not something we want to do for builtin nodes. For
those the `PanelDeclarationBuilder` should be used.
`PanelDeclarationBuilder` has `add_input`/`add_output` methods just like `NodeDeclarationBuilder`. Adding sockets to a panel increases its size by one. All sockets must be added in order: Adding sockets or panels to the root `NodeDeclarationBuilder` after a panel will complete the panel and adding more sockets to it after that will fail. This is to enforce a stable item order where indices don't change after adding a socket, which is important for things like field dependencies.
Example:
```cpp
static void node_declare(NodeDeclarationBuilder &b)
{
// Currently this is necessary to enable custom layouts and panels.
// Will go away eventually when most nodes uses custom layout.
b.use_custom_socket_order();
// Create a panel.
PanelDeclarationBuilder &pb = b.add_panel("My Panel").description("A demo panel").default_closed(true);
// Add to the panel instead of the root layout.
pb.add_input<decl::Color>("Color").default_value({0.8f, 0.8f, 0.8f, 1.0f});
pb.add_input<decl::Float>("Weight").unavailable();
// Continue socket declarations as usual.
b.add_output<decl::Shader>("BSDF");
// !!! Warning: continuing the panel after other items is not allowed and will show an error.
pb.add_output<decl::Float>("Bad Socket");
}
```
Pull Request: https://projects.blender.org/blender/blender/pulls/111695
This commit is contained in:
@@ -513,17 +513,34 @@ class PanelDeclaration : public ItemDeclaration {
|
||||
|
||||
class PanelDeclarationBuilder {
|
||||
protected:
|
||||
using Self = PanelDeclarationBuilder;
|
||||
NodeDeclarationBuilder *node_decl_builder_ = nullptr;
|
||||
PanelDeclaration *decl_;
|
||||
/**
|
||||
* Panel is complete once items are added after it.
|
||||
* Completed panels are locked and no more items can be added.
|
||||
*/
|
||||
bool is_complete_ = false;
|
||||
|
||||
friend class NodeDeclarationBuilder;
|
||||
|
||||
public:
|
||||
PanelDeclarationBuilder &default_closed(bool collapsed)
|
||||
Self &description(std::string value = "")
|
||||
{
|
||||
decl_->default_collapsed = collapsed;
|
||||
decl_->description = std::move(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &default_closed(bool closed)
|
||||
{
|
||||
decl_->default_collapsed = closed;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_input(StringRef name, StringRef identifier = "");
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_output(StringRef name, StringRef identifier = "");
|
||||
};
|
||||
|
||||
using PanelDeclarationPtr = std::unique_ptr<PanelDeclaration>;
|
||||
@@ -549,6 +566,9 @@ class NodeDeclaration {
|
||||
|
||||
friend NodeDeclarationBuilder;
|
||||
|
||||
/** Returns true if the declaration is considered valid. */
|
||||
bool is_valid() const;
|
||||
|
||||
bool matches(const bNode &node) const;
|
||||
Span<SocketDeclaration *> sockets(eNodeSocketInOut in_out) const;
|
||||
|
||||
@@ -565,8 +585,12 @@ class NodeDeclarationBuilder {
|
||||
NodeDeclaration &declaration_;
|
||||
Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> input_builders_;
|
||||
Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> output_builders_;
|
||||
Vector<std::unique_ptr<PanelDeclarationBuilder>> panel_builders_;
|
||||
bool is_function_node_ = false;
|
||||
|
||||
private:
|
||||
friend PanelDeclarationBuilder;
|
||||
|
||||
public:
|
||||
NodeDeclarationBuilder(NodeDeclaration &declaration);
|
||||
|
||||
@@ -581,10 +605,13 @@ class NodeDeclarationBuilder {
|
||||
|
||||
void finalize();
|
||||
|
||||
void use_custom_socket_order(bool enable = true);
|
||||
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_input(StringRef name, StringRef identifier = "");
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_output(StringRef name, StringRef identifier = "");
|
||||
PanelDeclarationBuilder &add_panel(StringRef name, int identifier = -1);
|
||||
|
||||
aal::RelationsInNode &get_anonymous_attribute_relations()
|
||||
{
|
||||
@@ -599,6 +626,10 @@ class NodeDeclarationBuilder {
|
||||
typename DeclType::Builder &add_socket(StringRef name,
|
||||
StringRef identifier,
|
||||
eNodeSocketInOut in_out);
|
||||
|
||||
/* Mark the most recent builder as 'complete' when changing builders
|
||||
* so no more items can be added. */
|
||||
void set_active_panel_builder(const PanelDeclarationBuilder *panel_builder);
|
||||
};
|
||||
|
||||
namespace implicit_field_inputs {
|
||||
@@ -758,6 +789,38 @@ inline void SocketDeclaration::make_available(bNode &node) const
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #PanelDeclarationBuilder Inline Methods
|
||||
* \{ */
|
||||
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &PanelDeclarationBuilder::add_input(StringRef name,
|
||||
StringRef identifier)
|
||||
{
|
||||
if (is_complete_) {
|
||||
static typename DeclType::Builder dummy_builder = {};
|
||||
BLI_assert_unreachable();
|
||||
return dummy_builder;
|
||||
}
|
||||
++this->decl_->num_child_decls;
|
||||
return node_decl_builder_->add_socket<DeclType>(name, identifier, SOCK_IN);
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &PanelDeclarationBuilder::add_output(StringRef name,
|
||||
StringRef identifier)
|
||||
{
|
||||
if (is_complete_) {
|
||||
static typename DeclType::Builder dummy_builder = {};
|
||||
BLI_assert_unreachable();
|
||||
return dummy_builder;
|
||||
}
|
||||
++this->decl_->num_child_decls;
|
||||
return node_decl_builder_->add_socket<DeclType>(name, identifier, SOCK_OUT);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #NodeDeclarationBuilder Inline Methods
|
||||
* \{ */
|
||||
@@ -767,10 +830,16 @@ inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declarati
|
||||
{
|
||||
}
|
||||
|
||||
inline void NodeDeclarationBuilder::use_custom_socket_order(bool enable)
|
||||
{
|
||||
declaration_.use_custom_socket_order = enable;
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
inline typename DeclType::Builder &NodeDeclarationBuilder::add_input(StringRef name,
|
||||
StringRef identifier)
|
||||
{
|
||||
set_active_panel_builder(nullptr);
|
||||
return this->add_socket<DeclType>(name, identifier, SOCK_IN);
|
||||
}
|
||||
|
||||
@@ -778,6 +847,7 @@ template<typename DeclType>
|
||||
inline typename DeclType::Builder &NodeDeclarationBuilder::add_output(StringRef name,
|
||||
StringRef identifier)
|
||||
{
|
||||
set_active_panel_builder(nullptr);
|
||||
return this->add_socket<DeclType>(name, identifier, SOCK_OUT);
|
||||
}
|
||||
|
||||
@@ -802,9 +872,11 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
|
||||
socket_decl->in_out = in_out;
|
||||
socket_decl_builder->index_ = declarations.append_and_get_index(socket_decl.get());
|
||||
declaration_.items.append(std::move(socket_decl));
|
||||
|
||||
Builder &socket_decl_builder_ref = *socket_decl_builder;
|
||||
((in_out == SOCK_IN) ? input_builders_ : output_builders_)
|
||||
.append(std::move(socket_decl_builder));
|
||||
|
||||
return socket_decl_builder_ref;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "NOD_socket_declarations.hh"
|
||||
#include "NOD_socket_declarations_geometry.hh"
|
||||
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_geometry_fields.hh"
|
||||
@@ -33,6 +34,8 @@ void build_node_declaration_dynamic(const bNodeTree &node_tree,
|
||||
|
||||
void NodeDeclarationBuilder::finalize()
|
||||
{
|
||||
BLI_assert(declaration_.is_valid());
|
||||
|
||||
if (is_function_node_) {
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : input_builders_) {
|
||||
SocketDeclaration &socket_decl = *socket_builder->declaration();
|
||||
@@ -97,6 +100,20 @@ void NodeDeclarationBuilder::finalize()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDeclarationBuilder::set_active_panel_builder(const PanelDeclarationBuilder *panel_builder)
|
||||
{
|
||||
if (panel_builders_.is_empty()) {
|
||||
BLI_assert(panel_builder == nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(!panel_builder || !panel_builder->is_complete_);
|
||||
PanelDeclarationBuilder *last_panel_builder = panel_builders_.last().get();
|
||||
if (last_panel_builder != panel_builder) {
|
||||
last_panel_builder->is_complete_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace anonymous_attribute_lifetime {
|
||||
|
||||
bool operator==(const RelationsInNode &a, const RelationsInNode &b)
|
||||
@@ -141,6 +158,88 @@ std::ostream &operator<<(std::ostream &stream, const RelationsInNode &relations)
|
||||
|
||||
} // namespace anonymous_attribute_lifetime
|
||||
|
||||
bool NodeDeclaration::is_valid() const
|
||||
{
|
||||
if (!this->use_custom_socket_order) {
|
||||
/* Skip validation for conventional socket layouts. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Validation state for the interface root items as well as any panel content. */
|
||||
struct ValidationState {
|
||||
/* Remaining number of items expected in a panel */
|
||||
int remaining_items = 0;
|
||||
/* Sockets first, followed by panels. */
|
||||
NodeTreeInterfaceItemType item_type = NODE_INTERFACE_SOCKET;
|
||||
/* Output sockets first, followed by input sockets. */
|
||||
eNodeSocketInOut socket_in_out = SOCK_OUT;
|
||||
};
|
||||
|
||||
Stack<ValidationState> panel_states;
|
||||
panel_states.push({});
|
||||
|
||||
for (const ItemDeclarationPtr &item_decl : items) {
|
||||
BLI_assert(panel_states.size() >= 1);
|
||||
ValidationState &state = panel_states.peek();
|
||||
|
||||
if (const SocketDeclaration *socket_decl = dynamic_cast<const SocketDeclaration *>(
|
||||
item_decl.get()))
|
||||
{
|
||||
if (state.item_type != NODE_INTERFACE_SOCKET) {
|
||||
std::cout << "Socket added after panel" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.socket_in_out == SOCK_OUT && socket_decl->in_out == SOCK_IN) {
|
||||
/* Start of input sockets. */
|
||||
state.socket_in_out = SOCK_IN;
|
||||
}
|
||||
if (socket_decl->in_out != state.socket_in_out) {
|
||||
std::cout << "Output socket added after input socket" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Item counting for the panels, but ignore for root items. */
|
||||
if (panel_states.size() > 1) {
|
||||
if (state.remaining_items <= 0) {
|
||||
std::cout << "More sockets than expected in panel" << std::endl;
|
||||
return false;
|
||||
}
|
||||
--state.remaining_items;
|
||||
/* Panel closed after last item is added. */
|
||||
if (state.remaining_items == 0) {
|
||||
panel_states.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (const PanelDeclaration *panel_decl = dynamic_cast<const PanelDeclaration *>(
|
||||
item_decl.get()))
|
||||
{
|
||||
if (state.item_type == NODE_INTERFACE_SOCKET) {
|
||||
/* Start of panels section */
|
||||
state.item_type = NODE_INTERFACE_PANEL;
|
||||
}
|
||||
BLI_assert(state.item_type == NODE_INTERFACE_PANEL);
|
||||
|
||||
if (panel_decl->num_child_decls > 0) {
|
||||
/* New panel started. */
|
||||
panel_states.push({panel_decl->num_child_decls});
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* All panels complete? */
|
||||
if (panel_states.size() != 1) {
|
||||
std::cout << "Incomplete last panel" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NodeDeclaration::matches(const bNode &node) const
|
||||
{
|
||||
const bNodeSocket *current_input = static_cast<bNodeSocket *>(node.inputs.first);
|
||||
@@ -237,6 +336,31 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
PanelDeclarationBuilder &NodeDeclarationBuilder::add_panel(StringRef name, int identifier)
|
||||
{
|
||||
std::unique_ptr<PanelDeclaration> panel_decl = std::make_unique<PanelDeclaration>();
|
||||
std::unique_ptr<PanelDeclarationBuilder> panel_decl_builder =
|
||||
std::make_unique<PanelDeclarationBuilder>();
|
||||
panel_decl_builder->decl_ = &*panel_decl;
|
||||
|
||||
panel_decl_builder->node_decl_builder_ = this;
|
||||
if (identifier >= 0) {
|
||||
panel_decl->identifier = identifier;
|
||||
}
|
||||
else {
|
||||
/* Use index as identifier. */
|
||||
panel_decl->identifier = declaration_.items.size();
|
||||
}
|
||||
panel_decl->name = name;
|
||||
declaration_.items.append(std::move(panel_decl));
|
||||
|
||||
PanelDeclarationBuilder &builder_ref = *panel_decl_builder;
|
||||
panel_builders_.append(std::move(panel_decl_builder));
|
||||
set_active_panel_builder(&builder_ref);
|
||||
|
||||
return builder_ref;
|
||||
}
|
||||
|
||||
void PanelDeclaration::build(bNodePanelState &panel) const
|
||||
{
|
||||
panel = {0};
|
||||
|
||||
Reference in New Issue
Block a user