GPencil Tools: Optimize Undo for Rotate Canvas
[blender-addons.git] / greasepencil_tools / prefs.py
blob900caa86b27ba121669b5edb4f5b33a11519d1d6
1 # SPDX-FileCopyrightText: 2020-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 import bpy
6 import os
7 from bpy.props import (
8 BoolProperty,
9 EnumProperty,
10 StringProperty,
11 PointerProperty,
12 FloatProperty,
13 # IntProperty,
16 from .ui_panels import GP_PT_sidebarPanel
18 def get_addon_prefs():
19 import os
20 addon_name = os.path.splitext(__name__)[0]
21 addon_prefs = bpy.context.preferences.addons[addon_name].preferences
22 return (addon_prefs)
24 from .timeline_scrub import GPTS_timeline_settings, draw_ts_pref
25 from .layer_navigator import GPNAV_layer_navigator_settings, draw_nav_pref
27 ## Addons Preferences Update Panel
28 def update_panel(self, context):
29 try:
30 bpy.utils.unregister_class(GP_PT_sidebarPanel)
31 except:
32 pass
33 GP_PT_sidebarPanel.bl_category = get_addon_prefs().category
34 bpy.utils.register_class(GP_PT_sidebarPanel)
36 ## keymap binder for rotate canvas
37 def auto_rebind(self, context):
38 unregister_keymaps()
39 register_keymaps()
41 class GreasePencilAddonPrefs(bpy.types.AddonPreferences):
42 bl_idname = os.path.splitext(__name__)[0] #__package__
44 ts: PointerProperty(type=GPTS_timeline_settings)
45 nav: PointerProperty(type=GPNAV_layer_navigator_settings)
48 category : StringProperty(
49 name="Category",
50 description="Choose a name for the category of the panel",
51 default="Grease Pencil",
52 update=update_panel)
54 ## Tool tab
55 pref_tab : EnumProperty(
56 name="Preference Tool Tab", description="Choose tool preferences to display",
57 default='canvas_rotate',
58 items=(
59 ('canvas_rotate', 'Rotate Canvas', 'Canvas Rotate tool shortcut and prefs', 0),
60 ('box_deform', 'Box Deform', 'Box Deform tool prefs', 1),
61 ('timeline_scrub', 'Timeline Scrub', 'Timeline Scrub tool shortcut and prefs', 2),
62 ('layer_navigator', 'Layer Navigator', 'Layer Navigation tool shortcut and prefs', 3),
66 # --- props
68 use_clic_drag : BoolProperty(
69 name='Use click drag directly on points',
70 description="Change the active tool to 'tweak' during modal, Allow to direct clic-drag points of the box",
71 default=True)
73 default_deform_type : EnumProperty(
74 items=(('KEY_LINEAR', "Linear (perspective mode)", "Linear interpolation, like corner deform / perspective tools of classic 2D", 'IPO_LINEAR',0),
75 ('KEY_BSPLINE', "Spline (smooth deform)", "Spline interpolation transformation\nBest when lattice is subdivided", 'IPO_CIRC',1),
77 name='Starting Interpolation', default='KEY_LINEAR', description='Choose default interpolation when entering mode')
79 # About interpolation : https://docs.blender.org/manual/en/2.83/animation/shape_keys/shape_keys_panel.html#fig-interpolation-type
81 auto_swap_deform_type : BoolProperty(
82 name='Auto swap interpolation mode',
83 description="Automatically set interpolation to 'spline' when subdividing lattice\n Back to 'linear' when",
84 default=True)
86 ## rotate canvas variables
88 ## Use HUD
89 canvas_use_hud: BoolProperty(
90 name = "Use Hud",
91 description = "Display angle lines and angle value as text on viewport",
92 default = False)
94 canvas_use_view_center: BoolProperty(
95 name = "Rotate From View Center In Camera",
96 description = "Rotate from view center in camera view, Else rotate from camera center",
97 default = True)
99 ## Canvas rotate
100 canvas_use_shortcut: BoolProperty(
101 name = "Use Default Shortcut",
102 description = "Use default shortcut: mouse double-click + modifier",
103 default = True,
104 update=auto_rebind)
106 mouse_click : EnumProperty(
107 name="Mouse button", description="click on right/left/middle mouse button in combination with a modifier to trigger alignment",
108 default='MIDDLEMOUSE',
109 items=(
110 ('RIGHTMOUSE', 'Right click', 'Use click on Right mouse button', 'MOUSE_RMB', 0),
111 ('LEFTMOUSE', 'Left click', 'Use click on Left mouse button', 'MOUSE_LMB', 1),
112 ('MIDDLEMOUSE', 'Mid click', 'Use click on Mid mouse button', 'MOUSE_MMB', 2),
114 update=auto_rebind)
116 use_shift: BoolProperty(
117 name = "combine with shift",
118 description = "add shift",
119 default = False,
120 update=auto_rebind)
122 use_alt: BoolProperty(
123 name = "combine with alt",
124 description = "add alt",
125 default = True,
126 update=auto_rebind)
128 use_ctrl: BoolProperty(
129 name = "combine with ctrl",
130 description = "add ctrl",
131 default = True,
132 update=auto_rebind)
134 rc_angle_step: FloatProperty(
135 name="Angle Steps",
136 description="Step the rotation using this angle when using rotate canvas step modifier",
137 default=0.2617993877991494, # 15
138 min=0.01745329238474369, # 1
139 max=3.1415927410125732, # 180
140 soft_min=0.01745329238474369, # 1
141 soft_max=1.5707963705062866, # 90
142 step=10, precision=1, subtype='ANGLE', unit='ROTATION')
144 def draw(self, context):
145 prefs = get_addon_prefs()
146 layout = self.layout
147 # layout.use_property_split = True
148 row= layout.row(align=True)
150 ## TAB CATEGORY
151 box = layout.box()
152 row = box.row(align=True)
153 row.label(text="Panel Category:")
154 row.prop(self, "category", text="")
156 row = layout.row(align=True)
157 row.prop(self, "pref_tab", expand=True)
159 if self.pref_tab == 'box_deform':
161 ## BOX DEFORM
162 box = layout.box()
163 row = box.row(align=True)
164 row.label(text='Box Deform:')
165 row.operator("wm.call_menu", text="", icon='QUESTION').name = "GPT_MT_box_deform_doc"
166 box.prop(self, "use_clic_drag")
168 box.prop(self, "default_deform_type")
169 box.label(text="Deformer type can be changed during modal with 'M' key, this is for default behavior", icon='INFO')
171 box.prop(self, "auto_swap_deform_type")
172 box.label(text="Once 'M' is hit, auto swap is deactivated to stay in your chosen mode", icon='INFO')
174 if self.pref_tab == 'canvas_rotate':
175 ## ROTATE CANVAS
176 box = layout.box()
177 box.label(text='Rotate Canvas:')
179 box.prop(self, "canvas_use_shortcut", text='Bind Shortcuts')
181 if self.canvas_use_shortcut:
183 row = box.row()
184 row.label(text="(Auto rebind when changing shortcut)")#icon=""
185 # row.operator("prefs.rebind_shortcut", text='Bind/Rebind shortcuts', icon='FILE_REFRESH')#EVENT_SPACEKEY
186 row = box.row(align = True)
187 row.prop(self, "use_ctrl", text='Ctrl')#, expand=True
188 row.prop(self, "use_alt", text='Alt')#, expand=True
189 row.prop(self, "use_shift", text='Shift')#, expand=True
190 row.prop(self, "mouse_click",text='')#expand=True
192 if not self.use_ctrl and not self.use_alt and not self.use_shift:
193 box.label(text="Choose at least one modifier to combine with click (default: Ctrl+Alt)", icon="ERROR")# INFO
195 if not all((self.use_ctrl, self.use_alt, self.use_shift)):
196 row = box.row(align = True)
197 snap_key_list = []
198 if not self.use_ctrl:
199 snap_key_list.append('Ctrl')
200 if not self.use_shift:
201 snap_key_list.append('Shift')
202 if not self.use_alt:
203 snap_key_list.append('Alt')
205 row.label(text=f"Step rotation with: {' or '.join(snap_key_list)}", icon='DRIVER_ROTATIONAL_DIFFERENCE')
206 row.prop(self, "rc_angle_step", text='Angle Steps')
208 else:
209 box.label(text="No hotkey has been set automatically. Following operators needs to be set manually:", icon="ERROR")
210 box.label(text="view3d.rotate_canvas")
211 box.prop(self, 'canvas_use_view_center')
212 box.prop(self, 'canvas_use_hud')
214 if self.pref_tab == 'timeline_scrub':
215 ## SCRUB TIMELINE
216 box = layout.box()
217 draw_ts_pref(prefs.ts, box)
219 if self.pref_tab == 'layer_navigator':
220 ## LAYER NAVIGATOR
221 box = layout.box()
222 draw_nav_pref(prefs.nav, box)
226 class GPT_MT_box_deform_doc(bpy.types.Menu):
227 # bl_idname = "OBJECT_MT_custom_menu"
228 bl_label = "Box Deform Infos Sheet"
230 def draw(self, context):
231 layout = self.layout
232 # call another menu
233 #layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map"
234 #**Behavior from context mode**
235 col = layout.column()
236 col.label(text='Box Deform Tool')
237 col.label(text="Usage:", icon='MOD_LATTICE')
238 col.label(text="Use the shortcut 'Ctrl+T' in available modes (listed below)")
239 col.label(text="The lattice box is generated facing your view (be sure to face canvas if you want to stay on it)")
240 col.label(text="Use shortcuts below to deform (a help will be displayed in the topbar)")
242 col.separator()
243 col.label(text="Shortcuts:", icon='HAND')
244 col.label(text="Spacebar / Enter : Confirm")
245 col.label(text="Shift + Spacebar / Enter : Confirm and let the lattice in place")
246 col.label(text="Delete / Backspace / Tab(twice) / Ctrl+T : Cancel")
247 col.label(text="M : Toggle between Linear and Spline mode at any moment")
248 col.label(text="1-9 top row number : Subdivide the box")
249 col.label(text="Ctrl + arrows-keys : Subdivide the box incrementally in individual X/Y axis")
251 col.separator()
252 col.label(text="Modes and deformation target:", icon='PIVOT_BOUNDBOX')
253 col.label(text="- Object mode : The whole GP object is deformed (including all frames)")
254 col.label(text="- GPencil Edit mode : Deform Selected points")
255 col.label(text="- Gpencil Paint : Deform last Strokes")
256 # col.label(text="- Lattice edit : Revive the modal after a ctrl+Z")
258 col.separator()
259 col.label(text="Notes:", icon='TEXT')
260 col.label(text="- If you return in box deform after applying (with a ctrl+Z), you need to hit 'Ctrl+T' again to revive the modal.")
261 col.label(text="- A cancel warning will be displayed the first time you hit Tab")
263 ### rotate canvas keymap
265 addon_keymaps = []
266 def register_keymaps():
267 pref = get_addon_prefs()
268 if not pref.canvas_use_shortcut:
269 return
270 kc = bpy.context.window_manager.keyconfigs.addon
271 if kc is None:
272 return
274 km = kc.keymaps.new(name = "3D View", space_type = "VIEW_3D")
276 if 'view3d.rotate_canvas' not in km.keymap_items:
277 km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
278 kmi = km.keymap_items.new('view3d.rotate_canvas',
279 type=pref.mouse_click, value="PRESS", alt=pref.use_alt, ctrl=pref.use_ctrl, shift=pref.use_shift, any=False)
281 addon_keymaps.append((km, kmi))
283 def unregister_keymaps():
284 for km, kmi in addon_keymaps:
285 km.keymap_items.remove(kmi)
286 addon_keymaps.clear()
289 ### REGISTER ---
291 classes = (
292 GPTS_timeline_settings,
293 GPNAV_layer_navigator_settings,
294 GPT_MT_box_deform_doc,
295 GreasePencilAddonPrefs,
298 def register():
299 for cls in classes:
300 bpy.utils.register_class(cls)
301 # Force box deform running to false
302 bpy.context.preferences.addons[os.path.splitext(__name__)[0]].preferences.boxdeform_running = False
303 register_keymaps()
305 def unregister():
306 unregister_keymaps()
307 for cls in reversed(classes):
308 bpy.utils.unregister_class(cls)