Nodes: support searching for specific math operations in Add menu

Previously, it was possible to search for specific math operations in
link-drag-search but not in the normal add menu. This patch adds support for
searching for specific operations in various math nodes.

A good trick for adding e.g. a vector add node is to search for `vadd`
(similarly with `iadd`).

The menu itself looks unchanged. This patch only adds additional elements to the
search.

Pull Request: https://projects.blender.org/blender/blender/pulls/138534
This commit is contained in:
Jacques Lucke 2025-05-08 04:28:22 +02:00
parent 0e8e98016b
commit 156a405dd5
7 changed files with 43 additions and 23 deletions

View file

@ -65,6 +65,17 @@ def draw_root_assets(layout):
layout.menu_contents("NODE_MT_node_add_root_catalogs")
def add_node_type_with_searchable_enum(context, layout, node_idname, property_name):
add_node_type(layout, node_idname)
if getattr(context, "is_menu_search", False):
node_type = getattr(bpy.types, node_idname)
for item in node_type.bl_rna.properties[property_name].enum_items_static:
props = node_add_menu.add_node_type(layout, node_idname, label=node_type.bl_rna.name + " ▸ " + item.name)
prop = props.settings.add()
prop.name = property_name
prop.value = repr(item.identifier)
def add_simulation_zone(layout, label):
"""Add simulation zone to a menu."""
props = layout.operator("node.add_simulation_zone", text=label, text_ctxt=i18n_contexts.default)

View file

@ -293,10 +293,10 @@ class NODE_MT_category_compositor_utilities(Menu):
bl_idname = "NODE_MT_category_compositor_utilities"
bl_label = "Utilities"
def draw(self, _context):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeMapRange")
node_add_menu.add_node_type(layout, "ShaderNodeMath")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "ShaderNodeMath", "operation")
node_add_menu.add_node_type(layout, "ShaderNodeMix")
node_add_menu.add_node_type(layout, "ShaderNodeClamp")
node_add_menu.add_node_type(layout, "ShaderNodeFloatCurve")
@ -317,12 +317,12 @@ class NODE_MT_category_compositor_vector(Menu):
bl_idname = "NODE_MT_category_compositor_vector"
bl_label = "Vector"
def draw(self, _context):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeCombineXYZ")
node_add_menu.add_node_type(layout, "ShaderNodeSeparateXYZ")
layout.separator()
node_add_menu.add_node_type(layout, "ShaderNodeVectorMath")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "ShaderNodeVectorMath", "operation")
node_add_menu.add_node_type(layout, "ShaderNodeVectorRotate")
props = node_add_menu.add_node_type(layout, "ShaderNodeMix", label=iface_("Mix Vector"))
ops = props.settings.add()

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu
from bl_ui import node_add_menu
from bpy.app.translations import (
@ -729,17 +730,17 @@ class NODE_MT_category_GEO_UTILITIES_MATH(Menu):
bl_idname = "NODE_MT_category_GEO_UTILITIES_MATH"
bl_label = "Math"
def draw(self, _context):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeBooleanMath")
node_add_menu.add_node_type(layout, "FunctionNodeIntegerMath")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "FunctionNodeBooleanMath", "operation")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "FunctionNodeIntegerMath", "operation")
node_add_menu.add_node_type(layout, "ShaderNodeClamp")
node_add_menu.add_node_type(layout, "FunctionNodeCompare")
node_add_menu.add_node_type(layout, "ShaderNodeFloatCurve")
node_add_menu.add_node_type(layout, "FunctionNodeFloatToInt")
node_add_menu.add_node_type(layout, "FunctionNodeHashValue")
node_add_menu.add_node_type(layout, "ShaderNodeMapRange")
node_add_menu.add_node_type(layout, "ShaderNodeMath")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "ShaderNodeMath", "operation")
node_add_menu.add_node_type(layout, "ShaderNodeMix")
node_add_menu.draw_assets_for_catalog(layout, "Utilities/Math")
@ -759,10 +760,10 @@ class NODE_MT_category_GEO_VECTOR(Menu):
bl_idname = "NODE_MT_category_GEO_VECTOR"
bl_label = "Vector"
def draw(self, _context):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeVectorCurve")
node_add_menu.add_node_type(layout, "ShaderNodeVectorMath")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "ShaderNodeVectorMath", "operation")
node_add_menu.add_node_type(layout, "ShaderNodeVectorRotate")
layout.separator()
node_add_menu.add_node_type(layout, "ShaderNodeCombineXYZ")

