Replacing the node Add menu and making the toolbar useful

As some people have already noticed, the "Add" menu for nodes is a bit messy since pynodes merge. The reason for this is that the order of nodes in submenus (categories) was previously defined by the order in which all nodes are registered (at the bottom of blenkernel/intern/node.c). For the dynamic registration of node types now possible this system of defining node order along with registration is no longer viable: while it would still sort of work for C nodes, it is completely meaningless for dynamic (python) nodes, which are basically registered automatically in whatever order modules and addons are loaded, with the added complexity of unloading and reloading.

To fix this problem and add a bunch of desirable features this commit replaces the C menu with a python implementation. The new menu does not rely on any particular order of types in the node registry, but instead uses a simple explicit list of all the available nodes, grouped by categories (in scripts/nodeitems_builtins.py).

There are a number of additional features that become possible with this implementation:

1) Node Toolbar can be populated!
The list of nodes is used to create 2 UI items for each node: 1 entry in a submenu of "Add" menu and 1 item in a node toolbar panel with basically the same functionality. Clicking a button in the toolbar will add a new node of this type, just like selecting an item in the menu. The toolbar has the advantage of having collapsible panels for each category, so users can decide if they don't need certain nodes categories and have the rest more easily accessible.

2) Each node item is a true operator call.
The old Add menu is a pretty old piece of C code which doesn't even use proper operator buttons. Now there is a generic node_add operator which can be used very flexibly for adding any of the available nodes.

3) Node Items support additional settings.
Each "NodeItem" consists of the basic node type plus an optional list of initial settings that shall be applied to a new instance. This gives additional flexibility for creating variants of the same node or for defining preferred initial settings. E.g. it has been requested to disable previews for all nodes except inputs, this would be simple change in the py code and much less intrusive than in C.

4) Node items can be generated with a function.
A callback can be used in any category instead of the fixed list, which generates a set of items based on the context (much like dynamic enum items in bpy.props). Originally this was implemented for group nodes, because these nodes only make sense when linked to a node tree from the library data. This principle could come in handy for a number of other nodes, e.g. Image nodes could provide a similar list of node variants based on images in the library - no need to first add node, then select an image.

WARNING: pynodes scripters will have to rework their "draw_add_menu" callback in node tree types, this has been removed now! It was already pretty redundant, since one can add draw functions to the Add menu just like for any other menu. In the future i'd like to improve the categories system further so scripters can use it for custom node systems too, for now just make a draw callback and attach it to the Add menu.
This commit is contained in:
Lukas Toenne 2013-04-13 15:38:02 +00:00
parent a710434f56
commit 94931f9f45
10 changed files with 509 additions and 260 deletions

View File

