properties_grease_pencil_common.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  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. import bpy
  20. from bpy.types import Menu, UIList
  21. from bpy.app.translations import pgettext_iface as iface_
  22. def gpencil_stroke_placement_settings(context, layout):
  23. if context.space_data.type == 'VIEW_3D':
  24. propname = "annotation_stroke_placement_view3d"
  25. elif context.space_data.type == 'SEQUENCE_EDITOR':
  26. propname = "annotation_stroke_placement_sequencer_preview"
  27. elif context.space_data.type == 'IMAGE_EDITOR':
  28. propname = "annotation_stroke_placement_image_editor"
  29. else:
  30. propname = "annotation_stroke_placement_view2d"
  31. tool_settings = context.tool_settings
  32. col = layout.column(align=True)
  33. if context.space_data.type != 'VIEW_3D':
  34. col.label(text="Stroke Placement:")
  35. row = col.row(align=True)
  36. row.prop_enum(tool_settings, propname, 'VIEW')
  37. row.prop_enum(tool_settings, propname, 'CURSOR', text="Cursor")
  38. def gpencil_active_brush_settings_simple(context, layout):
  39. tool_settings = context.tool_settings
  40. brush = tool_settings.gpencil_paint.brush
  41. if brush is None:
  42. layout.label(text="No Active Brush")
  43. return
  44. col = layout.column()
  45. col.label(text="Active Brush: ")
  46. row = col.row(align=True)
  47. row.operator_context = 'EXEC_REGION_WIN'
  48. row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA')
  49. row.prop(brush, "name", text="")
  50. col.prop(brush, "size", slider=True)
  51. row = col.row(align=True)
  52. row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE')
  53. row.prop(brush, "pen_sensitivity_factor", slider=True)
  54. row.prop(brush, "use_pressure", text="", icon='STYLUS_PRESSURE')
  55. row = col.row(align=True)
  56. row.prop(brush, "use_random_strength", text="", icon='RNDCURVE')
  57. row.prop(brush, "strength", slider=True)
  58. row.prop(brush, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
  59. row = col.row(align=True)
  60. row.prop(brush, "jitter", slider=True)
  61. row.prop(brush, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE')
  62. row = col.row()
  63. row.prop(brush, "angle", slider=True)
  64. row.prop(brush, "angle_factor", text="Factor", slider=True)
  65. # XXX: To be replaced with active tools
  66. class AnnotationDrawingToolsPanel:
  67. # subclass must set
  68. # bl_space_type = 'IMAGE_EDITOR'
  69. bl_label = "Annotation"
  70. bl_category = "Annotation"
  71. bl_region_type = 'TOOLS'
  72. def draw(self, context):
  73. layout = self.layout
  74. is_3d_view = context.space_data.type == 'VIEW_3D'
  75. is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
  76. col = layout.column(align=True)
  77. col.label(text="Draw:")
  78. row = col.row(align=True)
  79. row.operator("gpencil.annotate", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
  80. # XXX: Needs a dedicated icon
  81. row.operator("gpencil.annotate", icon='FORCE_CURVE', text="Erase").mode = 'ERASER'
  82. row = col.row(align=True)
  83. row.operator("gpencil.annotate", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
  84. row.operator("gpencil.annotate", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
  85. col.separator()
  86. sub = col.column(align=True)
  87. sub.operator("gpencil.blank_frame_add", icon='FILE_NEW')
  88. sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)")
  89. #sub = col.column(align=True)
  90. #sub.prop(context.tool_settings, "use_gpencil_draw_additive", text="Additive Drawing")
  91. #sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
  92. #sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back")
  93. col.separator()
  94. col.separator()
  95. if context.space_data.type == 'CLIP_EDITOR':
  96. col.separator()
  97. col.label(text="Data Source:")
  98. row = col.row(align=True)
  99. if is_3d_view:
  100. row.prop(context.tool_settings, "grease_pencil_source", expand=True)
  101. elif is_clip_editor:
  102. row.prop(context.space_data, "grease_pencil_source", expand=True)
  103. # col.separator()
  104. # col.separator()
  105. gpencil_stroke_placement_settings(context, col)
  106. class GreasePencilStrokeEditPanel:
  107. # subclass must set
  108. # bl_space_type = 'IMAGE_EDITOR'
  109. bl_label = "Edit Strokes"
  110. bl_category = "Tools"
  111. bl_region_type = 'TOOLS'
  112. @classmethod
  113. def poll(cls, context):
  114. if context.gpencil_data is None:
  115. return False
  116. gpd = context.gpencil_data
  117. return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
  118. def draw(self, context):
  119. layout = self.layout
  120. is_3d_view = context.space_data.type == 'VIEW_3D'
  121. if not is_3d_view:
  122. layout.label(text="Select:")
  123. col = layout.column(align=True)
  124. col.operator("gpencil.select_all", text="Select All")
  125. col.operator("gpencil.select_box")
  126. col.operator("gpencil.select_circle")
  127. layout.separator()
  128. col = layout.column(align=True)
  129. col.operator("gpencil.select_linked")
  130. col.operator("gpencil.select_more")
  131. col.operator("gpencil.select_less")
  132. col.operator("gpencil.select_alternate")
  133. layout.label(text="Edit:")
  134. row = layout.row(align=True)
  135. row.operator("gpencil.copy", text="Copy")
  136. row.operator("gpencil.paste", text="Paste").type = 'COPY'
  137. row.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE'
  138. col = layout.column(align=True)
  139. col.operator("gpencil.delete")
  140. col.operator("gpencil.duplicate_move", text="Duplicate")
  141. if is_3d_view:
  142. col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
  143. col.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps...", property="type")
  144. layout.separator()
  145. if not is_3d_view:
  146. col = layout.column(align=True)
  147. col.operator("transform.translate") # icon='MAN_TRANS'
  148. col.operator("transform.rotate") # icon='MAN_ROT'
  149. col.operator("transform.resize", text="Scale") # icon='MAN_SCALE'
  150. layout.separator()
  151. layout.separator()
  152. col = layout.column(align=True)
  153. col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction")
  154. col.operator("gpencil.stroke_change_color", text="Assign Material")
  155. layout.separator()
  156. col = layout.column(align=True)
  157. col.operator("gpencil.stroke_subdivide", text="Subdivide")
  158. row = col.row(align=True)
  159. row.operator("gpencil.stroke_simplify_fixed", text="Simplify")
  160. row.operator("gpencil.stroke_simplify", text="Adaptive")
  161. row.operator("gpencil.stroke_trim", text="Trim")
  162. col.separator()
  163. row = col.row(align=True)
  164. row.operator("gpencil.stroke_merge", text="Merge")
  165. row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
  166. row.operator("gpencil.stroke_join", text="& Copy").type = 'JOINCOPY'
  167. col.operator("gpencil.stroke_flip", text="Flip Direction")
  168. if is_3d_view:
  169. layout.separator()
  170. col = layout.column(align=True)
  171. col.operator_menu_enum("gpencil.stroke_separate", text="Separate...", property="mode")
  172. col.operator("gpencil.stroke_split", text="Split")
  173. col = layout.column(align=True)
  174. col.label(text="Cleanup:")
  175. col.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type")
  176. col.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode")
  177. class GreasePencilStrokeSculptPanel:
  178. # subclass must set
  179. # bl_space_type = 'IMAGE_EDITOR'
  180. bl_label = "Sculpt Strokes"
  181. bl_category = "Tools"
  182. def draw(self, context):
  183. layout = self.layout
  184. layout.use_property_split = True
  185. layout.use_property_decorate = False
  186. settings = context.tool_settings.gpencil_sculpt
  187. brush = settings.brush
  188. layout.template_icon_view(settings, "sculpt_tool", show_labels=True)
  189. if not self.is_popover:
  190. from bl_ui.properties_paint_common import (
  191. brush_basic_gpencil_sculpt_settings,
  192. )
  193. brush_basic_gpencil_sculpt_settings(layout, context, brush)
  194. class GreasePencilSculptOptionsPanel:
  195. bl_label = "Sculpt Strokes"
  196. @classmethod
  197. def poll(cls, context):
  198. settings = context.tool_settings.gpencil_sculpt
  199. tool = settings.sculpt_tool
  200. return bool(tool in {'SMOOTH', 'RANDOMIZE', 'SMOOTH'})
  201. def draw(self, context):
  202. layout = self.layout
  203. layout.use_property_split = True
  204. layout.use_property_decorate = False
  205. settings = context.tool_settings.gpencil_sculpt
  206. tool = settings.sculpt_tool
  207. brush = settings.brush
  208. if tool in {'SMOOTH', 'RANDOMIZE'}:
  209. layout.prop(settings, "use_edit_position", text="Affect Position")
  210. layout.prop(settings, "use_edit_strength", text="Affect Strength")
  211. layout.prop(settings, "use_edit_thickness", text="Affect Thickness")
  212. if tool == 'SMOOTH':
  213. layout.prop(brush, "use_edit_pressure")
  214. layout.prop(settings, "use_edit_uv", text="Affect UV")
  215. # GP Object Tool Settings
  216. class GreasePencilAppearancePanel:
  217. bl_label = "Brush Appearance"
  218. bl_options = {'DEFAULT_CLOSED'}
  219. @classmethod
  220. def poll(cls, context):
  221. ob = context.active_object
  222. return ob and ob.type == 'GPENCIL'
  223. def draw(self, context):
  224. layout = self.layout
  225. layout.use_property_split = True
  226. layout.use_property_decorate = False
  227. tool_settings = context.tool_settings
  228. ob = context.active_object
  229. if ob.mode == 'PAINT_GPENCIL':
  230. brush = tool_settings.gpencil_paint.brush
  231. gp_settings = brush.gpencil_settings
  232. sub = layout.column(align=True)
  233. sub.enabled = not brush.use_custom_icon
  234. sub.prop(gp_settings, "gp_icon", text="Icon")
  235. layout.prop(brush, "use_custom_icon")
  236. sub = layout.column()
  237. sub.active = brush.use_custom_icon
  238. sub.prop(brush, "icon_filepath", text="")
  239. layout.prop(gp_settings, "use_cursor", text="Show Brush")
  240. if brush.gpencil_tool == 'DRAW':
  241. layout.prop(gp_settings, "show_lasso", text="Show fill color while drawing")
  242. if brush.gpencil_tool == 'FILL':
  243. layout.prop(brush, "cursor_color_add", text="Color")
  244. elif ob.mode in {'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
  245. settings = tool_settings.gpencil_sculpt
  246. brush = settings.brush
  247. tool = settings.sculpt_tool
  248. col = layout.column(align=True)
  249. col.prop(brush, "use_cursor", text="Show Brush")
  250. if tool in {'THICKNESS', 'STRENGTH'}:
  251. col.prop(brush, "cursor_color_add", text="Add")
  252. col.prop(brush, "cursor_color_sub", text="Subtract")
  253. elif tool == 'PINCH':
  254. col.prop(brush, "cursor_color_add", text="Pinch")
  255. col.prop(brush, "cursor_color_sub", text="Inflate")
  256. elif tool == 'TWIST':
  257. col.prop(brush, "cursor_color_add", text="CCW")
  258. col.prop(brush, "cursor_color_sub", text="CW")
  259. else:
  260. col.prop(brush, "cursor_color_add", text="")
  261. class GPENCIL_MT_pie_tool_palette(Menu):
  262. """A pie menu for quick access to Grease Pencil tools"""
  263. bl_label = "Grease Pencil Tools"
  264. def draw(self, context):
  265. layout = self.layout
  266. pie = layout.menu_pie()
  267. gpd = context.gpencil_data
  268. # W - Drawing Types
  269. col = pie.column()
  270. col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
  271. col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
  272. col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
  273. # E - Eraser
  274. # XXX: needs a dedicated icon...
  275. col = pie.column()
  276. col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
  277. # E - "Settings" Palette is included here too, since it needs to be in a stable position...
  278. if gpd and gpd.layers.active:
  279. col.separator()
  280. col.operator(
  281. "wm.call_menu_pie",
  282. text="Settings...",
  283. icon='SCRIPTWIN').name = "GPENCIL_MT_pie_settings_palette"
  284. # Editing tools
  285. if gpd:
  286. if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
  287. # S - Exit Edit Mode
  288. pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT')
  289. # N - Transforms
  290. col = pie.column()
  291. row = col.row(align=True)
  292. row.operator("transform.translate", icon='MAN_TRANS')
  293. row.operator("transform.rotate", icon='MAN_ROT')
  294. row.operator("transform.resize", text="Scale", icon='MAN_SCALE')
  295. row = col.row(align=True)
  296. row.label(text="Proportional Edit:")
  297. row.prop(context.tool_settings, "use_proportional_edit", text="", icon_only=True)
  298. row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
  299. # NW - Select (Non-Modal)
  300. col = pie.column()
  301. col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
  302. col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
  303. col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
  304. col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR')
  305. # NE - Select (Modal)
  306. col = pie.column()
  307. col.operator("gpencil.select_box", text="Box Select", icon='BORDER_RECT')
  308. col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
  309. col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
  310. col.operator("gpencil.select_alternate", text="Alternate Select", icon='BORDER_LASSO')
  311. # SW - Edit Tools
  312. col = pie.column()
  313. col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
  314. col.operator("gpencil.delete", icon='X', text="Delete...")
  315. # SE - More Tools
  316. pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_MT_pie_tools_more"
  317. else:
  318. # Toggle Edit Mode
  319. pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT')
  320. class GPENCIL_MT_pie_settings_palette(Menu):
  321. """A pie menu for quick access to Grease Pencil settings"""
  322. bl_label = "Grease Pencil Settings"
  323. @classmethod
  324. def poll(cls, context):
  325. return bool(context.gpencil_data and context.active_gpencil_layer)
  326. def draw(self, context):
  327. layout = self.layout
  328. pie = layout.menu_pie()
  329. gpd = context.gpencil_data
  330. gpl = context.active_gpencil_layer
  331. palcolor = None # context.active_gpencil_palettecolor
  332. is_editmode = bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
  333. # W - Stroke draw settings
  334. col = pie.column(align=True)
  335. if palcolor is not None:
  336. col.enabled = not palcolor.lock
  337. col.label(text="Stroke")
  338. col.prop(palcolor, "color", text="")
  339. col.prop(palcolor, "alpha", text="", slider=True)
  340. # E - Fill draw settings
  341. col = pie.column(align=True)
  342. if palcolor is not None:
  343. col.enabled = not palcolor.lock
  344. col.label(text="Fill")
  345. col.prop(palcolor, "fill_color", text="")
  346. col.prop(palcolor, "fill_alpha", text="", slider=True)
  347. # S Brush settings
  348. gpencil_active_brush_settings_simple(context, pie)
  349. # N - Active Layer
  350. col = pie.column()
  351. col.label(text="Active Layer: ")
  352. row = col.row()
  353. row.operator_context = 'EXEC_REGION_WIN'
  354. row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL')
  355. row.prop(gpl, "info", text="")
  356. row.operator("gpencil.layer_remove", text="", icon='X')
  357. row = col.row()
  358. row.prop(gpl, "lock")
  359. row.prop(gpl, "hide")
  360. col.prop(gpl, "use_onion_skinning")
  361. # NW/NE/SW/SE - These operators are only available in editmode
  362. # as they require strokes to be selected to work
  363. if is_editmode:
  364. # NW - Move stroke Down
  365. col = pie.column(align=True)
  366. col.label(text="Arrange Strokes")
  367. col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM'
  368. col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN'
  369. # NE - Move stroke Up
  370. col = pie.column(align=True)
  371. col.label(text="Arrange Strokes")
  372. col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP'
  373. col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP'
  374. # SW - Move stroke to color
  375. col = pie.column(align=True)
  376. col.operator("gpencil.stroke_change_color", text="Move to Color")
  377. # SE - Join strokes
  378. col = pie.column(align=True)
  379. col.label(text="Join Strokes")
  380. row = col.row()
  381. row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
  382. row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
  383. col.operator("gpencil.stroke_flip", text="Flip Direction")
  384. col.prop(gpd, "show_stroke_direction", text="Show Drawing Direction")
  385. class GPENCIL_MT_pie_tools_more(Menu):
  386. """A pie menu for accessing more Grease Pencil tools"""
  387. bl_label = "More Grease Pencil Tools"
  388. @classmethod
  389. def poll(cls, context):
  390. gpd = context.gpencil_data
  391. return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
  392. def draw(self, _context):
  393. layout = self.layout
  394. pie = layout.menu_pie()
  395. # gpd = context.gpencil_data
  396. col = pie.column(align=True)
  397. col.operator("gpencil.copy", icon='COPYDOWN', text="Copy")
  398. col.operator("gpencil.paste", icon='PASTEDOWN', text="Paste")
  399. col = pie.column(align=True)
  400. col.operator("gpencil.select_more", icon='ADD')
  401. col.operator("gpencil.select_less", icon='REMOVE')
  402. pie.operator("transform.mirror", icon='MOD_MIRROR')
  403. pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM')
  404. pie.operator("transform.shear", icon='MOD_TRIANGULATE')
  405. pie.operator("transform.tosphere", icon='MOD_MULTIRES')
  406. pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
  407. pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_MT_pie_tool_palette"
  408. class GPENCIL_MT_pie_sculpt(Menu):
  409. """A pie menu for accessing Grease Pencil stroke sculpt settings"""
  410. bl_label = "Grease Pencil Sculpt"
  411. @classmethod
  412. def poll(cls, context):
  413. gpd = context.gpencil_data
  414. return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
  415. def draw(self, context):
  416. layout = self.layout
  417. pie = layout.menu_pie()
  418. settings = context.tool_settings.gpencil_sculpt
  419. brush = settings.brush
  420. # W - Launch Sculpt Mode
  421. col = pie.column()
  422. # col.label(text="Tool:")
  423. col.prop(settings, "sculpt_tool", text="")
  424. col.operator("gpencil.sculpt_paint", text="Sculpt", icon='SCULPTMODE_HLT')
  425. # E - Common Settings
  426. col = pie.column(align=True)
  427. col.prop(brush, "size", slider=True)
  428. row = col.row(align=True)
  429. row.prop(brush, "strength", slider=True)
  430. # row.prop(brush, "use_pressure_strength", text="", icon_only=True)
  431. col.prop(brush, "use_falloff")
  432. if settings.sculpt_tool in {'SMOOTH', 'RANDOMIZE'}:
  433. row = col.row(align=True)
  434. row.prop(settings, "use_edit_position", text="Position", icon='MESH_DATA', toggle=True)
  435. row.prop(settings, "use_edit_strength", text="Strength", icon='COLOR', toggle=True)
  436. row.prop(settings, "use_edit_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
  437. # S - Change Brush Type Shortcuts
  438. row = pie.row()
  439. row.prop_enum(settings, "tool", value='GRAB')
  440. row.prop_enum(settings, "tool", value='PUSH')
  441. row.prop_enum(settings, "tool", value='CLONE')
  442. # N - Change Brush Type Shortcuts
  443. row = pie.row()
  444. row.prop_enum(settings, "tool", value='SMOOTH')
  445. row.prop_enum(settings, "tool", value='THICKNESS')
  446. row.prop_enum(settings, "tool", value='STRENGTH')
  447. row.prop_enum(settings, "tool", value='RANDOMIZE')
  448. class GPENCIL_MT_snap(Menu):
  449. bl_label = "Snap"
  450. def draw(self, _context):
  451. layout = self.layout
  452. layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
  453. layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
  454. layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Keep Offset)").use_offset = True
  455. layout.separator()
  456. layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
  457. layout.operator("view3d.snap_cursor_to_center", text="Cursor to World Origin")
  458. layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
  459. class GPENCIL_MT_separate(Menu):
  460. bl_label = "Separate"
  461. def draw(self, _context):
  462. layout = self.layout
  463. layout.operator("gpencil.stroke_separate", text="Selected Points").mode = 'POINT'
  464. layout.operator("gpencil.stroke_separate", text="Selected Strokes").mode = 'STROKE'
  465. layout.operator("gpencil.stroke_separate", text="Active Layer").mode = 'LAYER'
  466. class GPENCIL_MT_gpencil_draw_delete(Menu):
  467. bl_label = "GPencil Draw Delete"
  468. def draw(self, _context):
  469. layout = self.layout
  470. layout.operator_context = 'INVOKE_REGION_WIN'
  471. layout.operator("gpencil.active_frames_delete_all", text="Delete Frame")
  472. class GPENCIL_MT_cleanup(Menu):
  473. bl_label = "Clean Up"
  474. def draw(self, _context):
  475. layout = self.layout
  476. layout.operator("gpencil.frame_clean_loose", text="Loose Points")
  477. layout.separator()
  478. layout.operator("gpencil.frame_clean_fill", text="Boundary Strokes").mode = 'ACTIVE'
  479. layout.operator("gpencil.frame_clean_fill", text="Boundary Strokes all Frames").mode = 'ALL'
  480. layout.separator()
  481. layout.operator("gpencil.reproject")
  482. class GPENCIL_UL_annotation_layer(UIList):
  483. def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
  484. # assert(isinstance(item, bpy.types.GPencilLayer)
  485. gpl = item
  486. if self.layout_type in {'DEFAULT', 'COMPACT'}:
  487. if gpl.lock:
  488. layout.active = False
  489. split = layout.split(factor=0.2)
  490. split.prop(gpl, "color", text="", emboss=True)
  491. split.prop(gpl, "info", text="", emboss=False)
  492. row = layout.row(align=True)
  493. row.prop(gpl, "annotation_hide", text="", emboss=False)
  494. elif self.layout_type == 'GRID':
  495. layout.alignment = 'CENTER'
  496. layout.label(text="", icon_value=icon)
  497. class AnnotationDataPanel:
  498. bl_label = "Annotations"
  499. bl_region_type = 'UI'
  500. bl_options = {'DEFAULT_CLOSED'}
  501. @classmethod
  502. def poll(cls, context):
  503. # Show this panel as long as someone that might own this exists
  504. # AND the owner isn't an object (e.g. GP Object)
  505. if context.gpencil_data_owner is None:
  506. return False
  507. elif type(context.gpencil_data_owner) is bpy.types.Object:
  508. return False
  509. else:
  510. return True
  511. def draw_header(self, context):
  512. if context.space_data.type not in {'VIEW_3D', 'TOPBAR'}:
  513. self.layout.prop(context.space_data, "show_annotation", text="")
  514. def draw(self, context):
  515. layout = self.layout
  516. layout.use_property_decorate = False
  517. # Grease Pencil owner.
  518. gpd_owner = context.gpencil_data_owner
  519. gpd = context.gpencil_data
  520. # Owner selector.
  521. if context.space_data.type == 'CLIP_EDITOR':
  522. layout.row().prop(context.space_data, "grease_pencil_source", expand=True)
  523. layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
  524. # List of layers/notes.
  525. if gpd and gpd.layers:
  526. self.draw_layers(context, layout, gpd)
  527. def draw_layers(self, context, layout, gpd):
  528. row = layout.row()
  529. col = row.column()
  530. if len(gpd.layers) >= 2:
  531. layer_rows = 5
  532. else:
  533. layer_rows = 3
  534. col.template_list("GPENCIL_UL_annotation_layer", "", gpd, "layers", gpd.layers, "active_index",
  535. rows=layer_rows, sort_reverse=True, sort_lock=True)
  536. col = row.column()
  537. sub = col.column(align=True)
  538. sub.operator("gpencil.layer_add", icon='ADD', text="")
  539. sub.operator("gpencil.layer_remove", icon='REMOVE', text="")
  540. gpl = context.active_gpencil_layer
  541. if gpl:
  542. if len(gpd.layers) > 1:
  543. col.separator()
  544. sub = col.column(align=True)
  545. sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
  546. sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
  547. tool_settings = context.tool_settings
  548. if gpd and gpl:
  549. layout.prop(gpl, "thickness")
  550. else:
  551. layout.prop(tool_settings, "annotation_thickness", text="Thickness")
  552. if gpl:
  553. # Full-Row - Frame Locking (and Delete Frame)
  554. row = layout.row(align=True)
  555. row.active = not gpl.lock
  556. if gpl.active_frame:
  557. lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked")
  558. lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status)
  559. else:
  560. lock_label = iface_("Lock Frame")
  561. row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
  562. row.operator("gpencil.active_frame_delete", text="", icon='X')
  563. class AnnotationOnionSkin:
  564. bl_label = "Onion Skin"
  565. bl_region_type = 'UI'
  566. bl_options = {'DEFAULT_CLOSED'}
  567. @classmethod
  568. def poll(cls, context):
  569. # Show this panel as long as someone that might own this exists
  570. # AND the owner isn't an object (e.g. GP Object)
  571. if context.gpencil_data_owner is None:
  572. return False
  573. elif type(context.gpencil_data_owner) is bpy.types.Object:
  574. return False
  575. else:
  576. gpl = context.active_gpencil_layer
  577. if gpl is None:
  578. return False
  579. return True
  580. def draw_header(self, context):
  581. gpl = context.active_gpencil_layer
  582. self.layout.prop(gpl, "use_annotation_onion_skinning", text="")
  583. def draw(self, context):
  584. layout = self.layout
  585. layout.use_property_decorate = False
  586. gpl = context.active_gpencil_layer
  587. col = layout.column()
  588. split = col.split(factor=0.5)
  589. split.active = gpl.use_annotation_onion_skinning
  590. # - Before Frames
  591. sub = split.column(align=True)
  592. row = sub.row(align=True)
  593. row.prop(gpl, "annotation_onion_before_color", text="")
  594. sub.prop(gpl, "annotation_onion_before_range", text="Before")
  595. # - After Frames
  596. sub = split.column(align=True)
  597. row = sub.row(align=True)
  598. row.prop(gpl, "annotation_onion_after_color", text="")
  599. sub.prop(gpl, "annotation_onion_after_range", text="After")
  600. class GreasePencilToolsPanel:
  601. # For use in "2D" Editors without their own toolbar
  602. # subclass must set
  603. # bl_space_type = 'IMAGE_EDITOR'
  604. bl_label = "Grease Pencil Settings"
  605. bl_region_type = 'UI'
  606. bl_options = {'DEFAULT_CLOSED'}
  607. @classmethod
  608. def poll(cls, context):
  609. # XXX - disabled in 2.8 branch.
  610. return False
  611. return (context.gpencil_data is not None)
  612. def draw(self, context):
  613. layout = self.layout
  614. gpencil_active_brush_settings_simple(context, layout)
  615. layout.separator()
  616. gpencil_stroke_placement_settings(context, layout)
  617. class GreasePencilMaterialsPanel:
  618. # Mix-in, use for properties editor and top-bar.
  619. def draw(self, context):
  620. layout = self.layout
  621. show_full_ui = (self.bl_space_type == 'PROPERTIES')
  622. is_view3d = (self.bl_space_type == 'VIEW_3D')
  623. tool_settings = context.scene.tool_settings
  624. gpencil_paint = tool_settings.gpencil_paint
  625. brush = gpencil_paint.brush
  626. ob = context.object
  627. row = layout.row()
  628. if ob:
  629. is_sortable = len(ob.material_slots) > 1
  630. rows = 7
  631. row.template_list("GPENCIL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
  632. # if topbar popover and brush pinned, disable
  633. if is_view3d and brush is not None:
  634. gp_settings = brush.gpencil_settings
  635. if gp_settings.use_material_pin:
  636. row.enabled = False
  637. col = row.column(align=True)
  638. if show_full_ui:
  639. col.operator("object.material_slot_add", icon='ADD', text="")
  640. col.operator("object.material_slot_remove", icon='REMOVE', text="")
  641. col.menu("GPENCIL_MT_color_context_menu", icon='DOWNARROW_HLT', text="")
  642. if is_sortable:
  643. col.separator()
  644. col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
  645. col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
  646. col.separator()
  647. sub = col.column(align=True)
  648. sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False
  649. sub.operator("gpencil.color_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True
  650. if show_full_ui:
  651. row = layout.row()
  652. row.template_ID(ob, "active_material", new="material.new", live_icon=True)
  653. slot = context.material_slot
  654. if slot:
  655. icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
  656. row.prop(slot, "link", icon=icon_link, icon_only=True)
  657. if ob.data.use_stroke_edit_mode:
  658. row = layout.row(align=True)
  659. row.operator("gpencil.stroke_change_color", text="Assign")
  660. row.operator("gpencil.color_select", text="Select").deselect = False
  661. row.operator("gpencil.color_select", text="Deselect").deselect = True
  662. # stroke color
  663. ma = None
  664. if is_view3d and brush is not None:
  665. gp_settings = brush.gpencil_settings
  666. if gp_settings.use_material_pin is False:
  667. ma = ob.material_slots[ob.active_material_index].material
  668. else:
  669. ma = gp_settings.material
  670. if ma is not None and ma.grease_pencil is not None:
  671. gpcolor = ma.grease_pencil
  672. if (
  673. gpcolor.stroke_style == 'SOLID' or
  674. gpcolor.use_stroke_pattern or
  675. gpcolor.use_stroke_texture_mix
  676. ):
  677. row = layout.row()
  678. row.prop(gpcolor, "color", text="Stroke Color")
  679. else:
  680. space = context.space_data
  681. row.template_ID(space, "pin_id")
  682. class GPENCIL_UL_layer(UIList):
  683. def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
  684. # assert(isinstance(item, bpy.types.GPencilLayer)
  685. gpl = item
  686. if self.layout_type in {'DEFAULT', 'COMPACT'}:
  687. if gpl.lock:
  688. layout.active = False
  689. row = layout.row(align=True)
  690. row.label(
  691. text="",
  692. icon='BONE_DATA' if gpl.is_parented else 'BLANK1',
  693. )
  694. row.prop(gpl, "info", text="", emboss=False)
  695. row = layout.row(align=True)
  696. row.prop(gpl, "mask_layer", text="",
  697. icon='MOD_MASK' if gpl.mask_layer else 'LAYER_ACTIVE',
  698. emboss=False)
  699. row.prop(gpl, "lock", text="", emboss=False)
  700. row.prop(gpl, "hide", text="", emboss=False)
  701. subrow = row.row(align=True)
  702. subrow.prop(
  703. gpl,
  704. "use_onion_skinning",
  705. text="",
  706. icon='ONIONSKIN_ON' if gpl.use_onion_skinning else 'ONIONSKIN_OFF',
  707. emboss=False,
  708. )
  709. elif self.layout_type == 'GRID':
  710. layout.alignment = 'CENTER'
  711. layout.label(
  712. text="",
  713. icon_value=icon,
  714. )
  715. classes = (
  716. GPENCIL_MT_pie_tool_palette,
  717. GPENCIL_MT_pie_settings_palette,
  718. GPENCIL_MT_pie_tools_more,
  719. GPENCIL_MT_pie_sculpt,
  720. GPENCIL_MT_snap,
  721. GPENCIL_MT_separate,
  722. GPENCIL_MT_cleanup,
  723. GPENCIL_MT_gpencil_draw_delete,
  724. GPENCIL_UL_annotation_layer,
  725. GPENCIL_UL_layer,
  726. )
  727. if __name__ == "__main__": # only for live edit.
  728. from bpy.utils import register_class
  729. for cls in classes:
  730. register_class(cls)