Search option for adding nodes.
authorLukas Toenne <lukas.toenne@googlemail.com>
Wed, 8 Aug 2012 16:44:16 +0000 (16:44 +0000)
committerLukas Toenne <lukas.toenne@googlemail.com>
Wed, 8 Aug 2012 16:44:16 +0000 (16:44 +0000)
The 'Add' menu in the node editor now has an option 'Search' at the top, which opens a separate popup for searching node types by name.

The operator for this is implemented completely in Python (this could also be done for the regular menu-based Add options in the future). There are a few necessary extensions to the RNA as well:

* The View2D struct in regions is now exposed. Currently only contains converter functions for coordinates from the region to the view (i.e. scrolled and zoomed view space). Used for converting mouse location to node space.

* The SpaceNode exposes the existing 'cursor_location' for operators to store mouse position beyond invoke calls. Not used for anything else (transforms) so far.

* The edit_tree in SpaceNode is also exposed, this is needed for operators to work correctly inside node groups.

release/scripts/startup/bl_operators/__init__.py
release/scripts/startup/bl_operators/node.py [new file with mode: 0644]
source/blender/editors/space_node/node_header.c
source/blender/makesrna/intern/rna_screen.c
source/blender/makesrna/intern/rna_space.c
source/blenderplayer/bad_level_call_stubs/stubs.c

index 06b4429d25ca2143c4f7fd3a1106e179f1b711a5..ecbbe34dbb48ee77ae71790a72379679d4f4785b 100644 (file)
@@ -29,6 +29,7 @@ _modules = (
     "console",
     "image",
     "mesh",
+    "node",
     "object_align",
     "object",
     "object_randomize_transform",
diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py
new file mode 100644 (file)
index 0000000..0927f13
--- /dev/null
@@ -0,0 +1,100 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8-80 compliant>
+
+import bpy
+from bpy.types import Operator
+from bpy.props import (EnumProperty,
+                       FloatVectorProperty,
+                       StringProperty,
+                       CollectionProperty
+                       )
+
+# XXX These node item lists should actually be generated by a callback at operator execution time (see node_type_items below),
+# using the active node tree from the context. Due to a difficult bug in bpy this is not possible (item list memory gets freed too early),
+# so for now just copy the static item lists to these global variables.
+#
+# In the custom_nodes branch, the static per-tree-type node items are replaced by a single independent type list anyway (with a poll function
+# to limit node types to the respective trees). So this workaround is only temporary.
+
+node_type_items_dict = {}
+node_type_items_dict['SHADER'] = [(item.identifier, item.name, item.description, item.value) for item in bpy.types.ShaderNode.bl_rna.properties['type'].enum_items]
+node_type_items_dict['COMPOSITING'] = [(item.identifier, item.name, item.description, item.value) for item in bpy.types.CompositorNode.bl_rna.properties['type'].enum_items]
+node_type_items_dict['TEXTURE'] = [(item.identifier, item.name, item.description, item.value) for item in bpy.types.TextureNode.bl_rna.properties['type'].enum_items]
+
+# Returns the enum item list for the edited tree in the context
+def node_type_items(self, context):
+    snode = context.space_data
+    if not snode:
+        return []
+    tree = snode.edit_tree
+    if not tree:
+        return []
+    
+    # XXX Does not work correctly, see comment above
+    #return [(item.identifier, item.name, item.description, item.value) for item in tree.nodes.bl_rna.functions['new'].parameters['type'].enum_items]
+    
+    if tree.type in node_type_items_dict:
+        return node_type_items_dict[tree.type]
+    else:
+        return []
+
+class NODE_OT_add_search(bpy.types.Operator):
+    '''Add a node to the active tree'''
+    bl_idname = "node.add_search"
+    bl_label = "Search and Add Node"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    # XXX this should be called 'node_type' but the operator search property is hardcoded to 'type' by a hack in bpy_operator_wrap.c ...
+    type = EnumProperty(items=node_type_items, name="Node Type", description="Node type")
+
+    def create_node(self, context):
+        space = context.space_data
+        tree = space.edit_tree
+        
+        node = tree.nodes.new(type=self.type)
+        for n in tree.nodes:
+            if n==node:
+                node.select = True
+                tree.nodes.active = node
+            else:
+                node.select = False
+        node.location = space.cursor_location
+        return node
+    
+    @classmethod
+    def poll(cls, context):
+        space = context.space_data
+        # needs active node editor and a tree to add nodes to
+        return space.type == 'NODE_EDITOR' and space.edit_tree
+
+    def execute(self, context):
+        self.create_node(context)
+        return {'FINISHED'}
+
+    def invoke(self, context, event):
+        space = context.space_data
+        v2d = context.region.view2d
+
+        # convert mouse position to the View2D for later node placement
+        space.cursor_location = v2d.region_to_view(event.mouse_region_x, event.mouse_region_y)
+        
+        context.window_manager.invoke_search_popup(self)
+        return {'CANCELLED'}
+
index 27ee600632733c34431e811d3642a05305676d12..b6bf8d77732596a721dabbbaa03eb6019a862bb7 100644 (file)
@@ -235,6 +235,9 @@ static void node_menu_add(const bContext *C, Menu *menu)
        if (!snode->nodetree)
                uiLayoutSetActive(layout, FALSE);
        
+       uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+       uiItemO(layout, "Search ...", 0, "NODE_OT_add_search");
+       
        if (ntreetype && ntreetype->foreach_nodeclass)
                ntreetype->foreach_nodeclass(scene, layout, node_menu_add_foreach_cb);
 }