@ -0,0 +1,123 @@
# ##### 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 compliant>
import bpy
from bpy.types import Menu, Panel
node_categories = []
class NodeCategory():
@classmethod
def poll(cls, context):
return True
@property
def items(self):
if hasattr(self, '_items'):
return self._items
elif hasattr(self, '_itemfunc'):
return self._itemfunc(self)
def __init__(self, identifier, name, description="", items=[]):
self.identifier = identifier
self.name = name
self.description = description
if callable(items):
self._itemfunc = items
else:
self._items = items
class NodeItem():
def __init__(self, nodetype, label=None, settings={}):
self.nodetype = nodetype
self._label = label
self.settings = settings
@property
def label(self):
if self._label:
return self._label
else:
# if no custom label is defined, fall back to the node type UI name
return getattr(bpy.types, self.nodetype).bl_rna.name
# Empty base class to detect subclasses in bpy.types
class NodeCategoryUI():
pass
def register_node_ui():
# works as draw function for both menus and panels
def draw_node_item(self, context):
layout = self.layout
for item in self.category.items:
op = layout.operator("node.add_node", text=item.label)
op.type = item.nodetype
op.use_transform = True
for setting in item.settings.items():
ops = op.settings.add()
ops.name = setting[0]
ops.value = setting[1]
for cat in node_categories:
menu = type("NODE_MT_category_"+cat.identifier, (bpy.types.Menu, NodeCategoryUI), {
"bl_space_type" : 'NODE_EDITOR',
"bl_label" : cat.name,
"category" : cat,
"poll" : cat.poll,
"draw" : draw_node_item,
})
panel = type("NODE_PT_category_"+cat.identifier, (bpy.types.Panel, NodeCategoryUI), {
"bl_space_type" : 'NODE_EDITOR',
"bl_region_type" : 'TOOLS',
"bl_label" : cat.name,
"category" : cat,
"poll" : cat.poll,
"draw" : draw_node_item,
})
bpy.utils.register_class(menu)
bpy.utils.register_class(panel)
def draw_add_menu(self, context):
layout = self.layout
for cat in node_categories:
if cat.poll(context):
layout.menu("NODE_MT_category_%s" % cat.identifier)
add_menu = type("NODE_MT_add", (bpy.types.Menu, NodeCategoryUI), {
"bl_space_type" : 'NODE_EDITOR',
"bl_label" : "Add",
"draw" : draw_add_menu,
})
bpy.utils.register_class(add_menu)
def unregister_node_ui():
# unregister existing UI classes
for c in NodeCategoryUI.__subclasses__():
if hasattr(c, "bl_rna"):
bpy.utils.unregister_class(c)
del c

View File

@ -19,8 +19,8 @@
# <pep8-80 compliant>
import bpy
from bpy.types import Operator
from bpy.props import BoolProperty, EnumProperty, StringProperty
from bpy.types import Operator, PropertyGroup
from bpy.props import BoolProperty, CollectionProperty, EnumProperty, StringProperty
# Base class for node 'Add' operators
@ -68,6 +68,13 @@ class NodeAddOperator():
return self.execute(context)
class NodeSetting(PropertyGroup):
value = StringProperty(
name="Value",
description="Python expression to be evaluated as the initial node setting",
default="",
)
# Simple basic operator for adding a node
class NODE_OT_add_node(NodeAddOperator, Operator):
'''Add a node to the active tree'''
@ -78,33 +85,41 @@ class NODE_OT_add_node(NodeAddOperator, Operator):
name="Node Type",
description="Node type",
)
# optional group tree parameter for group nodes
group_tree = StringProperty(
name="Group tree",
description="Group node tree name",
)
use_transform = BoolProperty(
name="Use Transform",
description="Start transform operator after inserting the node",
default=False,
)
settings = CollectionProperty(
name="Settings",
description="Settings to be applied on the newly created node",
type=NodeSetting,
)
def execute(self, context):
node = self.create_node(context, self.type)
# set the node group tree of a group node
if self.properties.is_property_set('group_tree'):
node.node_tree = bpy.data.node_groups[self.group_tree]
for setting in self.settings:
# XXX catch exceptions here?
value = eval(setting.value)
try:
setattr(node, setting.name, value)
except AttributeError as e:
self.report({'ERROR_INVALID_INPUT'}, "Node has no attribute "+setting.name)
print (str(e))
# Continue despite invalid attribute
return {'FINISHED'}
def invoke(self, context, event):
self.store_mouse_cursor(context, event)
result = self.execute(context)
if self.use_transform and ('FINISHED' in result):
return bpy.ops.transform.translate('INVOKE_DEFAULT')
else:
return result
bpy.ops.transform.translate('INVOKE_DEFAULT')
return result
def node_classes_iter(base=bpy.types.Node):
@ -186,19 +201,6 @@ class NODE_OT_add_search(NodeAddOperator, Operator):
return {'CANCELLED'}
# Simple basic operator for adding a node without further initialization
class NODE_OT_add_node(NodeAddOperator, bpy.types.Operator):
'''Add a node to the active tree'''
bl_idname = "node.add_node"
bl_label = "Add Node"
type = StringProperty(name="Node Type", description="Node type")
def execute(self, context):
node = self.create_node(context, self.type)
return {'FINISHED'}
class NODE_OT_add_group_node(NodeAddOperator, bpy.types.Operator):
'''Add a group node to the active tree'''
bl_idname = "node.add_group_node"

