1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 import nodeitems_utils
22 from bpy.types import Header, Menu, Panel
23 from bpy.app.translations import pgettext_iface as iface_
24 from bl_ui.properties_grease_pencil_common import (
25 GreasePencilDrawingToolsPanel,
26 GreasePencilStrokeEditPanel,
27 GreasePencilStrokeSculptPanel,
28 GreasePencilDataPanel,
29 GreasePencilToolsPanel,
33 class NODE_HT_header(Header):
34 bl_space_type = 'NODE_EDITOR'
36 def draw(self, context):
40 snode = context.space_data
42 id_from = snode.id_from
43 toolsettings = context.tool_settings
45 row = layout.row(align=True)
48 NODE_MT_editor_menus.draw_collapsible(context, layout)
50 layout.prop(snode, "tree_type", text="", expand=True)
52 if snode.tree_type == 'ShaderNodeTree':
53 if scene.render.use_shading_nodes:
54 layout.prop(snode, "shader_type", text="", expand=True)
57 if (not scene.render.use_shading_nodes or snode.shader_type == 'OBJECT') and ob:
59 # disable material slot buttons when pinned, cannot find correct slot within id_from (#36589)
60 row.enabled = not snode.pin
61 # Show material.new when no active ID/slot exists
62 if not id_from and ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'METABALL'}:
63 row.template_ID(ob, "active_material", new="material.new")
64 # Material ID, but not for Lamps
65 if id_from and ob.type != 'LAMP':
66 row.template_ID(id_from, "active_material", new="material.new")
68 # Don't show "Use Nodes" Button when Engine is BI for Lamps
69 if snode_id and not (scene.render.use_shading_nodes == 0 and ob.type == 'LAMP'):
70 layout.prop(snode_id, "use_nodes")
72 if scene.render.use_shading_nodes and snode.shader_type == 'WORLD':
74 row.enabled = not snode.pin
75 row.template_ID(scene, "world", new="world.new")
77 row.prop(snode_id, "use_nodes")
79 if scene.render.use_shading_nodes and snode.shader_type == 'LINESTYLE':
80 rl = context.scene.render.layers.active
81 lineset = rl.freestyle_settings.linesets.active
82 if lineset is not None:
84 row.enabled = not snode.pin
85 row.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
87 row.prop(snode_id, "use_nodes")
89 elif snode.tree_type == 'TextureNodeTree':
90 layout.prop(snode, "texture_type", text="", expand=True)
93 if snode.texture_type == 'BRUSH':
94 layout.template_ID(id_from, "texture", new="texture.new")
96 layout.template_ID(id_from, "active_texture", new="texture.new")
98 layout.prop(snode_id, "use_nodes")
100 elif snode.tree_type == 'CompositorNodeTree':
102 layout.prop(snode_id, "use_nodes")
103 layout.prop(snode_id.render, "use_free_unused_nodes", text="Free Unused")
104 layout.prop(snode, "show_backdrop")
105 if snode.show_backdrop:
106 row = layout.row(align=True)
107 row.prop(snode, "backdrop_channels", text="", expand=True)
108 layout.prop(snode, "use_auto_render")
111 # Custom node tree is edited as independent ID block
112 layout.template_ID(snode, "node_tree", new="node.new_node_tree")
114 layout.prop(snode, "pin", text="")
115 layout.operator("node.tree_path_parent", text="", icon='FILE_PARENT')
119 # Auto-offset nodes (called "insert_offset" in code)
120 layout.prop(snode, "use_insert_offset", text="")
123 row = layout.row(align=True)
124 row.prop(toolsettings, "use_snap", text="")
125 row.prop(toolsettings, "snap_node_element", icon_only=True)
126 if toolsettings.snap_node_element != 'GRID':
127 row.prop(toolsettings, "snap_target", text="")
129 row = layout.row(align=True)
130 row.operator("node.clipboard_copy", text="", icon='COPYDOWN')
131 row.operator("node.clipboard_paste", text="", icon='PASTEDOWN')
133 layout.template_running_jobs()
136 class NODE_MT_editor_menus(Menu):
137 bl_idname = "NODE_MT_editor_menus"
140 def draw(self, context):
141 self.draw_menus(self.layout, context)
144 def draw_menus(layout, context):
145 layout.menu("NODE_MT_view")
146 layout.menu("NODE_MT_select")
147 layout.menu("NODE_MT_add")
148 layout.menu("NODE_MT_node")
151 class NODE_MT_add(bpy.types.Menu):
152 bl_space_type = 'NODE_EDITOR'
155 def draw(self, context):
158 layout.operator_context = 'INVOKE_DEFAULT'
159 props = layout.operator("node.add_search", text="Search ...")
160 props.use_transform = True
162 # actual node submenus are defined by draw functions from node categories
163 nodeitems_utils.draw_node_categories_menu(self, context)
166 class NODE_MT_view(Menu):
169 def draw(self, context):
172 layout.operator("node.properties", icon='MENU_PANEL')
173 layout.operator("node.toolbar", icon='MENU_PANEL')
177 layout.operator("view2d.zoom_in")
178 layout.operator("view2d.zoom_out")
182 layout.operator("node.view_selected")
183 layout.operator("node.view_all")
185 if context.space_data.show_backdrop:
188 layout.operator("node.backimage_move", text="Backdrop move")
189 layout.operator("node.backimage_zoom", text="Backdrop zoom in").factor = 1.2
190 layout.operator("node.backimage_zoom", text="Backdrop zoom out").factor = 0.83333
191 layout.operator("node.backimage_fit", text="Fit backdrop to available space")
195 layout.operator("screen.area_dupli")
196 layout.operator("screen.screen_full_area", text="Toggle Maximize Area")
197 layout.operator("screen.screen_full_area").use_hide_panels = True
200 class NODE_MT_select(Menu):
203 def draw(self, context):
206 layout.operator("node.select_border").tweak = False
207 layout.operator("node.select_circle")
210 layout.operator("node.select_all").action = 'TOGGLE'
211 layout.operator("node.select_all", text="Inverse").action = 'INVERT'
212 layout.operator("node.select_linked_from")
213 layout.operator("node.select_linked_to")
217 layout.operator("node.select_grouped").extend = False
218 layout.operator("node.select_same_type_step", text="Activate Same Type Previous").prev = True
219 layout.operator("node.select_same_type_step", text="Activate Same Type Next").prev = False
223 layout.operator("node.find_node")
226 class NODE_MT_node(Menu):
229 def draw(self, context):
232 layout.operator("transform.translate")
233 layout.operator("transform.rotate")
234 layout.operator("transform.resize")
238 layout.operator("node.duplicate_move")
239 layout.operator("node.delete")
240 layout.operator("node.delete_reconnect")
244 layout.operator("node.join", text="Join in new Frame")
245 layout.operator("node.detach", text="Remove from Frame")
249 layout.operator("node.link_make").replace = False
250 layout.operator("node.link_make", text="Make and Replace Links").replace = True
251 layout.operator("node.links_cut")
252 layout.operator("node.links_detach")
256 layout.operator("node.group_edit").exit = False
257 layout.operator("node.group_ungroup")
258 layout.operator("node.group_make")
259 layout.operator("node.group_insert")
263 layout.operator("node.hide_toggle")
264 layout.operator("node.mute_toggle")
265 layout.operator("node.preview_toggle")
266 layout.operator("node.hide_socket_toggle")
267 layout.operator("node.options_toggle")
268 layout.operator("node.collapse_hide_unused_toggle")
272 layout.operator("node.read_renderlayers")
273 layout.operator("node.read_fullsamplelayers")
276 class NODE_MT_node_color_presets(Menu):
277 """Predefined node color"""
278 bl_label = "Color Presets"
279 preset_subdir = "node_color"
280 preset_operator = "script.execute_preset"
281 draw = Menu.draw_preset
284 class NODE_MT_node_color_specials(Menu):
285 bl_label = "Node Color Specials"
287 def draw(self, context):
290 layout.operator("node.node_copy_color", icon='COPY_ID')
293 class NODE_PT_active_node_generic(Panel):
294 bl_space_type = 'NODE_EDITOR'
295 bl_region_type = 'UI'
297 # bl_options = {'HIDE_HEADER'}
300 def poll(cls, context):
301 return context.active_node is not None
303 def draw(self, context):
305 node = context.active_node
307 layout.prop(node, "name", icon='NODE')
308 layout.prop(node, "label", icon='NODE')
311 class NODE_PT_active_node_color(Panel):
312 bl_space_type = 'NODE_EDITOR'
313 bl_region_type = 'UI'
315 bl_options = {'DEFAULT_CLOSED'}
318 def poll(cls, context):
319 return context.active_node is not None
321 def draw_header(self, context):
322 node = context.active_node
323 self.layout.prop(node, "use_custom_color", text="")
325 def draw(self, context):
327 node = context.active_node
329 layout.enabled = node.use_custom_color
333 col.menu("NODE_MT_node_color_presets")
334 col.prop(node, "color", text="")
335 col = row.column(align=True)
336 col.operator("node.node_color_preset_add", text="", icon='ZOOMIN').remove_active = False
337 col.operator("node.node_color_preset_add", text="", icon='ZOOMOUT').remove_active = True
338 col.menu("NODE_MT_node_color_specials", text="", icon='DOWNARROW_HLT')
341 class NODE_PT_active_node_properties(Panel):
342 bl_space_type = 'NODE_EDITOR'
343 bl_region_type = 'UI'
344 bl_label = "Properties"
347 def poll(cls, context):
348 return context.active_node is not None
350 def draw(self, context):
352 node = context.active_node
353 # set "node" context pointer for the panel layout
354 layout.context_pointer_set("node", node)
356 if hasattr(node, "draw_buttons_ext"):
357 node.draw_buttons_ext(context, layout)
358 elif hasattr(node, "draw_buttons"):
359 node.draw_buttons(context, layout)
361 # XXX this could be filtered further to exclude socket types which don't have meaningful input values (e.g. cycles shader)
362 value_inputs = [socket for socket in node.inputs if socket.enabled and not socket.is_linked]
365 layout.label("Inputs:")
366 for socket in value_inputs:
368 socket.draw(context, row, node, iface_(socket.name, socket.bl_rna.translation_context))
371 # Node Backdrop options
372 class NODE_PT_backdrop(Panel):
373 bl_space_type = 'NODE_EDITOR'
374 bl_region_type = 'UI'
375 bl_label = "Backdrop"
378 def poll(cls, context):
379 snode = context.space_data
380 return snode.tree_type == 'CompositorNodeTree'
382 def draw_header(self, context):
383 snode = context.space_data
384 self.layout.prop(snode, "show_backdrop", text="")
386 def draw(self, context):
389 snode = context.space_data
390 layout.active = snode.show_backdrop
391 layout.prop(snode, "backdrop_channels", text="")
392 layout.prop(snode, "backdrop_zoom", text="Zoom")
394 col = layout.column(align=True)
395 col.label(text="Offset:")
396 col.prop(snode, "backdrop_x", text="X")
397 col.prop(snode, "backdrop_y", text="Y")
398 col.operator("node.backimage_move", text="Move")
400 layout.operator("node.backimage_fit", text="Fit")
403 class NODE_PT_quality(bpy.types.Panel):
404 bl_space_type = 'NODE_EDITOR'
405 bl_region_type = 'UI'
406 bl_label = "Performance"
409 def poll(cls, context):
410 snode = context.space_data
411 return snode.tree_type == 'CompositorNodeTree' and snode.node_tree is not None
413 def draw(self, context):
416 snode = context.space_data
417 tree = snode.node_tree
419 col = layout.column()
420 col.prop(tree, "render_quality", text="Render")
421 col.prop(tree, "edit_quality", text="Edit")
422 col.prop(tree, "chunk_size")
424 col = layout.column()
425 col.prop(tree, "use_opencl")
426 col.prop(tree, "use_groupnode_buffer")
427 col.prop(tree, "use_two_pass")
428 col.prop(tree, "use_viewer_border")
429 col.prop(snode, "show_highlight")
432 class NODE_UL_interface_sockets(bpy.types.UIList):
433 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
435 color = socket.draw_color(context)
437 if self.layout_type in {'DEFAULT', 'COMPACT'}:
438 row = layout.row(align=True)
440 # inputs get icon on the left
441 if not socket.is_output:
442 row.template_node_socket(color)
444 row.prop(socket, "name", text="", emboss=False, icon_value=icon)
446 # outputs get icon on the right
448 row.template_node_socket(color)
450 elif self.layout_type == 'GRID':
451 layout.alignment = 'CENTER'
452 layout.template_node_socket(color)
455 # Grease Pencil properties
456 class NODE_PT_grease_pencil(GreasePencilDataPanel, Panel):
457 bl_space_type = 'NODE_EDITOR'
458 bl_region_type = 'UI'
460 # NOTE: this is just a wrapper around the generic GP Panel
463 def poll(cls, context):
464 snode = context.space_data
465 return snode is not None and snode.node_tree is not None
468 class NODE_PT_grease_pencil_tools(GreasePencilToolsPanel, Panel):
469 bl_space_type = 'NODE_EDITOR'
470 bl_region_type = 'UI'
471 bl_options = {'DEFAULT_CLOSED'}
473 # NOTE: this is just a wrapper around the generic GP tools panel
474 # It contains access to some essential tools usually found only in
475 # toolbar, but which may not necessarily be open
478 # Tool Shelf ------------------
481 # Grease Pencil drawing tools
482 class NODE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
483 bl_space_type = 'NODE_EDITOR'
484 bl_region_type = 'TOOLS'
487 # Grease Pencil stroke editing tools
488 class NODE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
489 bl_space_type = 'NODE_EDITOR'
490 bl_region_type = 'TOOLS'
493 # Grease Pencil stroke sculpting tools
494 class NODE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
495 bl_space_type = 'NODE_EDITOR'
496 bl_region_type = 'TOOLS'
498 # -----------------------------
501 def node_draw_tree_view(layout, context):
505 if __name__ == "__main__": # only for live edit.
506 bpy.utils.register_module(__name__)