space_graph.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  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.
  7. #
  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.
  12. #
  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.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # <pep8 compliant>
  19. from bpy.types import Header, Menu, Panel
  20. from bl_ui.space_dopesheet import (
  21. DopesheetFilterPopoverBase,
  22. dopesheet_filter,
  23. )
  24. class GRAPH_HT_header(Header):
  25. bl_space_type = 'GRAPH_EDITOR'
  26. def draw(self, context):
  27. layout = self.layout
  28. tool_settings = context.tool_settings
  29. st = context.space_data
  30. layout.template_header()
  31. # Now a exposed as a sub-space type
  32. # layout.prop(st, "mode", text="")
  33. GRAPH_MT_editor_menus.draw_collapsible(context, layout)
  34. row = layout.row(align=True)
  35. row.prop(st, "use_normalization", icon='NORMALIZE_FCURVES', text="Normalize", toggle=True)
  36. sub = row.row(align=True)
  37. sub.active = st.use_normalization
  38. sub.prop(st, "use_auto_normalization", icon='FILE_REFRESH', text="", toggle=True)
  39. layout.separator_spacer()
  40. dopesheet_filter(layout, context)
  41. row = layout.row(align=True)
  42. if st.has_ghost_curves:
  43. row.operator("graph.ghost_curves_clear", text="", icon='X')
  44. else:
  45. row.operator("graph.ghost_curves_create", text="", icon='FCURVE_SNAPSHOT')
  46. layout.popover(
  47. panel="GRAPH_PT_filters",
  48. text="",
  49. icon='FILTER',
  50. )
  51. layout.prop(st, "pivot_point", icon_only=True)
  52. layout.prop(st, "auto_snap", text="")
  53. row = layout.row(align=True)
  54. row.prop(tool_settings, "use_proportional_fcurve", text="", icon_only=True)
  55. sub = row.row(align=True)
  56. sub.active = tool_settings.use_proportional_fcurve
  57. sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
  58. class GRAPH_PT_filters(DopesheetFilterPopoverBase, Panel):
  59. bl_space_type = 'GRAPH_EDITOR'
  60. bl_region_type = 'HEADER'
  61. bl_label = "Filters"
  62. def draw(self, context):
  63. layout = self.layout
  64. DopesheetFilterPopoverBase.draw_generic_filters(context, layout)
  65. layout.separator()
  66. DopesheetFilterPopoverBase.draw_search_filters(context, layout)
  67. layout.separator()
  68. DopesheetFilterPopoverBase.draw_standard_filters(context, layout)
  69. class GRAPH_MT_editor_menus(Menu):
  70. bl_idname = "GRAPH_MT_editor_menus"
  71. bl_label = ""
  72. def draw(self, _context):
  73. layout = self.layout
  74. layout.menu("GRAPH_MT_view")
  75. layout.menu("GRAPH_MT_select")
  76. layout.menu("GRAPH_MT_marker")
  77. layout.menu("GRAPH_MT_channel")
  78. layout.menu("GRAPH_MT_key")
  79. class GRAPH_MT_view(Menu):
  80. bl_label = "View"
  81. def draw(self, context):
  82. layout = self.layout
  83. st = context.space_data
  84. layout.prop(st, "show_region_ui")
  85. layout.separator()
  86. layout.prop(st, "use_realtime_update")
  87. layout.prop(st, "show_frame_indicator")
  88. layout.prop(st, "show_cursor")
  89. layout.prop(st, "show_sliders")
  90. layout.prop(st, "show_group_colors")
  91. layout.prop(st, "show_marker_lines")
  92. layout.prop(st, "use_auto_merge_keyframes")
  93. layout.separator()
  94. layout.prop(st, "use_beauty_drawing")
  95. layout.separator()
  96. layout.prop(st, "show_handles")
  97. layout.prop(st, "use_only_selected_curves_handles")
  98. layout.prop(st, "use_only_selected_keyframe_handles")
  99. layout.prop(st, "show_seconds")
  100. layout.prop(st, "show_locked_time")
  101. layout.separator()
  102. layout.operator("anim.previewrange_set")
  103. layout.operator("anim.previewrange_clear")
  104. layout.operator("graph.previewrange_set")
  105. layout.separator()
  106. layout.operator("graph.view_all")
  107. layout.operator("graph.view_selected")
  108. layout.operator("graph.view_frame")
  109. # Add this to show key-binding (reverse action in dope-sheet).
  110. layout.separator()
  111. props = layout.operator("wm.context_set_enum", text="Toggle Dope Sheet")
  112. props.data_path = "area.type"
  113. props.value = 'DOPESHEET_EDITOR'
  114. layout.separator()
  115. layout.menu("INFO_MT_area")
  116. class GRAPH_MT_select(Menu):
  117. bl_label = "Select"
  118. def draw(self, _context):
  119. layout = self.layout
  120. layout.operator("graph.select_all", text="All").action = 'SELECT'
  121. layout.operator("graph.select_all", text="None").action = 'DESELECT'
  122. layout.operator("graph.select_all", text="Invert").action = 'INVERT'
  123. layout.separator()
  124. props = layout.operator("graph.select_box")
  125. props.axis_range = False
  126. props.include_handles = False
  127. props = layout.operator("graph.select_box", text="Border Axis Range")
  128. props.axis_range = True
  129. props.include_handles = False
  130. props = layout.operator("graph.select_box", text="Border (Include Handles)")
  131. props.axis_range = False
  132. props.include_handles = True
  133. layout.operator("graph.select_circle")
  134. layout.separator()
  135. layout.operator("graph.select_column", text="Columns on Selected Keys").mode = 'KEYS'
  136. layout.operator("graph.select_column", text="Column on Current Frame").mode = 'CFRA'
  137. layout.operator("graph.select_column", text="Columns on Selected Markers").mode = 'MARKERS_COLUMN'
  138. layout.operator("graph.select_column", text="Between Selected Markers").mode = 'MARKERS_BETWEEN'
  139. layout.separator()
  140. props = layout.operator("graph.select_leftright", text="Before Current Frame")
  141. props.extend = False
  142. props.mode = 'LEFT'
  143. props = layout.operator("graph.select_leftright", text="After Current Frame")
  144. props.extend = False
  145. props.mode = 'RIGHT'
  146. layout.separator()
  147. layout.operator("graph.select_more")
  148. layout.operator("graph.select_less")
  149. layout.separator()
  150. layout.operator("graph.select_linked")
  151. class GRAPH_MT_marker(Menu):
  152. bl_label = "Marker"
  153. def draw(self, context):
  154. layout = self.layout
  155. from bl_ui.space_time import marker_menu_generic
  156. marker_menu_generic(layout, context)
  157. # TODO: pose markers for action edit mode only?
  158. class GRAPH_MT_channel(Menu):
  159. bl_label = "Channel"
  160. def draw(self, context):
  161. layout = self.layout
  162. layout.operator_context = 'INVOKE_REGION_CHANNELS'
  163. layout.operator("anim.channels_delete")
  164. if context.space_data.mode == 'DRIVERS':
  165. layout.operator("graph.driver_delete_invalid")
  166. layout.separator()
  167. layout.operator("anim.channels_group")
  168. layout.operator("anim.channels_ungroup")
  169. layout.separator()
  170. layout.operator_menu_enum("anim.channels_setting_toggle", "type")
  171. layout.operator_menu_enum("anim.channels_setting_enable", "type")
  172. layout.operator_menu_enum("anim.channels_setting_disable", "type")
  173. layout.separator()
  174. layout.operator("anim.channels_editable_toggle")
  175. layout.operator_menu_enum("graph.extrapolation_type", "type", text="Extrapolation Mode")
  176. layout.separator()
  177. layout.operator("graph.hide", text="Hide Selected Curves").unselected = False
  178. layout.operator("graph.hide", text="Hide Unselected Curves").unselected = True
  179. layout.operator("graph.reveal")
  180. layout.separator()
  181. layout.operator("anim.channels_expand")
  182. layout.operator("anim.channels_collapse")
  183. layout.separator()
  184. layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
  185. layout.separator()
  186. layout.operator("anim.channels_fcurves_enable")
  187. class GRAPH_MT_key(Menu):
  188. bl_label = "Key"
  189. def draw(self, _context):
  190. layout = self.layout
  191. layout.menu("GRAPH_MT_key_transform", text="Transform")
  192. layout.operator_menu_enum("graph.snap", "type", text="Snap")
  193. layout.operator_menu_enum("graph.mirror", "type", text="Mirror")
  194. layout.separator()
  195. layout.operator_menu_enum("graph.keyframe_insert", "type")
  196. layout.operator_menu_enum("graph.fmodifier_add", "type")
  197. layout.operator("graph.sound_bake")
  198. layout.separator()
  199. layout.operator("graph.frame_jump")
  200. layout.separator()
  201. layout.operator("graph.copy")
  202. layout.operator("graph.paste")
  203. layout.operator("graph.paste", text="Paste Flipped").flipped = True
  204. layout.operator("graph.duplicate_move")
  205. layout.operator("graph.delete")
  206. layout.separator()
  207. layout.operator_menu_enum("graph.handle_type", "type", text="Handle Type")
  208. layout.operator_menu_enum("graph.interpolation_type", "type", text="Interpolation Mode")
  209. layout.operator_menu_enum("graph.easing_type", "type", text="Easing Type")
  210. layout.separator()
  211. layout.operator("graph.clean").channels = False
  212. layout.operator("graph.clean", text="Clean Channels").channels = True
  213. layout.operator("graph.smooth")
  214. layout.operator("graph.sample")
  215. layout.operator("graph.bake")
  216. layout.separator()
  217. layout.operator("graph.euler_filter", text="Discontinuity (Euler) Filter")
  218. class GRAPH_MT_key_transform(Menu):
  219. bl_label = "Transform"
  220. def draw(self, _context):
  221. layout = self.layout
  222. layout.operator("transform.translate", text="Move")
  223. layout.operator("transform.transform", text="Extend").mode = 'TIME_EXTEND'
  224. layout.operator("transform.rotate", text="Rotate")
  225. layout.operator("transform.resize", text="Scale")
  226. class GRAPH_MT_delete(Menu):
  227. bl_label = "Delete"
  228. def draw(self, _context):
  229. layout = self.layout
  230. layout.operator("graph.delete")
  231. layout.separator()
  232. layout.operator("graph.clean").channels = False
  233. layout.operator("graph.clean", text="Clean Channels").channels = True
  234. class GRAPH_MT_context_menu(Menu):
  235. bl_label = "F-Curve Context Menu"
  236. def draw(self, _context):
  237. layout = self.layout
  238. layout.operator_context = 'INVOKE_DEFAULT'
  239. layout.operator("graph.copy", text="Copy", icon='COPYDOWN')
  240. layout.operator("graph.paste", text="Paste", icon='PASTEDOWN')
  241. layout.operator("graph.paste", text="Paste Flipped", icon='PASTEFLIPDOWN').flipped = True
  242. layout.separator()
  243. layout.operator_menu_enum("graph.keyframe_type", "type", text="Keyframe Type")
  244. layout.operator_menu_enum("graph.handle_type", "type", text="Handle Type")
  245. layout.operator_menu_enum("graph.interpolation_type", "type", text="Interpolation Mode")
  246. layout.operator_menu_enum("graph.easing_type", "type", text="Easing Type")
  247. layout.separator()
  248. layout.operator("graph.keyframe_insert").type = 'SEL'
  249. layout.operator("graph.duplicate_move")
  250. layout.operator_context = 'EXEC_REGION_WIN'
  251. layout.operator("graph.delete")
  252. layout.separator()
  253. layout.operator_menu_enum("graph.mirror", "type", text="Mirror")
  254. layout.operator_menu_enum("graph.snap", "type", text="Snap")
  255. class GRAPH_MT_pivot_pie(Menu):
  256. bl_label = "Pivot Point"
  257. def draw(self, context):
  258. layout = self.layout
  259. pie = layout.menu_pie()
  260. pie.prop_enum(context.space_data, "pivot_point", value='BOUNDING_BOX_CENTER')
  261. pie.prop_enum(context.space_data, "pivot_point", value='CURSOR')
  262. pie.prop_enum(context.space_data, "pivot_point", value='INDIVIDUAL_ORIGINS')
  263. class GRAPH_MT_snap_pie(Menu):
  264. bl_label = "Snap"
  265. def draw(self, _context):
  266. layout = self.layout
  267. pie = layout.menu_pie()
  268. pie.operator("graph.snap", text="Current Frame").type = 'CFRA'
  269. pie.operator("graph.snap", text="Cursor Value").type = 'VALUE'
  270. pie.operator("graph.snap", text="Nearest Frame").type = 'NEAREST_FRAME'
  271. pie.operator("graph.snap", text="Nearest Second").type = 'NEAREST_SECOND'
  272. pie.operator("graph.snap", text="Nearest Marker").type = 'NEAREST_MARKER'
  273. pie.operator("graph.snap", text="Flatten Handles").type = 'HORIZONTAL'
  274. class GRAPH_MT_channel_context_menu(Menu):
  275. bl_label = "F-Curve Channel Context Menu"
  276. def draw(self, context):
  277. layout = self.layout
  278. st = context.space_data
  279. layout.separator()
  280. layout.operator("anim.channels_setting_enable", text="Mute Channels").type = 'MUTE'
  281. layout.operator("anim.channels_setting_disable", text="Unmute Channels").type = 'MUTE'
  282. layout.separator()
  283. layout.operator("anim.channels_setting_enable", text="Protect Channels").type = 'PROTECT'
  284. layout.operator("anim.channels_setting_disable", text="Unprotect Channels").type = 'PROTECT'
  285. layout.separator()
  286. layout.operator("anim.channels_group")
  287. layout.operator("anim.channels_ungroup")
  288. layout.separator()
  289. layout.operator("anim.channels_editable_toggle")
  290. layout.operator_menu_enum("graph.extrapolation_type", "type", text="Extrapolation Mode")
  291. layout.separator()
  292. layout.operator("graph.hide", text="Hide Selected Curves").unselected = False
  293. layout.operator("graph.hide", text="Hide Unselected Curves").unselected = True
  294. layout.operator("graph.reveal")
  295. layout.separator()
  296. layout.operator("anim.channels_expand")
  297. layout.operator("anim.channels_collapse")
  298. layout.separator()
  299. layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
  300. layout.separator()
  301. layout.operator("anim.channels_delete")
  302. if st.mode == 'DRIVERS':
  303. layout.operator("graph.driver_delete_invalid")
  304. classes = (
  305. GRAPH_HT_header,
  306. GRAPH_MT_editor_menus,
  307. GRAPH_MT_view,
  308. GRAPH_MT_select,
  309. GRAPH_MT_marker,
  310. GRAPH_MT_channel,
  311. GRAPH_MT_key,
  312. GRAPH_MT_key_transform,
  313. GRAPH_MT_delete,
  314. GRAPH_MT_context_menu,
  315. GRAPH_MT_channel_context_menu,
  316. GRAPH_MT_pivot_pie,
  317. GRAPH_MT_snap_pie,
  318. GRAPH_PT_filters,
  319. )
  320. if __name__ == "__main__": # only for live edit.
  321. from bpy.utils import register_class
  322. for cls in classes:
  323. register_class(cls)