View File

@ -0,0 +1,356 @@
# ##### 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 compliant>
import bpy
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem
# Subclasses for standard node types
class CompositorNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'CompositorNodeTree'
class ShaderNewNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'ShaderNodeTree' and \
context.scene.render.use_shading_nodes
class ShaderOldNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'ShaderNodeTree' and \
not context.scene.render.use_shading_nodes
class TextureNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'TextureNodeTree'
def compositor_node_group_items(self):
return [NodeItem('CompositorNodeGroup', group.name, { "node_tree" : "bpy.data.node_groups['%s']" % group.name })
for group in bpy.data.node_groups if group.bl_idname == 'CompositorNodeTree']
# Note: node groups not distinguished by old/new shader nodes
def shader_node_group_items(self):
return [NodeItem('ShaderNodeGroup', group.name, { "node_tree" : "bpy.data.node_groups['%s']" % group.name })
for group in bpy.data.node_groups if group.bl_idname == 'ShaderNodeTree']
def texture_node_group_items(self):
return [NodeItem('TextureNodeGroup', group.name, { "node_tree" : "bpy.data.node_groups['%s']" % group.name })
for group in bpy.data.node_groups if group.bl_idname == 'TextureNodeTree']
# All standard node categories currently used in nodes.
std_node_categories = [
# Shader Nodes
ShaderOldNodeCategory("SH_INPUT", "Input", items=[
NodeItem("ShaderNodeMaterial"),
NodeItem("ShaderNodeCameraData"),
NodeItem("ShaderNodeValue"),
NodeItem("ShaderNodeRGB"),
NodeItem("ShaderNodeTexture"),
NodeItem("ShaderNodeGeometry"),
NodeItem("ShaderNodeExtendedMaterial"),
]),
ShaderOldNodeCategory("SH_OUTPUT", "Output", items=[
NodeItem("ShaderNodeOutput"),
]),
ShaderOldNodeCategory("SH_OP_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeInvert"),
NodeItem("ShaderNodeHueSaturation"),
]),
ShaderOldNodeCategory("SH_OP_VECTOR", "Vector", items=[
NodeItem("ShaderNodeNormal"),
NodeItem("ShaderNodeMapping"),
NodeItem("ShaderNodeVectorCurve"),
]),
ShaderOldNodeCategory("SH_CONVERTOR", "Converter", items=[
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeRGBToBW"),
NodeItem("ShaderNodeMath"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeSqueeze"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
ShaderOldNodeCategory("SH_SCRIPT", "Script", items=[
]),
ShaderOldNodeCategory("SH_GROUP", "Group", items=shader_node_group_items),
ShaderOldNodeCategory("SH_LAYOUT", "Layout", items=[
]),
# New Shader Nodes (Cycles)
ShaderNewNodeCategory("SH_NEW_INPUT", "Input", items=[
NodeItem("ShaderNodeTexCoord"),
NodeItem("ShaderNodeAttribute"),
NodeItem("ShaderNodeLightPath"),
NodeItem("ShaderNodeFresnel"),
NodeItem("ShaderNodeLayerWeight"),
NodeItem("ShaderNodeRGB"),
NodeItem("ShaderNodeValue"),
NodeItem("ShaderNodeTangent"),
NodeItem("ShaderNodeNewGeometry"),
NodeItem("ShaderNodeObjectInfo"),
NodeItem("ShaderNodeHairInfo"),
NodeItem("ShaderNodeCameraData"),
NodeItem("ShaderNodeParticleInfo"),
]),
ShaderNewNodeCategory("SH_NEW_OUTPUT", "Output", items=[
NodeItem("ShaderNodeOutputLamp"),
NodeItem("ShaderNodeOutputMaterial"),
NodeItem("ShaderNodeOutputWorld"),
]),
ShaderNewNodeCategory("SH_NEW_SHADER", "Shader", items=[
NodeItem("ShaderNodeMixShader"),
NodeItem("ShaderNodeAddShader"),
NodeItem("ShaderNodeBsdfDiffuse"),
NodeItem("ShaderNodeBsdfGlossy"),
NodeItem("ShaderNodeBsdfTransparent"),
NodeItem("ShaderNodeBsdfRefraction"),
NodeItem("ShaderNodeBsdfGlass"),
NodeItem("ShaderNodeBsdfTranslucent"),
NodeItem("ShaderNodeEmission"),
NodeItem("ShaderNodeBsdfAnisotropic"),
NodeItem("ShaderNodeBackground"),
NodeItem("ShaderNodeBsdfVelvet"),
NodeItem("ShaderNodeSubsurfaceScatter"),
NodeItem("ShaderNodeHoldout"),
NodeItem("ShaderNodeAmbientOcclusion"),
]),
ShaderNewNodeCategory("SH_NEW_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexImage"),
NodeItem("ShaderNodeTexEnvironment"),
NodeItem("ShaderNodeTexSky"),
NodeItem("ShaderNodeTexNoise"),
NodeItem("ShaderNodeTexWave"),
NodeItem("ShaderNodeTexVoronoi"),
NodeItem("ShaderNodeTexMusgrave"),
NodeItem("ShaderNodeTexGradient"),
NodeItem("ShaderNodeTexMagic"),
NodeItem("ShaderNodeTexChecker"),
NodeItem("ShaderNodeTexBrick"),
]),
ShaderNewNodeCategory("SH_NEW_OP_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeInvert"),
NodeItem("ShaderNodeLightFalloff"),
NodeItem("ShaderNodeHueSaturation"),
NodeItem("ShaderNodeGamma"),
NodeItem("ShaderNodeBrightContrast"),
]),
ShaderNewNodeCategory("SH_NEW_OP_VECTOR", "Vector", items=[
NodeItem("ShaderNodeMapping"),
NodeItem("ShaderNodeBump"),
NodeItem("ShaderNodeNormalMap"),
NodeItem("ShaderNodeNormal"),
NodeItem("ShaderNodeVectorCurve"),
]),
ShaderNewNodeCategory("SH_NEW_CONVERTOR", "Converter", items=[
NodeItem("ShaderNodeMath"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeRGBToBW"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
ShaderNewNodeCategory("SH_NEW_SCRIPT", "Script", items=[
NodeItem("ShaderNodeScript"),
]),
ShaderNewNodeCategory("SH_NEW_GROUP", "Group", items=shader_node_group_items),
ShaderNewNodeCategory("SH_NEW_LAYOUT", "Layout", items=[
]),
# Compositor Nodes
CompositorNodeCategory("CMP_INPUT", "Input", items = [
NodeItem("CompositorNodeRLayers"),
NodeItem("CompositorNodeImage"),
NodeItem("CompositorNodeMovieClip"),
NodeItem("CompositorNodeMask"),
NodeItem("CompositorNodeRGB"),
NodeItem("CompositorNodeValue"),
NodeItem("CompositorNodeTexture"),
NodeItem("CompositorNodeBokehImage"),
NodeItem("CompositorNodeTime"),
NodeItem("CompositorNodeTrackPos"),
]),
CompositorNodeCategory("CMP_OUTPUT", "Output", items = [
NodeItem("CompositorNodeComposite"),
NodeItem("CompositorNodeViewer"),
NodeItem("CompositorNodeSplitViewer"),
NodeItem("CompositorNodeOutputFile"),
NodeItem("CompositorNodeLevels"),
]),
CompositorNodeCategory("CMP_OP_COLOR", "Color", items = [
NodeItem("CompositorNodeMixRGB"),
NodeItem("CompositorNodeAlphaOver"),
NodeItem("CompositorNodeInvert"),
NodeItem("CompositorNodeCurveRGB"),
NodeItem("CompositorNodeHueSat"),
NodeItem("CompositorNodeColorBalance"),
NodeItem("CompositorNodeHueCorrect"),
NodeItem("CompositorNodeBrightContrast"),
NodeItem("CompositorNodeGamma"),
NodeItem("CompositorNodeColorCorrection"),
NodeItem("CompositorNodeTonemap"),
NodeItem("CompositorNodeZcombine"),
]),
CompositorNodeCategory("CMP_CONVERTOR", "Converter", items = [
NodeItem("CompositorNodeMath"),
NodeItem("CompositorNodeValToRGB"),
NodeItem("CompositorNodeSetAlpha"),
NodeItem("CompositorNodePremulKey"),
NodeItem("CompositorNodeIDMask"),
NodeItem("CompositorNodeRGBToBW"),
NodeItem("CompositorNodeSepRGBA"),
NodeItem("CompositorNodeCombRGBA"),
NodeItem("CompositorNodeSepHSVA"),
NodeItem("CompositorNodeCombHSVA"),
NodeItem("CompositorNodeSepYUVA"),
NodeItem("CompositorNodeCombYUVA"),
NodeItem("CompositorNodeSepYCCA"),
NodeItem("CompositorNodeCombYCCA"),
]),
CompositorNodeCategory("CMP_OP_FILTER", "Filter", items = [
NodeItem("CompositorNodeBlur"),
NodeItem("CompositorNodeBilateralblur"),
NodeItem("CompositorNodeDilateErode"),
NodeItem("CompositorNodeDespeckle"),
NodeItem("CompositorNodeFilter"),
NodeItem("CompositorNodeBokehBlur"),
NodeItem("CompositorNodeVecBlur"),
NodeItem("CompositorNodeDefocus"),
NodeItem("CompositorNodeGlare"),
NodeItem("CompositorNodeInpaint"),
NodeItem("CompositorNodeDBlur"),
NodeItem("CompositorNodePixelate"),
]),
CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items = [
NodeItem("CompositorNodeNormal"),
NodeItem("CompositorNodeMapValue"),
NodeItem("CompositorNodeMapRange"),
NodeItem("CompositorNodeNormalize"),
NodeItem("CompositorNodeCurveVec"),
]),
CompositorNodeCategory("CMP_MATTE", "Matte", items = [
NodeItem("CompositorNodeKeying"),
NodeItem("CompositorNodeKeyingScreen"),
NodeItem("CompositorNodeChannelMatte"),
NodeItem("CompositorNodeColorSpill"),
NodeItem("CompositorNodeBoxMask"),
NodeItem("CompositorNodeEllipseMask"),
NodeItem("CompositorNodeLumaMatte"),
NodeItem("CompositorNodeDiffMatte"),
NodeItem("CompositorNodeDistanceMatte"),
NodeItem("CompositorNodeChromaMatte"),
NodeItem("CompositorNodeColorMatte"),
NodeItem("CompositorNodeDoubleEdgeMask"),
]),
CompositorNodeCategory("CMP_DISTORT", "Distort", items = [
NodeItem("CompositorNodeScale"),
NodeItem("CompositorNodeLensdist"),
NodeItem("CompositorNodeMovieDistortion"),
NodeItem("CompositorNodeTranslate"),
NodeItem("CompositorNodeRotate"),
NodeItem("CompositorNodeFlip"),
NodeItem("CompositorNodeCrop"),
NodeItem("CompositorNodeDisplace"),
NodeItem("CompositorNodeMapUV"),
NodeItem("CompositorNodeTransform"),
NodeItem("CompositorNodeStabilize"),
]),
CompositorNodeCategory("CMP_GROUP", "Group", items=compositor_node_group_items),
CompositorNodeCategory("CMP_LAYOUT", "Layout", items = [
NodeItem("CompositorNodeSwitch"),
]),
# Texture Nodes
TextureNodeCategory("TEX_INPUT", "Input", items = [
NodeItem("TextureNodeCurveTime"),
NodeItem("TextureNodeCoordinates"),
NodeItem("TextureNodeTexture"),
NodeItem("TextureNodeImage"),
]),
TextureNodeCategory("TEX_OUTPUT", "Output", items = [
NodeItem("TextureNodeOutput"),
NodeItem("TextureNodeViewer"),
]),
TextureNodeCategory("TEX_OP_COLOR", "Color", items = [
NodeItem("TextureNodeMixRGB"),
NodeItem("TextureNodeCurveRGB"),
NodeItem("TextureNodeInvert"),
NodeItem("TextureNodeHueSaturation"),
NodeItem("TextureNodeCompose"),
NodeItem("TextureNodeDecompose"),
]),
TextureNodeCategory("TEX_PATTERN", "Pattern", items = [
NodeItem("TextureNodeChecker"),
NodeItem("TextureNodeBricks"),
]),
TextureNodeCategory("TEX_TEXTURE", "Textures", items = [
NodeItem("TextureNodeTexNoise"),
NodeItem("TextureNodeTexDistNoise"),
NodeItem("TextureNodeTexClouds"),
NodeItem("TextureNodeTexBlend"),
NodeItem("TextureNodeTexVoronoi"),
NodeItem("TextureNodeTexMagic"),
NodeItem("TextureNodeTexMarble"),
NodeItem("TextureNodeTexWood"),
NodeItem("TextureNodeTexMusgrave"),
NodeItem("TextureNodeTexStucci"),
]),
TextureNodeCategory("TEX_CONVERTOR", "Converter", items = [
NodeItem("TextureNodeMath"),
NodeItem("TextureNodeValToRGB"),
NodeItem("TextureNodeRGBToBW"),
NodeItem("TextureNodeValToNor"),
NodeItem("TextureNodeDistance"),
]),
TextureNodeCategory("TEX_DISTORT", "Distort", items = [
NodeItem("TextureNodeScale"),
NodeItem("TextureNodeTranslate"),
NodeItem("TextureNodeRotate"),
]),
TextureNodeCategory("TEX_GROUP", "Group", items=texture_node_group_items),
TextureNodeCategory("TEX_LAYOUT", "Layout", items = [
]),
]
def register():
# XXX can be made a lot nicer, just get it working for now
nodeitems_utils.node_categories = std_node_categories
nodeitems_utils.register_node_ui()
def unregister():
nodeitems_utils.unregister_node_ui()
nodeitems_utils.node_categories = []
if __name__ == "__main__":
register()

View File

@ -305,8 +305,6 @@ typedef struct bNodeTreeType {
void (*free_cache)(struct bNodeTree *ntree);
void (*free_node_cache)(struct bNodeTree *ntree, struct bNode *node);
void (*foreach_nodeclass)(struct Scene *scene, void *calldata, bNodeClassCallback func); /* iteration over all node classes */
/* Add menu for this node tree. */
void (*draw_add_menu)(const struct bContext *C, struct uiLayout *layout, struct bNodeTree *ntree);
/* Check visibility in the node editor */
int (*poll)(const struct bContext *C, struct bNodeTreeType *ntreetype);
/* Select a node tree from the context */

View File

@ -47,7 +47,6 @@ set(SRC
node_draw.c
node_edit.c
node_group.c
node_header.c
node_ops.c
node_relationships.c
node_select.c

View File

@ -77,108 +77,6 @@
#include "NOD_shader.h"
#include "NOD_texture.h"
/* ****************** MENU FUNCTIONS ***************** */
static void node_add_menu_class(bContext *C, uiLayout *layout, void *arg_nodeclass)
{
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree;
int nodeclass = GET_INT_FROM_POINTER(arg_nodeclass);
int event, compatibility = 0;
ntree = snode->nodetree;
if (!ntree) {
uiItemS(layout);
return;
}
if (ntree->type == NTREE_SHADER) {
if (BKE_scene_use_new_shading_nodes(scene))
compatibility = NODE_NEW_SHADING;
else
compatibility = NODE_OLD_SHADING;
}
if (nodeclass == NODE_CLASS_GROUP) {
Main *bmain = CTX_data_main(C);
bNodeTree *ngroup;
const char *ngroup_type, *node_type;
PointerRNA ptr;
NODE_TYPES_BEGIN(ntype)
if (ntype->nclass != nodeclass || !ntype->ui_name)
continue;
if (!ntype->poll(ntype, ntree))
continue;
switch (ntree->type) {
case NTREE_COMPOSIT:
ngroup_type = "CompositorNodeTree";
node_type = "CompositorNodeGroup";
break;
case NTREE_SHADER:
ngroup_type = "ShaderNodeTree";
node_type = "ShaderNodeGroup";
break;
case NTREE_TEXTURE:
ngroup_type = "TextureNodeTree";
node_type = "TextureNodeGroup";
break;
}
ptr = uiItemFullO(layout, "NODE_OT_group_make", "New Group", ntype->ui_icon, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_string_set(&ptr, "node_type", node_type);
uiItemS(layout);
for (ngroup = bmain->nodetree.first, event = 0; ngroup; ngroup = ngroup->id.next, ++event) {
/* only use group trees of the right type */
if (STRNEQ(ngroup->idname, ngroup_type))
continue;
if (!nodeGroupPoll(ntree, ngroup))
continue;
ptr = uiItemFullO(layout, "NODE_OT_add_group_node", ngroup->id.name + 2, ntype->ui_icon, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_string_set(&ptr, "type", ntype->idname);
RNA_string_set(&ptr, "grouptree", ngroup->id.name + 2);
}
NODE_TYPES_END
}
else if (nodeclass == NODE_DYNAMIC) {
/* disabled */
}
else {
PointerRNA ptr;
NODE_TYPES_BEGIN(ntype)
if (ntype->nclass != nodeclass || !ntype->ui_name)
continue;
if (!ntype->poll(ntype, ntree))
continue;
if (compatibility && (ntype->compatibility & compatibility) == 0)
continue;
ptr = uiItemFullO(layout, "NODE_OT_add_node", IFACE_(ntype->ui_name), ntype->ui_icon, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS);
RNA_string_set(&ptr, "type", ntype->idname);
NODE_TYPES_END
}
}
static void node_add_menu_foreach_class_cb(void *calldata, int nclass, const char *name)
{
uiLayout *layout = calldata;
uiItemMenuF(layout, IFACE_(name), 0, node_add_menu_class, SET_INT_IN_POINTER(nclass));
}
static void node_add_menu_default(const bContext *C, uiLayout *layout, bNodeTree *ntree)
{
Scene *scene = CTX_data_scene(C);
if (ntree->typeinfo->foreach_nodeclass)
ntree->typeinfo->foreach_nodeclass(scene, layout, node_add_menu_foreach_class_cb);
}
/* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */
@ -2784,11 +2682,8 @@ void ED_node_init_butfuncs(void)
/* tree type icons */
ntreeType_Composite->ui_icon = ICON_RENDERLAYERS;
ntreeType_Composite->draw_add_menu = node_add_menu_default;
ntreeType_Shader->ui_icon = ICON_MATERIAL;
ntreeType_Shader->draw_add_menu = node_add_menu_default;
ntreeType_Texture->ui_icon = ICON_TEXTURE;
ntreeType_Texture->draw_add_menu = node_add_menu_default;
}
void ED_init_custom_node_type(bNodeType *ntype)

View File

@ -1,88 +0,0 @@
/*
* ***** 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.
*
* The Original Code is Copyright (C) 2008 Blender Foundation.
* All rights reserved.
*
*
* Contributor(s): Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/space_node/node_header.c
* \ingroup spnode
*/
#include <string.h>
#include "DNA_space_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLF_translation.h"
#include "BKE_blender.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "WM_api.h"
#include "WM_types.h"
#include "UI_view2d.h"
#include "node_intern.h" /* own include */
/* ************************ add menu *********************** */
static void node_menu_add(const bContext *C, Menu *menu)
{
SpaceNode *snode = CTX_wm_space_node(C);
uiLayout *layout = menu->layout;
bNodeTree *ntree = snode->edittree;
if (!ntree || !ntree->typeinfo || !ntree->typeinfo->draw_add_menu) {
uiLayoutSetActive(layout, FALSE);
return;
}
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Search ..."), 0, "NODE_OT_add_search");
ntree->typeinfo->draw_add_menu(C, layout, ntree);
}
void node_menus_register(void)
{
MenuType *mt;
mt = MEM_callocN(sizeof(MenuType), "spacetype node menu add");
strcpy(mt->idname, "NODE_MT_add");
strcpy(mt->label, N_("Add"));
strcpy(mt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
mt->draw = node_menu_add;
WM_menutype_add(mt);
}

View File

@ -68,9 +68,6 @@ ARegion *node_has_tools_region(ScrArea *sa);
void snode_group_offset(struct SpaceNode *snode, float *x, float *y); /* transform between View2Ds in the tree path */
/* node_header.c */
void node_menus_register(void);
/* node_draw.c */
int node_get_colorid(struct bNode *node);
void node_socket_circle_draw(const struct bContext *C, struct bNodeTree *ntree, struct bNode *node,

View File

@ -784,8 +784,6 @@ void ED_spacetype_node(void)
BLI_addhead(&st->regiontypes, art);
node_menus_register();
/* regions: listview/buttons */
art = MEM_callocN(sizeof(ARegionType), "spacetype node region");
art->regionid = RGN_TYPE_UI;

View File

@ -553,25 +553,6 @@ static void rna_NodeTree_update_reg(bNodeTree *ntree)
RNA_parameter_list_free(&list);
}
static void rna_NodeTree_draw_add_menu(const bContext *C, struct uiLayout *layout, bNodeTree *ntree)
{
extern FunctionRNA rna_NodeTree_draw_add_menu_func;
PointerRNA ptr;
ParameterList list;
FunctionRNA *func;
RNA_id_pointer_create(&ntree->id, &ptr);
func = &rna_NodeTree_draw_add_menu_func; /* RNA_struct_find_function(&ptr, "draw_add_menu"); */
RNA_parameter_list_create(&list, &ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
RNA_parameter_set_lookup(&list, "layout", &layout);
ntree->typeinfo->ext.call((bContext *)C, &ptr, func, &list);
RNA_parameter_list_free(&list);
}
static void rna_NodeTree_get_from_context(const bContext *C, bNodeTreeType *ntreetype,
bNodeTree **r_ntree, ID **r_id, ID **r_from)
{
@ -622,7 +603,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain, ReportList *reports, void *
bNodeTreeType *nt, dummynt;
bNodeTree dummyntree;
PointerRNA dummyptr;
int have_function[4];
int have_function[3];
/* setup dummy tree & tree type to store static properties in */
memset(&dummynt, 0, sizeof(bNodeTreeType));
@ -662,8 +643,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain, ReportList *reports, void *
nt->poll = (have_function[0]) ? rna_NodeTree_poll : NULL;
nt->update = (have_function[1]) ? rna_NodeTree_update_reg : NULL;
nt->draw_add_menu = (have_function[2]) ? rna_NodeTree_draw_add_menu : NULL;
nt->get_from_context = (have_function[3]) ? rna_NodeTree_get_from_context : NULL;
nt->get_from_context = (have_function[2]) ? rna_NodeTree_get_from_context : NULL;
ntreeTypeAdd(nt);
@ -6856,17 +6836,6 @@ static void rna_def_nodetree(BlenderRNA *brna)
RNA_def_function_ui_description(func, "Update on editor changes");
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
/* draw add menu */
func = RNA_def_function(srna, "draw_add_menu", NULL);
RNA_def_function_ui_description(func, "Draw the menu for adding nodes");
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL);
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(parm, "UILayout");
RNA_def_property_ui_text(parm, "Layout", "Menu layout in the UI");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
/* get a node tree from context */
func = RNA_def_function(srna, "get_from_context", NULL);
RNA_def_function_ui_description(func, "Get a node tree from the context");