index d405cba660731a985f9123f6352fd04d40caf156..a6d4e473df409ab6a477aa3ea1a6ece49d8f0542 100644 (file)
@@ -57,6 +57,8 @@ EnumPropertyItem region_type_items[] = {
 
 #include "BKE_global.h"
 
+#include "UI_view2d.h"
+
 static void rna_Screen_scene_set(PointerRNA *ptr, PointerRNA value)
 {
        bScreen *sc = (bScreen *)ptr->data;
@@ -138,6 +140,19 @@ static void rna_Area_type_update(bContext *C, PointerRNA *ptr)
        }
 }
 
+void rna_View2D_region_to_view(struct View2D *v2d, int x, int y, float result[2])
+{
+       UI_view2d_region_to_view(v2d, x, y, &result[0], &result[1]);
+}
+
+void rna_View2D_view_to_region(struct View2D *v2d, float x, float y, int clip, int result[2])
+{
+       if (clip)
+               UI_view2d_view_to_region(v2d, x, y, &result[0], &result[1]);
+       else
+               UI_view2d_to_region_no_clip(v2d, x, y, &result[0], &result[1]);
+}
+
 #else
 
 /* Area.spaces */
@@ -220,6 +235,50 @@ static void rna_def_area(BlenderRNA *brna)
        RNA_def_string(func, "text", NULL, 0, "Text", "New string for the header, no argument clears the text");
 }
 
+static void rna_def_view2d_api(StructRNA *srna)
+{
+       FunctionRNA *func;
+       PropertyRNA *parm;
+       
+       static const float view_default[2] = {0.0f, 0.0f};
+       static const int region_default[2] = {0.0f, 0.0f};
+       
+       func = RNA_def_function(srna, "region_to_view", "rna_View2D_region_to_view");
+       RNA_def_function_ui_description(func, "Transform region coordinates to 2D view");
+       parm = RNA_def_int(func, "x", 0, INT_MIN, INT_MAX, "x", "Region x coordinate", -10000, 10000);
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       parm = RNA_def_int(func, "y", 0, INT_MIN, INT_MAX, "y", "Region y coordinate", -10000, 10000);
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       parm = RNA_def_float_array(func, "result", 2, view_default, -FLT_MAX, FLT_MAX, "Result", "View coordinates", -10000.0f, 10000.0f);
+       RNA_def_property_flag(parm, PROP_THICK_WRAP);
+       RNA_def_function_output(func, parm);
+
+       func = RNA_def_function(srna, "view_to_region", "rna_View2D_view_to_region");
+       RNA_def_function_ui_description(func, "Transform 2D view coordinates to region");
+       parm = RNA_def_float(func, "x", 0.0f, -FLT_MAX, FLT_MAX, "x", "2D View x coordinate", -10000.0f, 10000.0f);
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       parm = RNA_def_float(func, "y", 0.0f, -FLT_MAX, FLT_MAX, "y", "2D View y coordinate", -10000.0f, 10000.0f);
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       RNA_def_boolean(func, "clip", 1, "Clip", "Clip coordinates to the visible region");
+       parm = RNA_def_int_array(func, "result", 2, region_default, INT_MIN, INT_MAX, "Result", "Region coordinates", -10000, 10000);
+       RNA_def_property_flag(parm, PROP_THICK_WRAP);
+       RNA_def_function_output(func, parm);
+}
+
+static void rna_def_view2d(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       /* PropertyRNA *prop; */
+
+       srna = RNA_def_struct(brna, "View2D", NULL);
+       RNA_def_struct_ui_text(srna, "View2D", "Scroll and zoom for a 2D region");
+       RNA_def_struct_sdna(srna, "View2D");
+       
+       /* TODO more View2D properties could be exposed here (read-only) */
+       
+       rna_def_view2d_api(srna);
+}
+
 static void rna_def_region(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -260,6 +319,12 @@ static void rna_def_region(BlenderRNA *brna)
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
        RNA_def_property_ui_text(prop, "Height", "Region height");
 
+       prop = RNA_def_property(srna, "view2d", PROP_POINTER, PROP_NONE);
+       RNA_def_property_pointer_sdna(prop, NULL, "v2d");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_ui_text(prop, "View2D", "2D view of the region");
+
        RNA_def_function(srna, "tag_redraw", "ED_region_tag_redraw");
 }
 
