Implemented a basic framework for node-based shaders.
[blender-staging.git] / release / scripts / startup / bl_ui / space_node.py
index b54162a0cb4825460823dbecb41ba4171e2e209e..e8914a3e9b023b9fa8ab7edf548c566ad530d37a 100644 (file)
@@ -28,7 +28,6 @@ class NODE_HT_header(Header):
         layout = self.layout
 
         scene = context.scene
-        ob = context.object
         snode = context.space_data
         snode_id = snode.id
         id_from = snode.id_from
@@ -37,11 +36,7 @@ class NODE_HT_header(Header):
         row = layout.row(align=True)
         row.template_header()
 
-        if context.area.show_menus:
-            row.menu("NODE_MT_view")
-            row.menu("NODE_MT_select")
-            row.menu("NODE_MT_add")
-            row.menu("NODE_MT_node")
+        NODE_MT_editor_menus.draw_collapsible(context, layout)
 
         layout.prop(snode, "tree_type", text="", expand=True)
 
@@ -49,21 +44,38 @@ class NODE_HT_header(Header):
             if scene.render.use_shading_nodes:
                 layout.prop(snode, "shader_type", text="", expand=True)
 
+            ob = context.object
             if (not scene.render.use_shading_nodes or snode.shader_type == 'OBJECT') and ob:
+                row = layout.row()
+                # disable material slot buttons when pinned, cannot find correct slot within id_from (#36589)
+                row.enabled = not snode.pin
                 # Show material.new when no active ID/slot exists
                 if not id_from and ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'METABALL'}:
-                    layout.template_ID(ob, "active_material", new="material.new")
+                    row.template_ID(ob, "active_material", new="material.new")
                 # Material ID, but not for Lamps
                 if id_from and ob.type != 'LAMP':
-                    layout.template_ID(id_from, "active_material", new="material.new")
+                    row.template_ID(id_from, "active_material", new="material.new")
+
                 # Don't show "Use Nodes" Button when Engine is BI for Lamps
                 if snode_id and not (scene.render.use_shading_nodes == 0 and ob.type == 'LAMP'):
                     layout.prop(snode_id, "use_nodes")
 
             if snode.shader_type == 'WORLD':
-                layout.template_ID(scene, "world", new="world.new")
+                row = layout.row()
+                row.enabled = not snode.pin
+                row.template_ID(scene, "world", new="world.new")
                 if snode_id:
-                    layout.prop(snode_id, "use_nodes")
+                    row.prop(snode_id, "use_nodes")
+
+            if scene.render.use_shading_nodes and snode.shader_type == 'LINESTYLE':
+                rl = context.scene.render.layers.active
+                lineset = rl.freestyle_settings.linesets.active
+                if lineset is not None:
+                    row = layout.row()
+                    row.enabled = not snode.pin
+                    row.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
+                    if snode_id:
+                        row.prop(snode_id, "use_nodes")
 
         elif snode.tree_type == 'TextureNodeTree':
             layout.prop(snode, "texture_type", text="", expand=True)
@@ -98,8 +110,8 @@ class NODE_HT_header(Header):
         # Snap
         row = layout.row(align=True)
         row.prop(toolsettings, "use_snap", text="")
-        row.prop(toolsettings, "snap_node_element", text="", icon_only=True)
-        if toolsettings.snap_node_element != 'INCREMENT':
+        row.prop(toolsettings, "snap_node_element", icon_only=True)
+        if toolsettings.snap_node_element != 'GRID':
             row.prop(toolsettings, "snap_target", text="")
 
         row = layout.row(align=True)
@@ -109,6 +121,21 @@ class NODE_HT_header(Header):
         layout.template_running_jobs()
 
 
+class NODE_MT_editor_menus(Menu):
+    bl_idname = "NODE_MT_editor_menus"
+    bl_label = ""
+
+    def draw(self, context):
+        self.draw_menus(self.layout, context)
+
+    @staticmethod
+    def draw_menus(layout, context):
+        layout.menu("NODE_MT_view")
+        layout.menu("NODE_MT_select")
+        layout.menu("NODE_MT_add")
+        layout.menu("NODE_MT_node")
+
+
 class NODE_MT_add(bpy.types.Menu):
     bl_space_type = 'NODE_EDITOR'
     bl_label = "Add"
@@ -131,7 +158,7 @@ class NODE_MT_view(Menu):
 
         layout.operator("node.properties", icon='MENU_PANEL')
         layout.operator("node.toolbar", icon='MENU_PANEL')
-        
+
         layout.separator()
 
         layout.operator("view2d.zoom_in")
@@ -147,7 +174,8 @@ class NODE_MT_view(Menu):
 
             layout.operator("node.backimage_move", text="Backdrop move")
             layout.operator("node.backimage_zoom", text="Backdrop zoom in").factor = 1.2
-            layout.operator("node.backimage_zoom", text="Backdrop zoom out").factor = 0.833
+            layout.operator("node.backimage_zoom", text="Backdrop zoom out").factor = 0.83333
+            layout.operator("node.backimage_fit", text="Fit backdrop to available space")
 
         layout.separator()
 
@@ -162,6 +190,7 @@ class NODE_MT_select(Menu):
         layout = self.layout
 
         layout.operator("node.select_border")
+        layout.operator("node.select_circle")
 
         layout.separator()
         layout.operator("node.select_all").action = 'TOGGLE'