View file

@ -271,13 +271,13 @@ class NODE_MT_category_shader_converter(Menu):
node_add_menu.add_node_type(layout, "ShaderNodeCombineXYZ")
node_add_menu.add_node_type(layout, "ShaderNodeFloatCurve")
node_add_menu.add_node_type(layout, "ShaderNodeMapRange")
node_add_menu.add_node_type(layout, "ShaderNodeMath")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "ShaderNodeMath", "operation")
node_add_menu.add_node_type(layout, "ShaderNodeMix")
node_add_menu.add_node_type(layout, "ShaderNodeRGBToBW")
node_add_menu.add_node_type(layout, "ShaderNodeSeparateColor")
node_add_menu.add_node_type(layout, "ShaderNodeSeparateXYZ")
node_add_menu.add_node_type(layout, "ShaderNodeShaderToRGB", poll=object_eevee_shader_nodes_poll(context))
node_add_menu.add_node_type(layout, "ShaderNodeVectorMath")
node_add_menu.add_node_type_with_searchable_enum(context, layout, "ShaderNodeVectorMath", "operation")
node_add_menu.add_node_type(layout, "ShaderNodeWavelength")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)

View file

@ -2008,6 +2008,7 @@ bool ui_but_context_poll_operator_ex(bContext *C,
bool result;
int old_but_flag = 0;
const bContextStore *previous_ctx = CTX_store_get(C);
if (but) {
old_but_flag = but->flag;
@ -2030,7 +2031,7 @@ bool ui_but_context_poll_operator_ex(bContext *C,
const_cast<uiBut *>(but)->flag = old_but_flag;
if (but->context) {
CTX_store_set(C, nullptr);
CTX_store_set(C, previous_ctx);
}
}

View file

@ -3088,13 +3088,7 @@ void uiItemMContents(uiLayout *layout, const StringRef menuname)
return;
}
const bContextStore *previous_ctx = CTX_store_get(C);
UI_menutype_draw(C, mt, layout);
/* Restore context that was cleared by `UI_menutype_draw`. */
if (layout->context_) {
CTX_store_set(C, previous_ctx);
}
}
void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index)
@ -6216,15 +6210,19 @@ void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
ui_block_add_dynamic_listener(block, mt->listener);
}
bContextStore context_store;
if (layout->context_) {
CTX_store_set(C, layout->context_);
context_store = *layout->context_;
}
const bContextStore *previous_context_store = CTX_store_get(C);
if (previous_context_store) {
context_store.entries.extend(previous_context_store->entries);
}
CTX_store_set(C, &context_store);
mt->draw(C, &menu);
if (layout->context_) {
CTX_store_set(C, nullptr);
}
CTX_store_set(C, previous_context_store);
}
static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *pt)

View file

@ -415,6 +415,15 @@ static MenuSearch_Data *menu_items_from_ui_create(bContext *C,
blender::Map<MenuType *, const char *> menu_display_name_map;
const uiStyle *style = UI_style_get_dpi();
const bContextStore *old_context_store = CTX_store_get(C);
BLI_SCOPED_DEFER([&]() { CTX_store_set(C, old_context_store); });
bContextStore context_store;
if (old_context_store) {
context_store = *old_context_store;
}
context_store.entries.append({"is_menu_search", true});
CTX_store_set(C, &context_store);
/* Convert into non-ui structure. */
MenuSearch_Data *data = MEM_new<MenuSearch_Data>(__func__);
ResourceScope &scope = data->scope;