@@ -345,6 +410,7 @@ void RNA_def_screen(BlenderRNA *brna)
        rna_def_screen(brna);
        rna_def_area(brna);
        rna_def_region(brna);
+       rna_def_view2d(brna);
 }
 
 #endif
index 01a58463fb6deb3527c850585c1381dbb8407166..b09227a9461e72588180b889f7afaaaeacda9b53 100644 (file)
@@ -2961,9 +2961,14 @@ static void rna_def_space_node(BlenderRNA *brna)
        RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
        RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_SpaceNodeEditor_node_tree_poll");
        RNA_def_property_flag(prop, PROP_EDITABLE);
-       RNA_def_property_ui_text(prop, "Node Tree", "Node tree being displayed and edited");
+       RNA_def_property_ui_text(prop, "Node Tree", "Node tree being displayed");
        RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, "rna_SpaceNodeEditor_node_tree_update");
 
+       prop = RNA_def_property(srna, "edit_tree", PROP_POINTER, PROP_NONE);
+       RNA_def_property_pointer_sdna(prop, NULL, "edittree");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_ui_text(prop, "Edit Tree", "Edited node tree");
+
        prop = RNA_def_property(srna, "show_backdrop", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flag", SNODE_BACKDRAW);
        RNA_def_property_ui_text(prop, "Backdrop", "Use active Viewer Node output as backdrop for compositing nodes");
@@ -3007,6 +3012,13 @@ static void rna_def_space_node(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "flag", SNODE_USE_HIDDEN_PREVIEW);
        RNA_def_property_ui_text(prop, "Hide Preview", "Hide preview for newly creating nodes");
        RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+
+       /* the mx/my "cursor" in the node editor is used only by operators to store the mouse position */
+       prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "mx");
+       RNA_def_property_array(prop, 2);
+       RNA_def_property_ui_text(prop, "Cursor Location", "Location for adding new nodes");
+       RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
 }
 
 static void rna_def_space_logic(BlenderRNA *brna)
index 69cb0dc619bd7abb41a7f68163fbaa6c2bb9a119..9ae2cb0c3531d61ad3fad4a6ab915514ea2f9834 100644 (file)
@@ -96,6 +96,7 @@ struct Tex;
 struct TexResult;
 struct Text;
 struct ToolSettings;
+struct View2D;
 struct View3D;
 struct bAction;
 struct bArmature;
@@ -212,6 +213,9 @@ void *ED_region_draw_cb_activate(struct ARegionType *art, void(*draw)(const stru
 void *ED_region_draw_cb_customdata(void *handle) {return 0;} /* XXX This one looks wrong also */
 void ED_region_draw_cb_exit(struct ARegionType *art, void *handle) {}
 void   ED_area_headerprint(struct ScrArea *sa, char *str) {}
+void UI_view2d_region_to_view(struct View2D *v2d, int x, int y, float *viewx, float *viewy) {}
+void UI_view2d_view_to_region(struct View2D *v2d, float x, float y, int *regionx, int *regiony) {}
+void UI_view2d_to_region_no_clip(struct View2D *v2d, float x, float y, int *regionx, int *region_y) {}
 
 struct EditBone *ED_armature_bone_get_mirrored(struct ListBase *edbo, struct EditBone *ebo) {return (struct EditBone *) NULL;}
 struct EditBone *ED_armature_edit_bone_add(struct bArmature *arm, char *name) {return (struct EditBone*) NULL;}