gp_toolbox/OP_keyframe_jump.py

206 lines
7.4 KiB
Python

import bpy
from .utils import get_addon_prefs, is_locked, is_hidden
from bpy.props import BoolProperty ,EnumProperty ,StringProperty
class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
bl_idname = "screen.gp_keyframe_jump"
bl_label = "Jump to GPencil keyframe"
bl_description = "Jump to prev/next keyframe on active and selected layers of active grease pencil object"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GREASEPENCIL'
next : BoolProperty(
name="Next GP keyframe", description="Go to next active GP keyframe",
default=True, options={'HIDDEN', 'SKIP_SAVE'})
target : EnumProperty(
name="Target layer", description="Choose wich layer to evaluate for keyframe change",
default='ACTIVE', options={'HIDDEN', 'SKIP_SAVE'},
items=(
('ACTIVE', 'Active and selected', 'jump in keyframes of active and other selected layers ', 0),
('VISIBLE', 'Visibles layers', 'jump in keyframes of visibles layers', 1),
('ACCESSIBLE', 'Visible and unlocked layers', 'jump in keyframe of all layers', 2),
))
keyframe_type : EnumProperty(
name="Keyframe Filter", description="Jump to choosen keyframe type, else use the UI jump filter",
default='NONE', options={'HIDDEN', 'SKIP_SAVE'},
items=(
('NONE', 'Use UI Filter', '', 0), # 'KEYFRAME'
('ALL', 'All', '', 1),
('KEYFRAME', 'Keyframe', '', 'KEYTYPE_KEYFRAME_VEC', 2),
('BREAKDOWN', 'Breakdown', '', 'KEYTYPE_BREAKDOWN_VEC', 3),
('MOVING_HOLD', 'Moving Hold', '', 'KEYTYPE_MOVING_HOLD_VEC', 4),
('EXTREME', 'Extreme', '', 'KEYTYPE_EXTREME_VEC', 5),
('JITTER', 'Jitter', '', 'KEYTYPE_JITTER_VEC', 6),
('GENERATED', 'Generated', '', 'KEYTYPE_GENERATED_VEC', 7),
))
def execute(self, context):
if not context.object.data.layers.active:
self.report({'ERROR'}, 'No active layer on current GPencil object')
return {"CANCELLED"}
if self.target == 'ACTIVE':
gpl = [l for l in context.object.data.layers if l.select and not is_hidden(l)]
if not context.object.data.layers.active in gpl:
gpl.append(context.object.data.layers.active)
elif self.target == 'VISIBLE':
gpl = [l for l in context.object.data.layers if not is_hidden(l)]
elif self.target == 'ACCESSIBLE':
gpl = [l for l in context.object.data.layers if not is_hidden(l) and not is_locked(l)]
if self.keyframe_type != 'NONE':
# use shortcut choice override
kftype = self.keyframe_type
else:
kftype = context.scene.gptoolprops.keyframe_type
current = context.scene.frame_current
p = n = None
mins = []
maxs = []
for l in gpl:
for f in l.frames:
# keyframe type filter
if kftype != 'ALL' and f.keyframe_type != kftype:
continue
if f.frame_number < current:
p = f.frame_number
if f.frame_number > current:
n = f.frame_number
break
mins.append(p)
maxs.append(n)
p = n = None
mins = [i for i in mins if i is not None]
maxs = [i for i in maxs if i is not None]
if mins:
p = max(mins)
if maxs:
n = min(maxs)
## Double the frame set to avoid refresh problem (had one in 2.91.2)
if self.next and n is not None:
context.scene.frame_set(n)
context.scene.frame_current = n
elif not self.next and p is not None:
context.scene.frame_set(p)
context.scene.frame_current = p
else:
self.report({'INFO'}, 'No keyframe in this direction')
return {"CANCELLED"}
return {"FINISHED"}
class KFJ_OT_rebinder(bpy.types.Operator):
bl_idname = "prefs.shortcut_rebinder"
bl_label = "rebind keyframe jump shortcut"
bl_options = {'REGISTER', 'INTERNAL'}
s_keycode: StringProperty()
s_ctrl: StringProperty()
s_shift: StringProperty()
s_alt: StringProperty()
def invoke(self, context, event):
self.prefs = get_addon_prefs()
self.init_value = getattr(self.prefs, self.s_keycode)
setattr(self.prefs, self.s_keycode, '')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
exclude_keys = {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE',
'TIMER_REPORT', 'ESC', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}
exclude_in = ('SHIFT', 'CTRL', 'ALT')
if event.type == 'ESC':
setattr(self.prefs, self.s_keycode, self.init_value)
return {'CANCELLED'}
if event.type not in exclude_keys and not any(x in event.type for x in exclude_in):
print('key:', event.type, 'value:', event.value)
if event.value == 'PRESS':
self.report({'INFO'}, event.type)
setattr(self.prefs, self.s_shift, event.shift)
setattr(self.prefs, self.s_alt, event.alt)
setattr(self.prefs, self.s_ctrl, event.ctrl)
setattr(self.prefs, self.s_keycode, event.type)
# -# maybe add an autorebind trigger at keycode propertie level
# -# now dependent of register function within the scope of the file...
# auto_rebind()
unregister_keymaps()
register_keymaps()
context.area.tag_redraw()# seems to do nothing, have to move the mouse to update
return {'FINISHED'}
return {"RUNNING_MODAL"}
""" class KFJ_OT_rebind(bpy.types.Operator):
bl_idname = "prefs.rebind_kfj_shortcut"
bl_label = "rebind keyframe jump shortcut"
bl_options = {'REGISTER', 'INTERNAL'}
def execute(self, context):
unregister_keymaps()
register_keymaps()
return{'FINISHED'} """
addon_keymaps = []
def register_keymaps():
pref = get_addon_prefs()
if not pref.kfj_use_shortcut:
return
addon = bpy.context.window_manager.keyconfigs.addon
km = addon.keymaps.new(name = "Screen", space_type = "EMPTY")
kmi = km.keymap_items.new('screen.gp_keyframe_jump', type=pref.kfj_prev_keycode, value="PRESS", alt=pref.kfj_prev_alt, ctrl=pref.kfj_prev_ctrl, shift=pref.kfj_prev_shift, any=False)
kmi.properties.next = False
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new('screen.gp_keyframe_jump', type=pref.kfj_next_keycode, value="PRESS", alt=pref.kfj_next_alt, ctrl=pref.kfj_next_ctrl, shift=pref.kfj_next_shift, any=False)
kmi.properties.next = True
addon_keymaps.append((km, kmi))
def unregister_keymaps():
# print('UNBIND CANVAS ROTATE KEYMAPS')#Dbg
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
# del addon_keymaps[:]
classes = (
KFJ_OT_rebinder,
GPTB_OT_jump_gp_keyframe,
)
def register():
if not bpy.app.background:
for cls in classes:
bpy.utils.register_class(cls)
register_keymaps()
def unregister():
if not bpy.app.background:
unregister_keymaps()
for cls in reversed(classes):
bpy.utils.unregister_class(cls)