@@ -226,13 +255,107 @@ class NODE_MT_node(Menu):
 
         layout.separator()
 
-        layout.operator("node.show_cyclic_dependencies")
         layout.operator("node.read_renderlayers")
         layout.operator("node.read_fullsamplelayers")
 
 
+class NODE_MT_node_color_presets(Menu):
+    """Predefined node color"""
+    bl_label = "Color Presets"
+    preset_subdir = "node_color"
+    preset_operator = "script.execute_preset"
+    draw = Menu.draw_preset
+
+
+class NODE_MT_node_color_specials(Menu):
+    bl_label = "Node Color Specials"
+
+    def draw(self, context):
+        layout = self.layout
+
+        layout.operator("node.node_copy_color", icon='COPY_ID')
+
+
+class NODE_PT_active_node_generic(Panel):
+    bl_space_type = 'NODE_EDITOR'
+    bl_region_type = 'UI'
+    bl_label = "Node"
+#    bl_options = {'HIDE_HEADER'}
+
+    @classmethod
+    def poll(cls, context):
+        return context.active_node is not None
+
+    def draw(self, context):
+        layout = self.layout
+        node = context.active_node
+
+        layout.prop(node, "name", icon='NODE')
+        layout.prop(node, "label", icon='NODE')
+
+
+class NODE_PT_active_node_color(Panel):
+    bl_space_type = 'NODE_EDITOR'
+    bl_region_type = 'UI'
+    bl_label = "Color"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    @classmethod
+    def poll(cls, context):
+        return context.active_node is not None
+
+    def draw_header(self, context):
+        node = context.active_node
+        self.layout.prop(node, "use_custom_color", text="")
+
+    def draw(self, context):
+        layout = self.layout
+        node = context.active_node
+
+        layout.enabled = node.use_custom_color
+
+        row = layout.row()
+        col = row.column()
+        col.menu("NODE_MT_node_color_presets")
+        col.prop(node, "color", text="")
+        col = row.column(align=True)
+        col.operator("node.node_color_preset_add", text="", icon='ZOOMIN').remove_active = False
+        col.operator("node.node_color_preset_add", text="", icon='ZOOMOUT').remove_active = True
+        col.menu("NODE_MT_node_color_specials", text="", icon='DOWNARROW_HLT')
+
+
+class NODE_PT_active_node_properties(Panel):
+    bl_space_type = 'NODE_EDITOR'
+    bl_region_type = 'UI'
+    bl_label = "Properties"
+
+    @classmethod
+    def poll(cls, context):
+        return context.active_node is not None
+
+    def draw(self, context):
+        layout = self.layout
+        node = context.active_node
+        # set "node" context pointer for the panel layout
+        layout.context_pointer_set("node", node)
+
+        if hasattr(node, "draw_buttons_ext"):
+            node.draw_buttons_ext(context, layout)
+        elif hasattr(node, "draw_buttons"):
+            node.draw_buttons(context, layout)
+
+        # XXX this could be filtered further to exclude socket types which don't have meaningful input values (e.g. cycles shader)
+        value_inputs = [socket for socket in node.inputs if socket.enabled and not socket.is_linked]
+        if value_inputs:
+            layout.separator()
+            layout.label("Inputs:")
+            for socket in value_inputs:
+                row = layout.row()
+                socket.draw(context, row, node, socket.name)
+
+
 # Node Backdrop options
-class NODE_PT_properties(Panel):
+class NODE_PT_backdrop(Panel):
     bl_space_type = 'NODE_EDITOR'
     bl_region_type = 'UI'
     bl_label = "Backdrop"
@@ -260,6 +383,8 @@ class NODE_PT_properties(Panel):
         col.prop(snode, "backdrop_y", text="Y")
         col.operator("node.backimage_move", text="Move")
 
+        layout.operator("node.backimage_fit", text="Fit")
+
 
 class NODE_PT_quality(bpy.types.Panel):
     bl_space_type = 'NODE_EDITOR'
@@ -288,24 +413,6 @@ class NODE_PT_quality(bpy.types.Panel):
         col.prop(tree, "use_two_pass")
         col.prop(tree, "use_viewer_border")
         col.prop(snode, "show_highlight")
-        col.prop(snode, "use_hidden_preview")
-
-
-class NODE_MT_node_color_presets(Menu):
-    """Predefined node color"""
-    bl_label = "Color Presets"
-    preset_subdir = "node_color"
-    preset_operator = "script.execute_preset"
-    draw = Menu.draw_preset
-
-
-class NODE_MT_node_color_specials(Menu):
-    bl_label = "Node Color Specials"
-
-    def draw(self, context):
-        layout = self.layout
-
-        layout.operator("node.node_copy_color", icon='COPY_ID')
 
 
 class NODE_UL_interface_sockets(bpy.types.UIList):
@@ -317,13 +424,13 @@ class NODE_UL_interface_sockets(bpy.types.UIList):
             row = layout.row(align=True)
 
             # inputs get icon on the left
-            if socket.in_out == 'IN':
+            if not socket.is_output:
                 row.template_node_socket(color)
 
-            row.label(text=socket.name, icon_value=icon)
+            row.prop(socket, "name", text="", emboss=False, icon_value=icon)
 
             # outputs get icon on the right
-            if socket.in_out == 'OUT':
+            if socket.is_output:
                 row.template_node_socket(color)
 
         elif self.layout_type in {'GRID'}: