From a239bfc4ddc3ecd33f83f24c01198d573c7d15de Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 11 Oct 2024 12:20:58 +0200 Subject: [PATCH] Refactor: Nodes: improve drawing of nodes based on node declaration The main goal is to simplify adding support for nested node panels. The patch makes use of the updated recursive node declarations introduced in 6ffc585fb864fbff9f2ee4c99cee73654ac5c75f. The main changes are: * Rewritten node drawing in a way that makes ui design decisions like panel visibility and margins more explicit. Especially the handling of margins is much better now imo. Previously, it was very hard to change the margin for specific cases without accidentally breaking other situations. Now each possible case has an explicit margin. This needs a few more lines of code but is much easier to work with. * Rewritten node drawing in panel (sidebar + material properties) using the new ways to iterate over the declaration. * It's possible to add custom layouts at any point in the node declaration now. This also replaces the need for having a `draw_buttons` callback for panels. Pull Request: https://projects.blender.org/blender/blender/pulls/128822 --- source/blender/blenkernel/BKE_node_runtime.hh | 13 +- .../blender/blenkernel/intern/node_runtime.cc | 11 + .../interface_template_node_inputs.cc | 94 +- .../blender/editors/space_node/node_draw.cc | 1086 ++++++++++------- .../editors/space_node/node_templates.cc | 93 +- source/blender/makesdna/DNA_node_types.h | 3 + source/blender/nodes/NOD_node_declaration.hh | 39 +- .../nodes/node_geo_attribute_capture.cc | 2 + .../nodes/geometry/nodes/node_geo_bake.cc | 2 + .../node_geo_foreach_geometry_element.cc | 2 + .../nodes/geometry/nodes/node_geo_warning.cc | 2 + source/blender/nodes/intern/node_common.cc | 21 +- .../blender/nodes/intern/node_declaration.cc | 66 +- .../nodes/node_shader_bsdf_principled.cc | 20 +- 14 files changed, 840 insertions(+), 614 deletions(-) diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index 2b909966e98..65a59a05084 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -262,16 +262,19 @@ class bNodeSocketRuntime : NonCopyable, NonMovable { int index_in_inout_sockets = -1; }; +struct bNodePanelExtent { + float min_y; + float max_y; + bool fill_node_end = false; +}; + class bNodePanelRuntime : NonCopyable, NonMovable { public: /* The vertical location of the panel in the tree, calculated while drawing the nodes and invalid * if the node tree hasn't been drawn yet. In the node tree's "world space" (the same as * #bNode::runtime::totr). */ - float location_y; - /* Vertical start location of the panel content. */ - float min_content_y; - /* Vertical end location of the panel content. */ - float max_content_y; + std::optional header_center_y; + std::optional content_extent; }; /** diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 43eec396d62..b7230d8f256 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -12,6 +12,7 @@ #include "BLI_task.hh" #include "NOD_geometry_nodes_lazy_function.hh" +#include "NOD_node_declaration.hh" namespace blender::bke::node_tree_runtime { @@ -651,3 +652,13 @@ const bNode *bNodeTree::find_nested_node(const int32_t nested_node_id, } return group->find_nested_node(ref->path.id_in_node, r_tree); } + +const bNodeSocket &bNode::socket_by_decl(const blender::nodes::SocketDeclaration &decl) const +{ + return decl.in_out == SOCK_IN ? this->input_socket(decl.index) : this->output_socket(decl.index); +} + +bNodeSocket &bNode::socket_by_decl(const blender::nodes::SocketDeclaration &decl) +{ + return decl.in_out == SOCK_IN ? this->input_socket(decl.index) : this->output_socket(decl.index); +} diff --git a/source/blender/editors/interface/templates/interface_template_node_inputs.cc b/source/blender/editors/interface/templates/interface_template_node_inputs.cc index 4699f8eeaf1..122ede00c39 100644 --- a/source/blender/editors/interface/templates/interface_template_node_inputs.cc +++ b/source/blender/editors/interface/templates/interface_template_node_inputs.cc @@ -28,6 +28,7 @@ * \{ */ using blender::nodes::ItemDeclaration; +using blender::nodes::LayoutDeclaration; using blender::nodes::NodeDeclaration; using blender::nodes::PanelDeclaration; using blender::nodes::SocketDeclaration; @@ -66,54 +67,37 @@ static void draw_node_input(bContext *C, socket.typeinfo->draw(C, row, &socket_ptr, node_ptr, text); } -static void draw_node_input(bContext *C, - uiLayout *layout, - PointerRNA *node_ptr, - StringRefNull identifier) +static void draw_node_inputs_recursive(bContext *C, + uiLayout *layout, + bNode &node, + PointerRNA *node_ptr, + const blender::nodes::PanelDeclaration &panel_decl) { - bNode &node = *static_cast(node_ptr->data); - bNodeSocket *socket = node.runtime->inputs_by_identifier.lookup(identifier); - draw_node_input(C, layout, node_ptr, *socket); -} - -/* Consume the item range, draw buttons if layout is not null. */ -static void handle_node_declaration_items(bContext *C, - Panel *root_panel, - uiLayout *layout, - PointerRNA *node_ptr, - ItemIterator &item_iter, - const ItemIterator item_end) -{ - while (item_iter != item_end) { - const ItemDeclaration *item_decl = item_iter->get(); - ++item_iter; - - if (const SocketDeclaration *socket_decl = dynamic_cast(item_decl)) - { - if (layout && socket_decl->in_out == SOCK_IN) { - draw_node_input(C, layout, node_ptr, socket_decl->identifier); + /* Use a root panel property to toggle open/closed state. */ + /* TODO: Use flag on the panel state instead which is better for dynamic panel amounts. */ + const std::string panel_idname = "NodePanel" + std::to_string(panel_decl.identifier); + Panel *root_panel = uiLayoutGetRootPanel(layout); + LayoutPanelState *state = BKE_panel_layout_panel_state_ensure( + root_panel, panel_idname.c_str(), panel_decl.default_collapsed); + PointerRNA state_ptr = RNA_pointer_create(nullptr, &RNA_LayoutPanelState, state); + uiLayout *panel_layout = uiLayoutPanelProp( + C, layout, &state_ptr, "is_open", IFACE_(panel_decl.name.c_str())); + if (!(state->flag & LAYOUT_PANEL_STATE_FLAG_OPEN)) { + return; + } + for (const ItemDeclaration *item_decl : panel_decl.items) { + if (const auto *socket_decl = dynamic_cast(item_decl)) { + if (socket_decl->in_out == SOCK_IN) { + draw_node_input(C, panel_layout, node_ptr, node.socket_by_decl(*socket_decl)); } } - else if (const PanelDeclaration *panel_decl = dynamic_cast( - item_decl)) - { - const ItemIterator panel_item_end = item_iter + panel_decl->items.size(); - BLI_assert(panel_item_end <= item_end); - - /* Use a root panel property to toggle open/closed state. */ - const std::string panel_idname = "NodePanel" + std::to_string(panel_decl->identifier); - LayoutPanelState *state = BKE_panel_layout_panel_state_ensure( - root_panel, panel_idname.c_str(), panel_decl->default_collapsed); - PointerRNA state_ptr = RNA_pointer_create(nullptr, &RNA_LayoutPanelState, state); - uiLayout *panel_layout = uiLayoutPanelProp( - C, layout, &state_ptr, "is_open", IFACE_(panel_decl->name.c_str())); - /* Draw panel buttons at the top of each panel section. */ - if (panel_layout && panel_decl->draw_buttons) { - panel_decl->draw_buttons(panel_layout, C, node_ptr); + else if (const auto *sub_panel_decl = dynamic_cast(item_decl)) { + draw_node_inputs_recursive(C, panel_layout, node, node_ptr, *sub_panel_decl); + } + else if (const auto *layout_decl = dynamic_cast(item_decl)) { + if (!layout_decl->is_default) { + layout_decl->draw(panel_layout, C, node_ptr); } - - handle_node_declaration_items( - C, root_panel, panel_layout, node_ptr, item_iter, panel_item_end); } } } @@ -122,6 +106,7 @@ static void handle_node_declaration_items(bContext *C, void uiTemplateNodeInputs(uiLayout *layout, bContext *C, PointerRNA *ptr) { + using namespace blender::nodes; bNodeTree &tree = *reinterpret_cast(ptr->owner_id); bNode &node = *static_cast(ptr->data); @@ -138,11 +123,22 @@ void uiTemplateNodeInputs(uiLayout *layout, bContext *C, PointerRNA *ptr) if (node.declaration()) { /* Draw socket inputs and panel buttons in the order of declaration panels. */ - ItemIterator item_iter = node.declaration()->all_items.begin(); - const ItemIterator item_end = node.declaration()->all_items.end(); - Panel *root_panel = uiLayoutGetRootPanel(layout); - blender::ui::nodes::handle_node_declaration_items( - C, root_panel, layout, ptr, item_iter, item_end); + const NodeDeclaration &node_decl = *node.declaration(); + for (const ItemDeclaration *item_decl : node_decl.root_items) { + if (const auto *panel_decl = dynamic_cast(item_decl)) { + blender::ui::nodes::draw_node_inputs_recursive(C, layout, node, ptr, *panel_decl); + } + else if (const auto *socket_decl = dynamic_cast(item_decl)) { + if (socket_decl->in_out == SOCK_IN) { + blender::ui::nodes::draw_node_input(C, layout, ptr, node.socket_by_decl(*socket_decl)); + } + } + else if (const auto *layout_decl = dynamic_cast(item_decl)) { + if (!layout_decl->is_default) { + layout_decl->draw(layout, C, ptr); + } + } + } } else { /* Draw socket values using the flat inputs list. */ diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 29bf2be0c4c..48a84eee516 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -25,6 +25,7 @@ #include "BLI_array.hh" #include "BLI_bounds.hh" #include "BLI_convexhull_2d.h" +#include "BLI_function_ref.hh" #include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_span.hh" @@ -383,7 +384,7 @@ static bool is_node_panels_supported(const bNode &node) static bool node_update_basis_buttons(const bContext &C, bNodeTree &ntree, bNode &node, - nodes::PanelDrawButtonsFunction draw_buttons, + blender::FunctionRef draw_buttons, uiBlock &block, int &dy) { @@ -550,378 +551,479 @@ static bool node_update_basis_socket(const bContext &C, return true; } -struct NodeInterfaceItemData { - private: - NodeInterfaceItemData() = default; +namespace flat_item { - public: - /* Declaration of a socket (only for socket items). */ - const nodes::SocketDeclaration *socket_decl = nullptr; +enum class Type { + Socket, + Separator, + Layout, + PanelHeader, + PanelContentBegin, + PanelContentEnd, +}; + +struct Socket { + static constexpr Type type = Type::Socket; bNodeSocket *input = nullptr; bNodeSocket *output = nullptr; - - /* Declaration of a panel (only for panel items). */ const nodes::PanelDeclaration *panel_decl = nullptr; - /* State of the panel instance on the node. - * Mutable so that panel visibility can be updated. */ - bNodePanelState *state = nullptr; - /* Runtime panel state for draw locations. */ - bke::bNodePanelRuntime *runtime = nullptr; +}; +struct Separator { + static constexpr Type type = Type::Separator; +}; +struct PanelHeader { + static constexpr Type type = Type::PanelHeader; + const nodes::PanelDeclaration *decl; +}; +struct PanelContentBegin { + static constexpr Type type = Type::PanelContentBegin; + const nodes::PanelDeclaration *decl; +}; +struct PanelContentEnd { + static constexpr Type type = Type::PanelContentEnd; + const nodes::PanelDeclaration *decl; +}; +struct Layout { + static constexpr Type type = Type::Layout; + const nodes::LayoutDeclaration *decl; +}; - bool is_separator = false; +} // namespace flat_item - NodeInterfaceItemData(const nodes::SocketDeclaration *_socket_decl, - bNodeSocket *_input, - bNodeSocket *_output) - : socket_decl(_socket_decl), input(_input), output(_output) - { - } - NodeInterfaceItemData(const nodes::PanelDeclaration *_panel_decl, - bNodePanelState *_state, - bke::bNodePanelRuntime *_runtime) - : panel_decl(_panel_decl), state(_state), runtime(_runtime) - { - } +struct FlatNodeItem { + std::variant + item; - static NodeInterfaceItemData separator() + flat_item::Type type() const { - NodeInterfaceItemData item; - item.is_separator = true; - return item; - } - - bool is_valid_socket() const - { - /* At least one socket pointer must be valid. */ - return this->socket_decl && (input || output); - } - - bool is_valid_panel() const - { - /* Panel can only be drawn when state data is available. */ - return this->panel_decl && this->state && this->runtime; - } - - bool is_valid_separator() const - { - return this->is_separator; + return std::visit([](auto &&item) { return item.type; }, this->item); } }; -/* Compile relevant socket and panel pointer data into a vector. - * This helps ensure correct pointer access in complex situations like inlined sockets. - */ -static Vector node_build_item_data(bNode &node) +static void determine_potentially_visible_panels_recursive( + const bNode &node, const nodes::PanelDeclaration &panel_decl, MutableSpan r_result) { - namespace nodes = blender::nodes; - using ItemDeclIterator = blender::Span::iterator; - using SocketIterator = blender::Span::iterator; - using PanelStateIterator = blender::MutableSpan::iterator; - using PanelRuntimeIterator = blender::MutableSpan::iterator; + bool potentially_visible = false; + for (const nodes::ItemDeclaration *item_decl : panel_decl.items) { + if (const auto *socket_decl = dynamic_cast(item_decl)) { + const bNodeSocket &socket = node.socket_by_decl(*socket_decl); + potentially_visible |= socket.is_visible(); + } + else if (const auto *sub_panel_decl = dynamic_cast(item_decl)) + { + determine_potentially_visible_panels_recursive(node, *sub_panel_decl, r_result); + potentially_visible |= r_result[sub_panel_decl->index]; + } + } + r_result[panel_decl.index] = potentially_visible; +} +/** + * A panel is potentially visible if it contains any socket that is available and not hidden. + */ +static void determine_potentially_visible_panels(const bNode &node, MutableSpan r_result) +{ + for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) { + if (const auto *panel_decl = dynamic_cast(item_decl)) { + determine_potentially_visible_panels_recursive(node, *panel_decl, r_result); + } + } +} + +static void determine_visible_panels_impl_recursive(const bNode &node, + const nodes::PanelDeclaration &panel_decl, + const Span potentially_visible_states, + MutableSpan r_result) +{ + if (!potentially_visible_states[panel_decl.index]) { + /* This panel does not contain any visible sockets.*/ + return; + } + r_result[panel_decl.index] = true; + const bNodePanelState &panel_state = node.panel_states_array[panel_decl.index]; + if (panel_state.is_collapsed()) { + /* The subpanels can't be visible if this panel is collapsed. */ + return; + } + for (const nodes::ItemDeclaration *item_decl : panel_decl.items) { + if (const auto *sub_panel_decl = dynamic_cast(item_decl)) { + determine_visible_panels_impl_recursive( + node, *sub_panel_decl, potentially_visible_states, r_result); + } + } +} + +static void determine_visible_panels_impl(const bNode &node, + const Span potentially_visible_states, + MutableSpan r_result) +{ + for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) { + if (const auto *panel_decl = dynamic_cast(item_decl)) { + determine_visible_panels_impl_recursive( + node, *panel_decl, potentially_visible_states, r_result); + } + } +} + +/** + * A panel is visible if all of the following are true: + * - All parent panels are visible and not collapsed. + * - The panel contains any visible sockets. + */ +static void determine_visible_panels(const bNode &node, MutableSpan r_visibility_states) +{ + Array potentially_visible_states(r_visibility_states.size(), false); + determine_potentially_visible_panels(node, potentially_visible_states); + determine_visible_panels_impl(node, potentially_visible_states, r_visibility_states); +} + +static void add_flat_items_for_socket(bNode &node, + const nodes::SocketDeclaration &socket_decl, + const nodes::PanelDeclaration *panel_decl, + Vector &r_items) +{ + bNodeSocket &socket = node.socket_by_decl(socket_decl); + if (!socket_decl.align_with_previous_socket) { + r_items.append({flat_item::Socket()}); + } + flat_item::Socket &item = std::get(r_items.last().item); + if (socket_decl.in_out == SOCK_IN) { + BLI_assert(!item.input); + item.input = &socket; + } + else { + BLI_assert(!item.output); + item.output = &socket; + } + item.panel_decl = panel_decl; +} + +static void add_flat_items_for_separator(Vector &r_items) +{ + r_items.append({flat_item::Separator()}); +} + +static void add_flat_items_for_layout(const bNode &node, + const nodes::LayoutDeclaration &layout_decl, + Vector &r_items) +{ + if (!(node.flag & NODE_OPTIONS)) { + return; + } + r_items.append({flat_item::Layout{&layout_decl}}); +} + +static void add_flat_items_for_panel(bNode &node, + const nodes::PanelDeclaration &panel_decl, + const Span panel_visibility, + Vector &r_items) +{ + if (!panel_visibility[panel_decl.index]) { + return; + } + r_items.append({flat_item::PanelHeader{&panel_decl}}); + const bNodePanelState &panel_state = node.panel_states_array[panel_decl.index]; + if (panel_state.is_collapsed()) { + return; + } + r_items.append({flat_item::PanelContentBegin{&panel_decl}}); + for (const nodes::ItemDeclaration *item_decl : panel_decl.items) { + if (const auto *socket_decl = dynamic_cast(item_decl)) { + add_flat_items_for_socket(node, *socket_decl, &panel_decl, r_items); + } + else if (const auto *sub_panel_decl = dynamic_cast(item_decl)) + { + add_flat_items_for_panel(node, *sub_panel_decl, panel_visibility, r_items); + } + else if (dynamic_cast(item_decl)) { + add_flat_items_for_separator(r_items); + } + else if (const auto *layout_decl = dynamic_cast(item_decl)) { + add_flat_items_for_layout(node, *layout_decl, r_items); + } + } + r_items.append({flat_item::PanelContentEnd{&panel_decl}}); +} + +/** + * Flattens the visible panels, sockets etc. of the node into a list that is then used to draw it. + */ +static Vector make_flat_node_items(bNode &node) +{ BLI_assert(is_node_panels_supported(node)); BLI_assert(node.runtime->panels.size() == node.num_panel_states); - ItemDeclIterator item_decl = node.declaration()->all_items.begin(); - SocketIterator input = node.input_sockets().begin(); - SocketIterator output = node.output_sockets().begin(); - PanelStateIterator panel_state = node.panel_states().begin(); - PanelRuntimeIterator panel_runtime = node.runtime->panels.begin(); - const ItemDeclIterator item_decl_end = node.declaration()->all_items.end(); - const SocketIterator input_end = node.input_sockets().end(); - const SocketIterator output_end = node.output_sockets().end(); - const PanelStateIterator panel_state_end = node.panel_states().end(); - const PanelRuntimeIterator panel_runtime_end = node.runtime->panels.end(); - UNUSED_VARS_NDEBUG(input_end, output_end, panel_state_end, panel_runtime_end); + const int panels_num = node.num_panel_states; + Array panel_visibility(panels_num, false); + determine_visible_panels(node, panel_visibility); - Vector result; - result.reserve(node.declaration()->all_items.size()); - - while (item_decl != item_decl_end) { - if (const nodes::SocketDeclaration *socket_decl = - dynamic_cast(item_decl->get())) - { - if (socket_decl->align_with_previous_socket) { - NodeInterfaceItemData &last_item = result.last(); - switch (socket_decl->in_out) { - case SOCK_IN: - BLI_assert(input != input_end); - BLI_assert(last_item.input == nullptr); - last_item.input = *input; - ++input; - break; - case SOCK_OUT: - BLI_assert(output != output_end); - BLI_assert(last_item.output == nullptr); - last_item.output = *output; - ++output; - break; - } - } - else { - switch (socket_decl->in_out) { - case SOCK_IN: - BLI_assert(input != input_end); - result.append({socket_decl, *input, nullptr}); - ++input; - break; - case SOCK_OUT: - BLI_assert(output != output_end); - result.append({socket_decl, nullptr, *output}); - ++output; - break; - } - } - ++item_decl; + Vector items; + for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) { + if (const auto *socket_decl = dynamic_cast(item_decl)) { + add_flat_items_for_socket(node, *socket_decl, nullptr, items); } - else if (const nodes::PanelDeclaration *panel_decl = - dynamic_cast(item_decl->get())) - { - BLI_assert(panel_state != panel_state_end); - BLI_assert(panel_runtime != panel_runtime_end); - result.append({panel_decl, panel_state, panel_runtime}); - ++item_decl; - ++panel_state; - ++panel_runtime; + else if (const auto *panel_decl = dynamic_cast(item_decl)) { + add_flat_items_for_panel(node, *panel_decl, panel_visibility, items); } - else if (dynamic_cast(item_decl->get())) { - result.append(NodeInterfaceItemData::separator()); - ++item_decl; + else if (dynamic_cast(item_decl)) { + add_flat_items_for_separator(items); + } + else if (const auto *layout_decl = dynamic_cast(item_decl)) { + add_flat_items_for_layout(node, *layout_decl, items); } } - return result; + return items; } -using ItemIterator = Vector::const_iterator; - -struct VisibilityUpdateState { - ItemIterator item_iter; - const ItemIterator item_end; - - explicit VisibilityUpdateState(const Span items) - : item_iter(items.begin()), item_end(items.end()) - { - } -}; - -/* Recursive function to determine visibility of items before drawing. */ -static void node_update_panel_items_visibility_recursive(int num_items, - const bool is_parent_collapsed, - bNodePanelState &parent_state, - VisibilityUpdateState &state) +/** Get the height of an empty node body. */ +static float get_margin_empty() { - parent_state.flag &= ~NODE_PANEL_CONTENT_VISIBLE; - while (state.item_iter != state.item_end) { - /* Stop after adding the expected number of items. - * Root panel consumes all remaining items (num_items == -1). */ - if (num_items == 0) { + return NODE_DYS; +} + +/** Get the margin between the node header and the first item. */ +static float get_margin_from_top(const Span items) +{ + const FlatNodeItem &first_item = items[0]; + const flat_item::Type first_item_type = first_item.type(); + switch (first_item_type) { + case flat_item::Type::Socket: + return 2 * NODE_ITEM_SPACING_Y; + case flat_item::Type::Separator: + return NODE_ITEM_SPACING_Y / 2; + case flat_item::Type::Layout: + return 3 * NODE_ITEM_SPACING_Y; + case flat_item::Type::PanelHeader: + return 4 * NODE_ITEM_SPACING_Y; + case flat_item::Type::PanelContentBegin: + case flat_item::Type::PanelContentEnd: + break; + } + BLI_assert_unreachable(); + return 0; +} + +/** Get the margin between the last item and the node bottom. */ +static float get_margin_to_bottom(const Span items) +{ + const FlatNodeItem &last_item = items.last(); + const flat_item::Type last_item_type = last_item.type(); + switch (last_item_type) { + case flat_item::Type::Socket: + return 5 * NODE_ITEM_SPACING_Y; + case flat_item::Type::Separator: + return NODE_ITEM_SPACING_Y; + case flat_item::Type::Layout: + return 5 * NODE_ITEM_SPACING_Y; + case flat_item::Type::PanelHeader: + return 4 * NODE_ITEM_SPACING_Y; + case flat_item::Type::PanelContentBegin: + break; + case flat_item::Type::PanelContentEnd: + return 3 * NODE_ITEM_SPACING_Y; + } + BLI_assert_unreachable(); + return 0; +} + +/** Get the margin between two consecutive items. */ +static float get_margin_between_elements(const Span items, const int next_index) +{ + BLI_assert(next_index >= 1); + const FlatNodeItem &prev = items[next_index - 1]; + const FlatNodeItem &next = items[next_index]; + using flat_item::Type; + const Type prev_type = prev.type(); + const Type next_type = next.type(); + + /* Handle all cases explicitly. This simplifies modifying the margins for specific cases + * without breaking other cases significantly. */ + switch (prev_type) { + case Type::Socket: { + switch (next_type) { + case Type::Socket: + return NODE_ITEM_SPACING_Y; + case Type::Separator: + return 0; + case Type::Layout: + return 2 * NODE_ITEM_SPACING_Y; + case Type::PanelHeader: + return 2 * NODE_ITEM_SPACING_Y; + case Type::PanelContentBegin: + break; + case Type::PanelContentEnd: + return 2 * NODE_ITEM_SPACING_Y; + } break; } - else if (num_items > 0) { - --num_items; + case Type::Layout: { + switch (next_type) { + case Type::Socket: + return 2 * NODE_ITEM_SPACING_Y; + case Type::Separator: + return 0; + case Type::Layout: + return NODE_ITEM_SPACING_Y; + case Type::PanelHeader: + return 3 * NODE_ITEM_SPACING_Y; + case Type::PanelContentBegin: + break; + case Type::PanelContentEnd: + return 2 * NODE_ITEM_SPACING_Y; + } + break; } - /* Consume item. */ - const NodeInterfaceItemData &item = *state.item_iter++; + case Type::Separator: { + switch (next_type) { + case Type::Socket: + return 2 * NODE_ITEM_SPACING_Y; + case Type::Separator: + return NODE_ITEM_SPACING_Y; + case Type::Layout: + return NODE_ITEM_SPACING_Y; + case Type::PanelHeader: + return NODE_ITEM_SPACING_Y; + case Type::PanelContentBegin: + break; + case Type::PanelContentEnd: + return NODE_ITEM_SPACING_Y; + } + break; + } + case Type::PanelHeader: { + switch (next_type) { + case Type::Socket: + return 4 * NODE_ITEM_SPACING_Y; + case Type::Separator: + return 3 * NODE_ITEM_SPACING_Y; + case Type::Layout: + return 3 * NODE_ITEM_SPACING_Y; + case Type::PanelHeader: + return 5 * NODE_ITEM_SPACING_Y; + case Type::PanelContentBegin: + return 3 * NODE_ITEM_SPACING_Y; + case Type::PanelContentEnd: + break; + } + break; + } + case Type::PanelContentBegin: { + switch (next_type) { + case Type::Socket: + return 2 * NODE_ITEM_SPACING_Y; + case Type::Separator: + return NODE_ITEM_SPACING_Y; + case Type::Layout: + return 2 * NODE_ITEM_SPACING_Y; + case Type::PanelHeader: + return 3 * NODE_ITEM_SPACING_Y; + case Type::PanelContentBegin: + break; + case Type::PanelContentEnd: + return NODE_ITEM_SPACING_Y; + } + break; + } + case Type::PanelContentEnd: { + switch (next_type) { + case Type::Socket: + return NODE_ITEM_SPACING_Y; + case Type::Separator: + return NODE_ITEM_SPACING_Y; + case Type::Layout: + return NODE_ITEM_SPACING_Y; + case Type::PanelHeader: + return 3 * NODE_ITEM_SPACING_Y; + case Type::PanelContentBegin: + break; + case Type::PanelContentEnd: + return NODE_ITEM_SPACING_Y; + } + break; + } + } + BLI_assert_unreachable(); + return 0.0f; +} - if (item.is_valid_panel()) { - SET_FLAG_FROM_TEST(item.state->flag, is_parent_collapsed, NODE_PANEL_PARENT_COLLAPSED); - /* New top panel is collapsed if self or parent is collapsed. */ - const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed(); - - node_update_panel_items_visibility_recursive( - item.panel_decl->items.size(), is_collapsed, *item.state, state); - if (item.panel_decl->draw_buttons) { - item.state->flag |= NODE_PANEL_CONTENT_VISIBLE; - } - if (item.state->flag & NODE_PANEL_CONTENT_VISIBLE) { - /* If child panel is visible so is the parent panel. */ - parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE; - } +/** Tags all the sockets in the panel as collapsed and updates their positions. */ +static void mark_sockets_collapsed_recursive(bNode &node, + const int node_left_x, + const nodes::PanelDeclaration &visible_panel_decl, + const nodes::PanelDeclaration &panel_decl) +{ + const bke::bNodePanelRuntime &visible_panel_runtime = + node.runtime->panels[visible_panel_decl.index]; + for (const nodes::ItemDeclaration *item_decl : panel_decl.items) { + if (const auto *socket_decl = dynamic_cast(item_decl)) { + bNodeSocket &socket = node.socket_by_decl(*socket_decl); + const int socket_x = socket.in_out == SOCK_IN ? node_left_x : node_left_x + NODE_WIDTH(node); + socket.runtime->location = math::round( + float2(socket_x, *visible_panel_runtime.header_center_y)); + socket.flag |= SOCK_PANEL_COLLAPSED; } - else if (item.is_valid_socket()) { - if (item.input) { - SET_FLAG_FROM_TEST(item.input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); - if (item.input->is_visible()) { - parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE; - } - } - if (item.output) { - SET_FLAG_FROM_TEST(item.output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); - if (item.output->is_visible()) { - parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE; - } - } - } - else if (item.is_valid_separator()) { - /* Nothing to do. */ - } - else { - /* Should not happen. */ - BLI_assert_unreachable(); + else if (const auto *sub_panel_decl = dynamic_cast(item_decl)) + { + mark_sockets_collapsed_recursive(node, node_left_x, visible_panel_decl, *sub_panel_decl); } } } -struct LocationUpdateState { - ItemIterator item_iter; - const ItemIterator item_end; - - /* Checked at various places to avoid adding duplicate spacers without anything in between. */ - bool need_spacer_after_item = false; - /* Makes sure buttons are only drawn once. */ - bool buttons_drawn = false; - /* Only true for the first item in the layout. */ - bool is_first = true; - - explicit LocationUpdateState(const Span items) - : item_iter(items.begin()), item_end(items.end()) - { - } -}; - -/* Recursive function that adds the expected number of items in a panel and advances the - * iterator. */ -static void add_panel_items_recursive(const bContext &C, - bNodeTree &ntree, - bNode &node, - uiBlock &block, - const int locx, - int &locy, - int num_items, - const bool is_parent_collapsed, - const char *parent_label, - bke::bNodePanelRuntime *parent_runtime, - LocationUpdateState &state) +static void update_collapsed_sockets_recursive(bNode &node, + const int node_left_x, + const nodes::PanelDeclaration &panel_decl) { - while (state.item_iter != state.item_end) { - /* Stop after adding the expected number of items. - * Root panel consumes all remaining items (num_items == -1). */ - if (num_items == 0) { - break; + const bNodePanelState &panel_state = node.panel_states_array[panel_decl.index]; + const bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[panel_decl.index]; + const bool is_open = panel_runtime.header_center_y.has_value() && !panel_state.is_collapsed(); + if (!is_open) { + mark_sockets_collapsed_recursive(node, node_left_x, panel_decl, panel_decl); + return; + } + for (const nodes::ItemDeclaration *item_decl : panel_decl.items) { + if (const auto *sub_panel_decl = dynamic_cast(item_decl)) { + update_collapsed_sockets_recursive(node, node_left_x, *sub_panel_decl); } - else if (num_items > 0) { - --num_items; + } +} + +/** + * Finds all collapsed sockets and updates them based on the visible parent panel that contains + * them. + */ +static void update_collapsed_sockets(bNode &node, const int node_left_x) +{ + for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) { + if (const auto *panel_decl = dynamic_cast(item_decl)) { + update_collapsed_sockets_recursive(node, node_left_x, *panel_decl); } - /* Consume item. */ - const NodeInterfaceItemData &item = *state.item_iter++; + } +} - if (item.is_valid_panel()) { - /* Draw buttons before the first panel. */ - if (!state.buttons_drawn) { - state.buttons_drawn = true; - state.need_spacer_after_item = node_update_basis_buttons( - C, ntree, node, node.typeinfo->draw_buttons, block, locy); - } - - /* Panel visible if any content is visible. */ - if (item.state->has_visible_content()) { - if (!is_parent_collapsed) { - locy -= NODE_DY; - state.is_first = false; - } - - /* New top panel is collapsed if self or parent is collapsed. */ - const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed(); - - /* Round the socket location to stop it from jiggling. */ - item.runtime->location_y = round(locy + NODE_DYS); - if (is_collapsed) { - item.runtime->max_content_y = item.runtime->min_content_y = round(locy); - } - else { - locy -= NODE_ITEM_SPACING_Y / 2; /* Space at bottom of panel header. */ - item.runtime->max_content_y = item.runtime->min_content_y = round(locy); - locy -= NODE_ITEM_SPACING_Y; /* Space at top of panel contents. */ - - node_update_basis_buttons(C, ntree, node, item.panel_decl->draw_buttons, block, locy); - } - - add_panel_items_recursive(C, - ntree, - node, - block, - locx, - locy, - item.panel_decl->items.size(), - is_collapsed, - item.panel_decl->name.c_str(), - item.runtime, - state); - } - } - else if (item.is_valid_socket()) { - bool need_socket_spacing = false; - if (item.input) { - /* Draw buttons before the first input. */ - if (!state.buttons_drawn) { - state.buttons_drawn = true; - state.need_spacer_after_item = node_update_basis_buttons( - C, ntree, node, node.typeinfo->draw_buttons, block, locy); - } - - if (is_parent_collapsed) { - item.input->runtime->location = float2(locx, round(locy + NODE_DYS)); - } - else { - /* Space between items. */ - if (!state.is_first && item.input->is_visible()) { - need_socket_spacing = true; - } - } - } - if (item.output) { - if (is_parent_collapsed) { - item.output->runtime->location = float2(round(locx + NODE_WIDTH(node)), - round(locy + NODE_DYS)); - } - else { - /* Space between items. */ - if (!state.is_first && item.output->is_visible()) { - need_socket_spacing = true; - } - } - } - if (need_socket_spacing) { - locy -= NODE_ITEM_SPACING_Y; - } - - if (!is_parent_collapsed && - node_update_basis_socket( - C, ntree, node, parent_label, item.input, item.output, block, locx, locy)) - { - state.is_first = false; - state.need_spacer_after_item = true; - } - } - else if (item.is_valid_separator()) { - if (!is_parent_collapsed) { - uiLayout *layout = UI_block_layout(&block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - locx + NODE_DYS, - locy, - NODE_WIDTH(node) - NODE_DY, - NODE_DY, - 0, - UI_style_get_dpi()); - uiItemS_ex(layout, 1.0, LayoutSeparatorType::Line); - UI_block_layout_resolve(&block, nullptr, nullptr); - locy -= NODE_ITEM_SPACING_Y; - } +/** + * Tag the innermost panel that goes to the very end of the node. The background color of that + * panel is extended to fill the entire rest of the node. + */ +static void tag_final_panel(bNode &node, const Span items) +{ + const flat_item::PanelContentEnd *final_panel = nullptr; + for (int item_i = items.size() - 1; item_i >= 0; item_i--) { + const FlatNodeItem &item = items[item_i]; + if (const auto *panel_item = std::get_if(&item.item)) { + final_panel = panel_item; } else { - /* Should not happen. */ - BLI_assert_unreachable(); + break; } } - - /* Finalize the vertical extent of the content. */ - if (!is_parent_collapsed) { - if (parent_runtime) { - locy -= 2 * NODE_ITEM_SPACING_Y; /* Space at bottom of panel contents. */ - parent_runtime->min_content_y = round(locy); - } - locy -= NODE_ITEM_SPACING_Y / 2; /* Space at top of next panel header. */ + if (final_panel) { + bke::bNodePanelRuntime &final_panel_runtime = node.runtime->panels[final_panel->decl->index]; + final_panel_runtime.content_extent->fill_node_end = true; } } @@ -929,45 +1031,118 @@ static void add_panel_items_recursive(const bContext &C, static void node_update_basis_from_declaration( const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy) { - namespace nodes = blender::nodes; - BLI_assert(is_node_panels_supported(node)); BLI_assert(node.runtime->panels.size() == node.num_panel_states); - const Vector item_data = node_build_item_data(node); - - /* Update item visibility flags first. */ - VisibilityUpdateState visibility_state(item_data); - /* Dummy state item to write into, unused. */ - bNodePanelState root_panel_state; - node_update_panel_items_visibility_recursive(-1, false, root_panel_state, visibility_state); - - /* Space at the top. */ - locy -= NODE_DYS / 2; - - /* Start by adding root panel items. */ - LocationUpdateState location_state(item_data); - - /* Draw buttons at the top when the node has a custom socket order. This could be customized in - * the future to support showing the buttons in any place. */ - if (node.declaration()->allow_any_socket_order) { - location_state.buttons_drawn = true; - location_state.need_spacer_after_item = node_update_basis_buttons( - C, ntree, node, node.typeinfo->draw_buttons, block, locy); + /* Reset states. */ + for (bke::bNodePanelRuntime &panel_runtime : node.runtime->panels) { + panel_runtime.header_center_y.reset(); + panel_runtime.content_extent.reset(); + } + for (bNodeSocket *socket : node.input_sockets()) { + socket->flag &= ~SOCK_PANEL_COLLAPSED; + } + for (bNodeSocket *socket : node.output_sockets()) { + socket->flag &= ~SOCK_PANEL_COLLAPSED; } - add_panel_items_recursive( - C, ntree, node, block, locx, locy, -1, false, "", nullptr, location_state); - - /* Draw buttons at the bottom if no inputs exist. */ - if (!location_state.buttons_drawn) { - location_state.need_spacer_after_item = node_update_basis_buttons( - C, ntree, node, node.typeinfo->draw_buttons, block, locy); + /* Gather flattened list of items in the node.*/ + const Vector flat_items = make_flat_node_items(node); + if (flat_items.is_empty()) { + const float margin = get_margin_empty(); + locy -= margin; + return; } - if (location_state.need_spacer_after_item) { - locy -= NODE_DYS / 2; + for (const int item_i : flat_items.index_range()) { + /* Apply margins. This should be the only place that applies margins between elements so that + * it is easy change later on.*/ + if (item_i == 0) { + const float margin = get_margin_from_top(flat_items); + locy -= margin; + } + else { + const float margin = get_margin_between_elements(flat_items, item_i); + locy -= margin; + } + + const FlatNodeItem &item_variant = flat_items[item_i]; + std::visit( + [&](const auto &item) { + using ItemT = std::decay_t; + if constexpr (std::is_same_v) { + bNodeSocket *input_socket = item.input; + bNodeSocket *output_socket = item.output; + const nodes::PanelDeclaration *panel_decl = item.panel_decl; + const char *parent_label = panel_decl ? panel_decl->name.c_str() : ""; + node_update_basis_socket( + C, ntree, node, parent_label, input_socket, output_socket, block, locx, locy); + } + else if constexpr (std::is_same_v) { + const nodes::LayoutDeclaration &decl = *item.decl; + /* Round the node origin because text contents are always pixel-aligned. */ + const float2 loc = math::round(node_to_view(node, float2(0))); + uiLayout *layout = UI_block_layout(&block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + loc.x + NODE_DYS, + locy, + NODE_WIDTH(node) - NODE_DY, + 0, + 0, + UI_style_get_dpi()); + if (node.flag & NODE_MUTED) { + uiLayoutSetActive(layout, false); + } + PointerRNA node_ptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node); + uiLayoutSetContextPointer(layout, "node", &node_ptr); + decl.draw(layout, const_cast(&C), &node_ptr); + UI_block_align_end(&block); + int buty; + UI_block_layout_resolve(&block, nullptr, &buty); + locy = buty; + } + else if constexpr (std::is_same_v) { + uiLayout *layout = UI_block_layout(&block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + locx + NODE_DYS, + locy, + NODE_WIDTH(node) - NODE_DY, + NODE_DY, + 0, + UI_style_get_dpi()); + uiItemS_ex(layout, 1.0, LayoutSeparatorType::Line); + UI_block_layout_resolve(&block, nullptr, nullptr); + } + else if constexpr (std::is_same_v) { + const nodes::PanelDeclaration &node_decl = *item.decl; + bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[node_decl.index]; + const float panel_header_height = NODE_DYS; + locy -= panel_header_height / 2; + panel_runtime.header_center_y = locy; + locy -= panel_header_height / 2; + } + else if constexpr (std::is_same_v) { + const nodes::PanelDeclaration &node_decl = *item.decl; + bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[node_decl.index]; + panel_runtime.content_extent.emplace(); + panel_runtime.content_extent->max_y = locy; + } + else if constexpr (std::is_same_v) { + const nodes::PanelDeclaration &node_decl = *item.decl; + bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[node_decl.index]; + panel_runtime.content_extent->min_y = locy; + } + }, + item_variant.item); } + + const float bottom_margin = get_margin_to_bottom(flat_items); + locy -= bottom_margin; + + update_collapsed_sockets(node, locx); + tag_final_panel(node, flat_items); } /* Conventional drawing in outputs/buttons/inputs order. */ @@ -2460,103 +2635,64 @@ static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, voi } /* Draw panel backgrounds first, so other node elements can be rendered on top. */ -static void node_draw_panels_background(const bNode &node, uiBlock &block) +static void node_draw_panels_background(const bNode &node) { - namespace nodes = blender::nodes; - BLI_assert(is_node_panels_supported(node)); - BLI_assert(node.runtime->panels.size() == node.panel_states().size()); - const nodes::NodeDeclaration &decl = *node.declaration(); - const rctf &rct = node.runtime->totr; - float color_panel[4]; - UI_GetThemeColorShade4fv(TH_NODE, -15, color_panel); + float panel_color[4]; + UI_GetThemeColorShade4fv(TH_NODE, -15, panel_color); + const rctf &totr = node.runtime->totr; - /* True if the last panel is open, draw bottom gap as background. */ - bool is_last_panel_visible = false; - float last_panel_content_y = 0.0f; + const nodes::PanelDeclaration *final_panel_decl = nullptr; - int panel_i = 0; - for (const nodes::ItemDeclarationPtr &item_decl : decl.all_items) { - const nodes::PanelDeclaration *panel_decl = dynamic_cast( - item_decl.get()); - if (panel_decl == nullptr) { - /* Not a panel. */ + const nodes::NodeDeclaration &node_decl = *node.declaration(); + for (const int panel_i : node_decl.panels.index_range()) { + const nodes::PanelDeclaration &panel_decl = *node_decl.panels[panel_i]; + const bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[panel_i]; + if (!panel_runtime.content_extent.has_value()) { continue; } - - const bNodePanelState &state = node.panel_states()[panel_i]; - const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i]; - - /* Don't draw hidden or collapsed panels. */ - const bool is_background_visible = state.has_visible_content() && - !(state.is_collapsed() || state.is_parent_collapsed()); - is_last_panel_visible = is_background_visible; - last_panel_content_y = runtime.max_content_y; - if (!is_background_visible) { - ++panel_i; - continue; - } - - UI_block_emboss_set(&block, UI_EMBOSS_NONE); - - /* Panel background. */ - const rctf content_rect = {rct.xmin, rct.xmax, runtime.min_content_y, runtime.max_content_y}; + const rctf content_rect = {totr.xmin, + totr.xmax, + panel_runtime.content_extent->min_y, + panel_runtime.content_extent->max_y}; UI_draw_roundbox_corner_set(UI_CNR_NONE); - UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, color_panel); - - UI_block_emboss_set(&block, UI_EMBOSS); - - ++panel_i; + UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, panel_color); + if (panel_runtime.content_extent->fill_node_end) { + final_panel_decl = &panel_decl; + } } - - /* If last item is an open panel, extend the panel background to cover the bottom border. */ - if (is_last_panel_visible) { - UI_block_emboss_set(&block, UI_EMBOSS_NONE); - - const rctf content_rect = {rct.xmin, rct.xmax, rct.ymin, last_panel_content_y}; + if (final_panel_decl) { + const bke::bNodePanelRuntime &final_panel_runtime = + node.runtime->panels[final_panel_decl->index]; + const rctf content_rect = { + totr.xmin, totr.xmax, totr.ymin, final_panel_runtime.content_extent->min_y}; UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT); - UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, color_panel); - - UI_block_emboss_set(&block, UI_EMBOSS); + const int repeats = final_panel_decl->depth() + 1; + for ([[maybe_unused]] const int i : IndexRange(repeats)) { + UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, panel_color); + } } } static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block) { - namespace nodes = blender::nodes; - BLI_assert(is_node_panels_supported(node)); - BLI_assert(node.runtime->panels.size() == node.panel_states().size()); + const rctf &totr = node.runtime->totr; - const nodes::NodeDeclaration &decl = *node.declaration(); - const rctf &rct = node.runtime->totr; - - int panel_i = 0; - for (const nodes::ItemDeclarationPtr &item_decl : decl.all_items) { - const nodes::PanelDeclaration *panel_decl = dynamic_cast( - item_decl.get()); - if (panel_decl == nullptr) { - /* Not a panel. */ + const nodes::NodeDeclaration &node_decl = *node.declaration(); + for (const int panel_i : node_decl.panels.index_range()) { + const nodes::PanelDeclaration &panel_decl = *node_decl.panels[panel_i]; + const bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[panel_i]; + const bNodePanelState &panel_state = node.panel_states_array[panel_i]; + if (!panel_runtime.header_center_y.has_value()) { continue; } - const bNodePanelState &state = node.panel_states()[panel_i]; - /* Don't draw hidden panels. */ - const bool is_header_visible = state.has_visible_content() && !state.is_parent_collapsed(); - if (!is_header_visible) { - ++panel_i; - continue; - } - const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i]; - - const rctf rect = { - rct.xmin, - rct.xmax, - runtime.location_y - NODE_DYS, - runtime.location_y + NODE_DYS, - }; - + const rctf header_rect = {totr.xmin, + totr.xmax, + *panel_runtime.header_center_y - NODE_DYS, + *panel_runtime.header_center_y + NODE_DYS}; UI_block_emboss_set(&block, UI_EMBOSS_NONE); /* Collapse/expand icon. */ @@ -2564,9 +2700,9 @@ static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block uiDefIconBut(&block, UI_BTYPE_BUT_TOGGLE, 0, - state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT, - rct.xmin + (NODE_MARGIN_X / 3), - runtime.location_y - but_size / 2, + panel_state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT, + totr.xmin + (NODE_MARGIN_X / 3), + *panel_runtime.header_center_y - but_size / 2, but_size, but_size, nullptr, @@ -2575,43 +2711,45 @@ static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block ""); /* Panel label. */ - uiBut *but = uiDefBut(&block, - UI_BTYPE_LABEL, - 0, - IFACE_(panel_decl->name.c_str()), - int(rct.xmin + NODE_MARGIN_X + 0.4f), - int(runtime.location_y - NODE_DYS), - short(rct.xmax - rct.xmin - (30.0f * UI_SCALE_FAC)), - short(NODE_DY), - nullptr, - 0, - 0, - ""); + uiBut *label_but = uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + IFACE_(panel_decl.name.c_str()), + int(totr.xmin + NODE_MARGIN_X + 0.4f), + int(*panel_runtime.header_center_y - NODE_DYS), + short(totr.xmax - totr.xmin - (30.0f * UI_SCALE_FAC)), + short(NODE_DY), + nullptr, + 0, + 0, + ""); if (node.flag & NODE_MUTED) { - UI_but_flag_enable(but, UI_BUT_INACTIVE); + UI_but_flag_enable(label_but, UI_BUT_INACTIVE); } /* Invisible button covering the entire header for collapsing/expanding. */ const int header_but_margin = NODE_MARGIN_X / 3; - but = uiDefIconBut(&block, - UI_BTYPE_BUT_TOGGLE, - 0, - ICON_NONE, - rect.xmin + header_but_margin, - rect.ymin, - std::max(int(rect.xmax - rect.xmin - 2 * header_but_margin), 0), - rect.ymax - rect.ymin, - nullptr, - 0.0f, - 0.0f, - panel_decl->description.c_str()); - UI_but_func_pushed_state_set(but, [&state](const uiBut &) { return state.is_collapsed(); }); - UI_but_func_set( - but, node_panel_toggle_button_cb, const_cast(&state), &ntree); + uiBut *toggle_action_but = uiDefIconBut( + &block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_NONE, + header_rect.xmin + header_but_margin, + header_rect.ymin, + std::max(int(header_rect.xmax - header_rect.xmin - 2 * header_but_margin), 0), + header_rect.ymax - header_rect.ymin, + nullptr, + 0.0f, + 0.0f, + panel_decl.description.c_str()); + UI_but_func_pushed_state_set( + toggle_action_but, [&panel_state](const uiBut &) { return panel_state.is_collapsed(); }); + UI_but_func_set(toggle_action_but, + node_panel_toggle_button_cb, + const_cast(&panel_state), + &ntree); UI_block_emboss_set(&block, UI_EMBOSS); - - ++panel_i; } } @@ -3622,7 +3760,7 @@ static void node_draw_basis(const bContext &C, UI_draw_roundbox_4fv(&rect, true, corner_radius, color); if (is_node_panels_supported(node)) { - node_draw_panels_background(node, block); + node_draw_panels_background(node); } } diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index bb98a9d2434..e5b6dd9e97d 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -756,11 +756,9 @@ static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, voi } static void ui_node_draw_panel(uiLayout &layout, - bContext &C, bNodeTree &ntree, const nodes::PanelDeclaration &panel_decl, - bNodePanelState &panel_state, - PointerRNA nodeptr) + bNodePanelState &panel_state) { uiLayout *row = uiLayoutRow(&layout, true); uiLayoutSetPropDecorate(row, false); @@ -784,11 +782,40 @@ static void ui_node_draw_panel(uiLayout &layout, UI_but_drawflag_enable(but, UI_BUT_TEXT_LEFT | UI_BUT_NO_TOOLTIP); UI_but_func_set(but, node_panel_toggle_button_cb, &panel_state, &ntree); UI_block_emboss_set(block, UI_EMBOSS); +} - /* Panel buttons. */ - if (!panel_state.is_collapsed() && panel_decl.draw_buttons) { - uiLayoutSetPropSep(&layout, true); - panel_decl.draw_buttons(&layout, &C, &nodeptr); +static void ui_node_draw_recursive(uiLayout &layout, + bContext &C, + bNodeTree &ntree, + bNode &node, + const nodes::PanelDeclaration &panel_decl, + const int depth) +{ + bNodePanelState &panel_state = node.panel_states_array[panel_decl.index]; + ui_node_draw_panel(layout, ntree, panel_decl, panel_state); + if (panel_state.is_collapsed()) { + return; + } + for (const nodes::ItemDeclaration *item_decl : panel_decl.items) { + if (const auto *socket_decl = dynamic_cast(item_decl)) { + if (socket_decl->in_out == SOCK_IN) { + ui_node_draw_input(layout, + C, + ntree, + node, + node.socket_by_decl(*socket_decl), + depth, + panel_decl.name.c_str()); + } + } + else if (const auto *sub_panel_decl = dynamic_cast(item_decl)) + { + ui_node_draw_recursive(layout, C, ntree, node, *sub_panel_decl, depth + 1); + } + else if (const auto *layout_decl = dynamic_cast(item_decl)) { + PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node); + layout_decl->draw(&layout, &C, &nodeptr); + } } } @@ -797,51 +824,29 @@ static void ui_node_draw_node( { PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node); - if (node.typeinfo->draw_buttons) { - if (node.type != NODE_GROUP) { - uiLayoutSetPropSep(&layout, true); - node.typeinfo->draw_buttons(&layout, &C, &nodeptr); - } - } - if (node.declaration() && node.declaration()->use_custom_socket_order) { - /* Node with panels. */ - namespace nodes = blender::nodes; - using ItemDeclIterator = blender::Span::iterator; - using SocketIterator = blender::Span::iterator; - using PanelStateIterator = blender::MutableSpan::iterator; - - ItemDeclIterator item_decl = node.declaration()->all_items.begin(); - SocketIterator input = node.input_sockets().begin(); - PanelStateIterator panel_state = node.panel_states().begin(); - const ItemDeclIterator item_decl_end = node.declaration()->all_items.end(); - - bool panel_collapsed = false; - const char *panel_label = nullptr; - - for (; item_decl != item_decl_end; ++item_decl) { - if (const nodes::SocketDeclaration *socket_decl = - dynamic_cast(item_decl->get())) + const nodes::NodeDeclaration &node_decl = *node.declaration(); + for (const nodes::ItemDeclaration *item_decl : node_decl.root_items) { + if (const auto *panel_decl = dynamic_cast(item_decl)) { + ui_node_draw_recursive(layout, C, ntree, node, *panel_decl, depth + 1); + } + else if (const auto *socket_decl = dynamic_cast(item_decl)) { if (socket_decl->in_out == SOCK_IN) { - if (!panel_collapsed) { - ui_node_draw_input(layout, C, ntree, node, **input, depth + 1, panel_label); - } - ++input; + ui_node_draw_input( + layout, C, ntree, node, node.socket_by_decl(*socket_decl), depth, nullptr); } } - else if (const nodes::PanelDeclaration *panel_decl = - dynamic_cast(item_decl->get())) - { - panel_collapsed = panel_state->is_collapsed(); - panel_label = panel_decl->name.c_str(); - ui_node_draw_panel(layout, C, ntree, *panel_decl, *panel_state, nodeptr); - ++panel_state; - } } } else { - /* Node without panels. */ + if (node.typeinfo->draw_buttons) { + if (node.type != NODE_GROUP) { + uiLayoutSetPropSep(&layout, true); + node.typeinfo->draw_buttons(&layout, &C, &nodeptr); + } + } + LISTBASE_FOREACH (bNodeSocket *, input, &node.inputs) { ui_node_draw_input(layout, C, ntree, node, *input, depth + 1, nullptr); } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index f8d669a763c..e9762345c2b 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -489,6 +489,9 @@ typedef struct bNode { const bNodeSocket &output_by_identifier(blender::StringRef identifier) const; bNodeSocket &input_by_identifier(blender::StringRef identifier); bNodeSocket &output_by_identifier(blender::StringRef identifier); + /** Lookup socket by its declaration. */ + const bNodeSocket &socket_by_decl(const blender::nodes::SocketDeclaration &decl) const; + bNodeSocket &socket_by_decl(const blender::nodes::SocketDeclaration &decl); /** If node is frame, will return all children nodes. */ blender::Span direct_children_in_frame() const; blender::Span panel_states() const; diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index c71d4f813a8..302601c4cb8 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -190,6 +190,9 @@ class SocketDeclaration : public ItemDeclaration { /** Puts this socket on the same line as the previous one in the UI. */ bool align_with_previous_socket = false; + /** Index in the list of inputs or outputs of the node. */ + int index = -1; + InputSocketFieldType input_field_type = InputSocketFieldType::None; OutputFieldDependency output_field_dependency; @@ -257,8 +260,6 @@ class PanelDeclarationBuilder; class BaseSocketDeclarationBuilder { protected: - /* Index of the socket in the list of inputs or outputs. */ - int index_ = -1; bool reference_pass_all_ = false; bool field_on_all_ = false; bool propagate_from_all_ = false; @@ -414,10 +415,20 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { using SocketDeclarationPtr = std::unique_ptr; -using PanelDrawButtonsFunction = void (*)(uiLayout *, bContext *, PointerRNA *); +using DrawNodeLayoutFn = void(uiLayout *, bContext *, PointerRNA *); class SeparatorDeclaration : public ItemDeclaration {}; +class LayoutDeclaration : public ItemDeclaration { + public: + std::function draw; + /** + * Sometimes the default layout has special handling (e.g. choose between #draw_buttons and + * #draw_buttons_ex). + */ + bool is_default = false; +}; + /** * Describes a panel containing sockets or other panels. */ @@ -428,8 +439,10 @@ class PanelDeclaration : public ItemDeclaration { std::string description; std::string translation_context; bool default_collapsed = false; - PanelDrawButtonsFunction draw_buttons = nullptr; Vector items; + /** Index in the list of panels on the node. */ + int index = -1; + PanelDeclaration *parent_panel = nullptr; private: friend NodeDeclarationBuilder; @@ -441,6 +454,8 @@ class PanelDeclaration : public ItemDeclaration { void build(bNodePanelState &panel) const; bool matches(const bNodePanelState &panel) const; void update_or_build(const bNodePanelState &old_panel, bNodePanelState &new_panel) const; + + int depth() const; }; /** @@ -451,6 +466,7 @@ class DeclarationListBuilder { public: NodeDeclarationBuilder &node_decl_builder; Vector &items; + PanelDeclaration *parent_panel_decl = nullptr; DeclarationListBuilder(NodeDeclarationBuilder &node_decl_builder, Vector &items) @@ -484,6 +500,8 @@ class DeclarationListBuilder { PanelDeclarationBuilder &add_panel(StringRef name, int identifier = -1); void add_separator(); + void add_default_layout(); + void add_layout(std::function draw); }; class PanelDeclarationBuilder : public DeclarationListBuilder { @@ -497,11 +515,11 @@ class PanelDeclarationBuilder : public DeclarationListBuilder { PanelDeclarationBuilder(NodeDeclarationBuilder &node_builder, PanelDeclaration &decl) : DeclarationListBuilder(node_builder, decl.items), decl_(&decl) { + this->parent_panel_decl = &decl; } Self &description(std::string value = ""); Self &default_closed(bool closed); - Self &draw_buttons(PanelDrawButtonsFunction func); }; using PanelDeclarationPtr = std::unique_ptr; @@ -515,6 +533,7 @@ class NodeDeclaration { /* All input and output socket declarations. */ Vector inputs; Vector outputs; + Vector panels; std::unique_ptr anonymous_attribute_relations_; /** Leave the sockets in place, even if they don't match the declaration. Used for dynamic @@ -553,6 +572,7 @@ class NodeDeclaration { class NodeDeclarationBuilder : public DeclarationListBuilder { private: + const bke::bNodeType &typeinfo_; NodeDeclaration &declaration_; const bNodeTree *ntree_ = nullptr; const bNode *node_ = nullptr; @@ -566,7 +586,8 @@ class NodeDeclarationBuilder : public DeclarationListBuilder { friend DeclarationListBuilder; public: - NodeDeclarationBuilder(NodeDeclaration &declaration, + NodeDeclarationBuilder(const bke::bNodeType &typeinfo, + NodeDeclaration &declaration, const bNodeTree *ntree = nullptr, const bNode *node = nullptr); @@ -686,12 +707,12 @@ inline typename DeclType::Builder &DeclarationListBuilder::add_socket(StringRef if (in_out == SOCK_IN) { this->node_decl_builder.input_socket_builders_.append(&socket_decl_builder); - socket_decl_builder.index_ = this->node_decl_builder.declaration_.inputs.append_and_get_index( + socket_decl.index = this->node_decl_builder.declaration_.inputs.append_and_get_index( &socket_decl); } else { this->node_decl_builder.output_socket_builders_.append(&socket_decl_builder); - socket_decl_builder.index_ = this->node_decl_builder.declaration_.outputs.append_and_get_index( + socket_decl.index = this->node_decl_builder.declaration_.outputs.append_and_get_index( &socket_decl); } return socket_decl_builder; @@ -705,7 +726,7 @@ inline typename DeclType::Builder &DeclarationListBuilder::add_socket(StringRef inline int BaseSocketDeclarationBuilder::index() const { - return index_; + return decl_base_->index; } inline bool BaseSocketDeclarationBuilder::is_input() const diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index d50955f7078..2fbcd5f1ab2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -31,6 +31,8 @@ static void node_declare(NodeDeclarationBuilder &b) b.use_custom_socket_order(); b.allow_any_socket_order(); + b.add_default_layout(); + b.add_input("Geometry"); b.add_output("Geometry").propagate_all().align_with_previous(); if (node != nullptr) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_bake.cc b/source/blender/nodes/geometry/nodes/node_geo_bake.cc index 4360361098a..b8db69c8431 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bake.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bake.cc @@ -51,6 +51,8 @@ static void node_declare(NodeDeclarationBuilder &b) b.use_custom_socket_order(); b.allow_any_socket_order(); + b.add_default_layout(); + const bNodeTree *ntree = b.tree_or_null(); const bNode *node = b.node_or_null(); if (!node) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_geometry_element.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_geometry_element.cc index 4fe00de9815..646d308b6d9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_geometry_element.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_geometry_element.cc @@ -104,6 +104,8 @@ static void node_declare(NodeDeclarationBuilder &b) const bNode *node = b.node_or_null(); const bNodeTree *tree = b.tree_or_null(); + b.add_default_layout(); + if (!node || !tree) { return; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_warning.cc b/source/blender/nodes/geometry/nodes/node_geo_warning.cc index e6b18680ce2..cb53b17ab2d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_warning.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_warning.cc @@ -18,6 +18,8 @@ static void node_declare(NodeDeclarationBuilder &b) b.use_custom_socket_order(); b.allow_any_socket_order(); + b.add_default_layout(); + b.add_input("Show").default_value(true).hide_value(); b.add_output("Show").align_with_previous(); b.add_input("Message").hide_label(); diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index ade0bf2f73f..7fc8de443a7 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -402,27 +402,42 @@ static void set_default_input_field(const bNodeTreeInterfaceSocket &input, Socke static void node_group_declare_panel_recursive(DeclarationListBuilder &b, const bNodeTree &group, - const bNodeTreeInterfacePanel &io_parent_panel) + const bNodeTreeInterfacePanel &io_parent_panel, + const bool is_root) { + bool layout_added = false; + auto add_layout_if_needed = [&]() { + if (is_root && !layout_added) { + b.add_default_layout(); + layout_added = true; + } + }; + for (const bNodeTreeInterfaceItem *item : io_parent_panel.items()) { switch (item->item_type) { case NODE_INTERFACE_SOCKET: { const auto &io_socket = node_interface::get_item_as(*item); const eNodeSocketInOut in_out = (io_socket.flag & NODE_INTERFACE_SOCKET_INPUT) ? SOCK_IN : SOCK_OUT; + if (in_out == SOCK_IN) { + add_layout_if_needed(); + } build_interface_socket_declaration(group, io_socket, in_out, b); break; } case NODE_INTERFACE_PANEL: { + add_layout_if_needed(); const auto &io_panel = node_interface::get_item_as(*item); auto &panel_b = b.add_panel(StringRef(io_panel.name), io_panel.identifier) .description(StringRef(io_panel.description)) .default_closed(io_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED); - node_group_declare_panel_recursive(panel_b, group, io_panel); + node_group_declare_panel_recursive(panel_b, group, io_panel, false); break; } } } + + add_layout_if_needed(); } void node_group_declare(NodeDeclarationBuilder &b) @@ -445,7 +460,7 @@ void node_group_declare(NodeDeclarationBuilder &b) /* Allow the node group interface to define the socket order. */ r_declaration.use_custom_socket_order = true; - node_group_declare_panel_recursive(b, *group, group->tree_interface.root_panel); + node_group_declare_panel_recursive(b, *group, group->tree_interface.root_panel, true); if (group->type == NTREE_GEOMETRY) { group->ensure_interface_cache(); diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index 647a50c4901..3ca4826d258 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -30,7 +30,7 @@ void build_node_declaration(const bke::bNodeType &typeinfo, const bNode *node) { reset_declaration(r_declaration); - NodeDeclarationBuilder node_decl_builder{r_declaration, ntree, node}; + NodeDeclarationBuilder node_decl_builder{typeinfo, r_declaration, ntree, node}; typeinfo.declare(node_decl_builder); node_decl_builder.finalize(); } @@ -57,7 +57,7 @@ void NodeDeclarationBuilder::build_remaining_anonymous_attribute_relations() for (BaseSocketDeclarationBuilder *socket_builder : input_socket_builders_) { if (socket_builder->field_on_all_) { aal::RelationsInNode &relations = this->get_anonymous_attribute_relations(); - const int field_input = socket_builder->index_; + const int field_input = socket_builder->decl_base_->index; for (const int geometry_input : geometry_inputs) { relations.eval_relations.append({field_input, geometry_input}); } @@ -66,14 +66,14 @@ void NodeDeclarationBuilder::build_remaining_anonymous_attribute_relations() for (BaseSocketDeclarationBuilder *socket_builder : output_socket_builders_) { if (socket_builder->field_on_all_) { aal::RelationsInNode &relations = this->get_anonymous_attribute_relations(); - const int field_output = socket_builder->index_; + const int field_output = socket_builder->decl_base_->index; for (const int geometry_output : geometry_outputs) { relations.available_relations.append({field_output, geometry_output}); } } if (socket_builder->reference_pass_all_) { aal::RelationsInNode &relations = this->get_anonymous_attribute_relations(); - const int field_output = socket_builder->index_; + const int field_output = socket_builder->decl_base_->index; for (const int input_i : declaration_.inputs.index_range()) { SocketDeclaration &input_socket_decl = *declaration_.inputs[input_i]; if (input_socket_decl.input_field_type != InputSocketFieldType::None) { @@ -83,7 +83,7 @@ void NodeDeclarationBuilder::build_remaining_anonymous_attribute_relations() } if (socket_builder->propagate_from_all_) { aal::RelationsInNode &relations = this->get_anonymous_attribute_relations(); - const int geometry_output = socket_builder->index_; + const int geometry_output = socket_builder->decl_base_->index; for (const int geometry_input : geometry_inputs) { relations.propagate_relations.append({geometry_input, geometry_output}); } @@ -99,10 +99,12 @@ void NodeDeclarationBuilder::finalize() #endif } -NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration, +NodeDeclarationBuilder::NodeDeclarationBuilder(const bke::bNodeType &typeinfo, + NodeDeclaration &declaration, const bNodeTree *ntree, const bNode *node) : DeclarationListBuilder(*this, declaration.root_items), + typeinfo_(typeinfo), declaration_(declaration), ntree_(ntree), node_(node) @@ -236,8 +238,10 @@ bool NodeDeclaration::matches(const bNode &node) const } ++current_panel; } - else if (dynamic_cast(item_decl.get())) { - /* Separators are ignored here because they don't have corresponding data in DNA. */ + else if (dynamic_cast(item_decl.get()) || + dynamic_cast(item_decl.get())) + { + /* Ignored because they don't have corresponding data in DNA. */ } else { /* Unknown item type. */ @@ -416,6 +420,26 @@ void DeclarationListBuilder::add_separator() this->items.append(&decl); } +void DeclarationListBuilder::add_default_layout() +{ + BLI_assert(this->node_decl_builder.typeinfo_.draw_buttons); + this->add_layout([](uiLayout *layout, bContext *C, PointerRNA *ptr) { + const bNode &node = *static_cast(ptr->data); + node.typeinfo->draw_buttons(layout, C, ptr); + }); + static_cast(*this->items.last()).is_default = true; +} + +void DeclarationListBuilder::add_layout( + std::function draw) +{ + auto decl_ptr = std::make_unique(); + LayoutDeclaration &decl = *decl_ptr; + decl.draw = std::move(draw); + this->node_decl_builder.declaration_.all_items.append(std::move(decl_ptr)); + this->items.append(&decl); +} + PanelDeclarationBuilder &DeclarationListBuilder::add_panel(const StringRef name, int identifier) { auto panel_decl_ptr = std::make_unique(); @@ -432,8 +456,10 @@ PanelDeclarationBuilder &DeclarationListBuilder::add_panel(const StringRef name, panel_decl.identifier = this->node_decl_builder.declaration_.all_items.size(); } panel_decl.name = name; + panel_decl.parent_panel = this->parent_panel_decl; + panel_decl.index = this->node_decl_builder.declaration_.panels.append_and_get_index(&panel_decl); this->node_decl_builder.declaration_.all_items.append(std::move(panel_decl_ptr)); - this->node_decl_builder.panel_builders_.append(std::move(panel_decl_builder_ptr)); + this->node_decl_builder.panel_builders_.append_and_get_index(std::move(panel_decl_builder_ptr)); this->items.append(&panel_decl); return panel_decl_builder; } @@ -458,6 +484,16 @@ void PanelDeclaration::update_or_build(const bNodePanelState &old_panel, SET_FLAG_FROM_TEST(new_panel.flag, old_panel.is_collapsed(), NODE_PANEL_COLLAPSED); } +int PanelDeclaration::depth() const +{ + int count = 0; + for (const PanelDeclaration *parent = this->parent_panel; parent; parent = parent->parent_panel) + { + count++; + } + return count; +} + BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::supports_field() { BLI_assert(this->is_input()); @@ -508,7 +544,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::reference_pass( for (const int from_input : input_indices) { aal::ReferenceRelation relation; relation.from_field_input = from_input; - relation.to_field_output = index_; + relation.to_field_output = decl_base_->index; relations.reference_relations.append(relation); } return *this; @@ -521,7 +557,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on(const Span< this->supports_field(); for (const int input_index : indices) { aal::EvalRelation relation; - relation.field_input = index_; + relation.field_input = decl_base_->index; relation.geometry_input = input_index; relations.eval_relations.append(relation); } @@ -530,7 +566,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on(const Span< this->field_source(); for (const int output_index : indices) { aal::AvailableRelation relation; - relation.field_output = index_; + relation.field_output = decl_base_->index; relation.geometry_output = output_index; relations.available_relations.append(relation); } @@ -783,12 +819,6 @@ PanelDeclarationBuilder &PanelDeclarationBuilder::default_closed(bool closed) return *this; } -PanelDeclarationBuilder &PanelDeclarationBuilder::draw_buttons(PanelDrawButtonsFunction func) -{ - decl_->draw_buttons = func; - return *this; -} - namespace implicit_field_inputs { void position(const bNode & /*node*/, void *r_value) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index 85c410c7075..a92e25390bb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -81,12 +81,10 @@ static void node_declare(NodeDeclarationBuilder &b) #define SOCK_DIFFUSE_ROUGHNESS_ID 7 /* Panel for Subsurface scattering settings. */ - PanelDeclarationBuilder &sss = - b.add_panel("Subsurface") - .default_closed(true) - .draw_buttons([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { - uiItemR(layout, ptr, "subsurface_method", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); - }); + PanelDeclarationBuilder &sss = b.add_panel("Subsurface").default_closed(true); + sss.add_layout([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { + uiItemR(layout, ptr, "subsurface_method", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + }); sss.add_input("Subsurface Weight") .default_value(0.0f) .min(0.0f) @@ -134,12 +132,10 @@ static void node_declare(NodeDeclarationBuilder &b) #define SOCK_SUBSURFACE_ANISOTROPY_ID 12 /* Panel for Specular settings. */ - PanelDeclarationBuilder &spec = - b.add_panel("Specular") - .default_closed(true) - .draw_buttons([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { - uiItemR(layout, ptr, "distribution", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); - }); + PanelDeclarationBuilder &spec = b.add_panel("Specular").default_closed(true); + spec.add_layout([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { + uiItemR(layout, ptr, "distribution", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + }); spec.add_input("Specular IOR Level") .default_value(0.5f) .min(0.0f)