diff --git a/source/blender/editors/geometry/node_group_operator.cc b/source/blender/editors/geometry/node_group_operator.cc index c6c993f0620..2410ce2f854 100644 --- a/source/blender/editors/geometry/node_group_operator.cc +++ b/source/blender/editors/geometry/node_group_operator.cc @@ -68,6 +68,7 @@ #include "BLT_translation.hh" +#include "NOD_geometry_nodes_caller_ui.hh" #include "NOD_geometry_nodes_dependencies.hh" #include "NOD_geometry_nodes_execute.hh" #include "NOD_geometry_nodes_lazy_function.hh" @@ -811,130 +812,6 @@ static std::string run_node_group_get_description(bContext *C, return asset->get_metadata().description; } -struct DrawOperatorInputsContext { - const bNodeTree &ntree; - PointerRNA *bmain_ptr; - PointerRNA *op_ptr; - nodes::PropertiesVectorSet properties; - Array input_usages; -}; - -static void add_attribute_search_or_value_buttons(DrawOperatorInputsContext &ctx, - uiLayout *layout, - const StringRef socket_id_esc, - const StringRefNull rna_path, - const bNodeTreeInterfaceSocket &socket) -{ - bke::bNodeSocketType *typeinfo = bke::node_socket_type_find(socket.socket_type); - const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type); - - const std::string rna_path_use_attribute = fmt::format( - "[\"{}{}\"]", socket_id_esc, nodes::input_use_attribute_suffix); - const std::string rna_path_attribute_name = fmt::format( - "[\"{}{}\"]", socket_id_esc, nodes::input_attribute_name_suffix); - - /* We're handling this manually in this case. */ - uiLayoutSetPropDecorate(layout, false); - - uiLayout *split = &layout->split(0.4f, false); - uiLayout *name_row = &split->row(false); - uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); - - const bool use_attribute = RNA_boolean_get(ctx.op_ptr, rna_path_use_attribute.c_str()); - if (socket_type == SOCK_BOOLEAN && !use_attribute) { - name_row->label("", ICON_NONE); - } - else { - name_row->label(socket.name ? socket.name : "", ICON_NONE); - } - - uiLayout *prop_row = &split->row(true); - if (socket_type == SOCK_BOOLEAN) { - uiLayoutSetPropSep(prop_row, false); - uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND); - } - - if (use_attribute) { - /* TODO: Add attribute search. */ - prop_row->prop(ctx.op_ptr, rna_path_attribute_name, UI_ITEM_NONE, "", ICON_NONE); - } - else { - const char *name = socket_type == SOCK_BOOLEAN ? (socket.name ? socket.name : "") : ""; - prop_row->prop(ctx.op_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE); - } - - prop_row->prop(ctx.op_ptr, rna_path_use_attribute, UI_ITEM_R_ICON_ONLY, "", ICON_SPREADSHEET); -} - -static void draw_property_for_socket(DrawOperatorInputsContext &ctx, - uiLayout *layout, - const bNodeTreeInterfaceSocket &socket) -{ - bke::bNodeSocketType *typeinfo = bke::node_socket_type_find(socket.socket_type); - const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type); - - /* The property should be created in #MOD_nodes_update_interface with the correct type. */ - const IDProperty *property = ctx.properties.lookup_key_default_as(socket.identifier, nullptr); - - /* IDProperties can be removed with python, so there could be a situation where - * there isn't a property for a socket or it doesn't have the correct type. */ - if (!property || !nodes::id_property_type_matches_socket(socket, *property, true)) { - return; - } - - const int socket_index = ctx.ntree.interface_input_index(socket); - const bool affects_output = ctx.input_usages[socket_index].is_used; - - const std::string socket_id_esc = BLI_str_escape(socket.identifier); - const std::string rna_path = fmt::format("[\"{}\"]", socket_id_esc); - - uiLayout *row = &layout->row(true); - uiLayoutSetActive(row, affects_output); - uiLayoutSetPropDecorate(row, false); - - /* Use #uiItemPointerR to draw pointer properties because #uiLayout::prop would not have enough - * information about what type of ID to select for editing the values. This is because - * pointer IDProperties contain no information about their type. */ - const char *name = socket.name ? socket.name : ""; - switch (socket_type) { - case SOCK_OBJECT: - uiItemPointerR(row, ctx.op_ptr, rna_path, ctx.bmain_ptr, "objects", name, ICON_OBJECT_DATA); - break; - case SOCK_COLLECTION: - uiItemPointerR( - row, ctx.op_ptr, rna_path, ctx.bmain_ptr, "collections", name, ICON_OUTLINER_COLLECTION); - break; - case SOCK_MATERIAL: - uiItemPointerR(row, ctx.op_ptr, rna_path, ctx.bmain_ptr, "materials", name, ICON_MATERIAL); - break; - case SOCK_TEXTURE: - uiItemPointerR(row, ctx.op_ptr, rna_path, ctx.bmain_ptr, "textures", name, ICON_TEXTURE); - break; - case SOCK_IMAGE: - uiItemPointerR(row, ctx.op_ptr, rna_path, ctx.bmain_ptr, "images", name, ICON_IMAGE); - break; - case SOCK_MENU: { - if (socket.flag & NODE_INTERFACE_SOCKET_MENU_EXPANDED) { - row->prop(ctx.op_ptr, rna_path, UI_ITEM_R_EXPAND, name, ICON_NONE); - } - else { - row->prop(ctx.op_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE); - } - break; - } - default: - if (nodes::input_has_attribute_toggle(ctx.ntree, socket_index)) { - add_attribute_search_or_value_buttons(ctx, row, socket_id_esc, rna_path, socket); - } - else { - row->prop(ctx.op_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE); - } - } - if (!nodes::input_has_attribute_toggle(ctx.ntree, socket_index)) { - row->label("", ICON_BLANK1); - } -} - static void run_node_group_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; @@ -948,17 +825,11 @@ static void run_node_group_ui(bContext *C, wmOperator *op) return; } - node_tree->ensure_interface_cache(); - - DrawOperatorInputsContext ctx{*node_tree, &bmain_ptr, op->ptr}; - ctx.properties = nodes::build_properties_vector_set(op->properties); - ctx.input_usages.reinitialize(node_tree->interface_inputs().size()); - nodes::socket_usage_inference::infer_group_interface_inputs_usage( - *node_tree, ctx.properties, ctx.input_usages); - - for (const bNodeTreeInterfaceSocket *io_socket : node_tree->interface_inputs()) { - draw_property_for_socket(ctx, layout, *io_socket); - } + bke::OperatorComputeContext compute_context; + GeoOperatorLog &eval_log = get_static_eval_log(); + geo_log::GeoTreeLog &tree_log = eval_log.log->get_tree_log(compute_context.hash()); + nodes::draw_geometry_nodes_operator_redo_ui( + *C, *op, const_cast(*node_tree), &tree_log); } static bool run_node_ui_poll(wmOperatorType * /*ot*/, PointerRNA *ptr) diff --git a/source/blender/nodes/NOD_geometry_nodes_caller_ui.hh b/source/blender/nodes/NOD_geometry_nodes_caller_ui.hh index d098c607f63..c7265dd7b1d 100644 --- a/source/blender/nodes/NOD_geometry_nodes_caller_ui.hh +++ b/source/blender/nodes/NOD_geometry_nodes_caller_ui.hh @@ -7,11 +7,22 @@ struct bContext; struct PointerRNA; struct uiLayout; +struct wmOperator; +struct bNodeTree; namespace blender::nodes { +namespace geo_eval_log { +class GeoTreeLog; +} + void draw_geometry_nodes_modifier_ui(const bContext &C, PointerRNA *modifier_ptr, uiLayout &layout); -} +void draw_geometry_nodes_operator_redo_ui(const bContext &C, + wmOperator &op, + bNodeTree &tree, + geo_eval_log::GeoTreeLog *tree_log); + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/geometry_nodes_caller_ui.cc b/source/blender/nodes/intern/geometry_nodes_caller_ui.cc index 7cfca9ccaa1..c1df794dca9 100644 --- a/source/blender/nodes/intern/geometry_nodes_caller_ui.cc +++ b/source/blender/nodes/intern/geometry_nodes_caller_ui.cc @@ -13,6 +13,7 @@ #include "BKE_modifier.hh" #include "BKE_node.hh" #include "BKE_node_runtime.hh" +#include "BKE_screen.hh" #include "BLI_string.h" @@ -55,9 +56,18 @@ struct SearchInfo { IDProperty *properties = nullptr; }; -struct SocketSearchData { +struct ModifierSearchData { uint32_t object_session_uid; char modifier_name[MAX_NAME]; +}; + +struct OperatorSearchData { + /** Can store this data directly, because it's more persistent than for the modifier. */ + SearchInfo info; +}; + +struct SocketSearchData { + std::variant search_data; char socket_identifier[MAX_NAME]; bool is_output; @@ -92,7 +102,7 @@ static geo_log::GeoTreeLog *get_root_tree_log(const NodesModifierData &nmd) static NodesModifierData *get_modifier_data(Main &bmain, const wmWindowManager &wm, - const SocketSearchData &data) + const ModifierSearchData &data) { if (ED_screen_animation_playing(&wm)) { /* Work around an issue where the attribute search exec function has stale pointers when data @@ -116,15 +126,22 @@ static NodesModifierData *get_modifier_data(Main &bmain, SearchInfo SocketSearchData::info(const bContext &C) const { - const NodesModifierData *nmd = get_modifier_data(*CTX_data_main(&C), *CTX_wm_manager(&C), *this); - if (nmd == nullptr) { - return {}; + if (const auto *modifier_search_data = std::get_if(&this->search_data)) { + const NodesModifierData *nmd = get_modifier_data( + *CTX_data_main(&C), *CTX_wm_manager(&C), *modifier_search_data); + if (nmd == nullptr) { + return {}; + } + if (nmd->node_group == nullptr) { + return {}; + } + geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd); + return {tree_log, nmd->node_group, nmd->settings.properties}; } - if (nmd->node_group == nullptr) { - return {}; + if (const auto *operator_search_data = std::get_if(&this->search_data)) { + return operator_search_data->info; } - geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd); - return {tree_log, nmd->node_group, nmd->settings.properties}; + return {}; } static void layer_name_search_update_fn( @@ -232,7 +249,11 @@ static void add_layer_name_search_button(DrawGroupInputsContext &ctx, return; } - SocketSearchData *data = MEM_dupallocN(__func__, ctx.socket_search_data_fn(socket)); + /* Using a custom free function make the search not work currently. So make sure this data can be + * freed with MEM_freeN. */ + SocketSearchData *data = static_cast( + MEM_mallocN(sizeof(SocketSearchData), __func__)); + *data = ctx.socket_search_data_fn(socket); UI_but_func_search_set_results_are_suggestions(but, true); UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); UI_but_func_search_set(but, @@ -346,7 +367,11 @@ static void add_attribute_search_button(DrawGroupInputsContext &ctx, return; } - SocketSearchData *data = MEM_dupallocN(__func__, ctx.socket_search_data_fn(socket)); + /* Using a custom free function make the search not work currently. So make sure this data can be + * freed with MEM_freeN. */ + SocketSearchData *data = static_cast( + MEM_mallocN(sizeof(SocketSearchData), __func__)); + *data = ctx.socket_search_data_fn(socket); UI_but_func_search_set_results_are_suggestions(but, true); UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); UI_but_func_search_set(but, @@ -881,8 +906,9 @@ void draw_geometry_nodes_modifier_ui(const bContext &C, PointerRNA *modifier_ptr }; ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData { SocketSearchData data{}; - data.object_session_uid = object.id.session_uid; - STRNCPY(data.modifier_name, nmd.modifier.name); + ModifierSearchData &modifier_search_data = data.search_data.emplace(); + modifier_search_data.object_session_uid = object.id.session_uid; + STRNCPY(modifier_search_data.modifier_name, nmd.modifier.name); STRNCPY(data.socket_identifier, io_socket.identifier); data.is_output = io_socket.flag & NODE_INTERFACE_SOCKET_OUTPUT; return data; @@ -936,4 +962,53 @@ void draw_geometry_nodes_modifier_ui(const bContext &C, PointerRNA *modifier_ptr } } +void draw_geometry_nodes_operator_redo_ui(const bContext &C, + wmOperator &op, + bNodeTree &tree, + geo_eval_log::GeoTreeLog *tree_log) +{ + uiLayout &layout = *op.layout; + Main &bmain = *CTX_data_main(&C); + PointerRNA bmain_ptr = RNA_main_pointer_create(&bmain); + + DrawGroupInputsContext ctx{ + C, &tree, tree_log, nodes::build_properties_vector_set(op.properties), op.ptr, &bmain_ptr}; + ctx.panel_open_property_fn = [&](const bNodeTreeInterfacePanel &io_panel) -> PanelOpenProperty { + Panel *root_panel = uiLayoutGetRootPanel(&layout); + LayoutPanelState *state = BKE_panel_layout_panel_state_ensure( + root_panel, + "node_operator_panel_" + std::to_string(io_panel.identifier), + io_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + PointerRNA state_ptr = RNA_pointer_create_discrete(nullptr, &RNA_LayoutPanelState, state); + return {state_ptr, "is_open"}; + }; + ctx.socket_search_data_fn = [&](const bNodeTreeInterfaceSocket &io_socket) -> SocketSearchData { + SocketSearchData data{}; + OperatorSearchData &operator_search_data = data.search_data.emplace(); + operator_search_data.info.tree = &tree; + operator_search_data.info.tree_log = tree_log; + operator_search_data.info.properties = op.properties; + STRNCPY(data.socket_identifier, io_socket.identifier); + data.is_output = io_socket.flag & NODE_INTERFACE_SOCKET_OUTPUT; + return data; + }; + ctx.draw_attribute_toggle_fn = + [&](uiLayout &layout, const int icon, const bNodeTreeInterfaceSocket &io_socket) { + const std::string prop_name = fmt::format( + "[\"{}{}\"]", BLI_str_escape(io_socket.identifier), nodes::input_use_attribute_suffix); + layout.prop(op.ptr, prop_name, UI_ITEM_R_ICON_ONLY, "", icon); + }; + + uiLayoutSetPropSep(&layout, true); + /* Decorators are added manually for supported properties because the + * attribute/value toggle requires a manually built layout anyway. */ + uiLayoutSetPropDecorate(&layout, false); + + tree.ensure_interface_cache(); + ctx.input_usages.reinitialize(tree.interface_inputs().size()); + nodes::socket_usage_inference::infer_group_interface_inputs_usage( + tree, ctx.properties, ctx.input_usages); + draw_interface_panel_content(ctx, &layout, tree.tree_interface.root_panel); +} + } // namespace blender::nodes