diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 3fe936a706c..62c6996e082 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1821,6 +1821,10 @@ def km_graph_editor(params): ("graph.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("confirm", False)]}), ("graph.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("graph.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None), + ("graph.keyframe_jump", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, + {"properties": [("next", True)]}), + ("graph.keyframe_jump", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, + {"properties": [("next", False)]}), ("graph.click_insert", {"type": params.action_mouse, "value": 'CLICK', "ctrl": True}, None), ("graph.click_insert", {"type": params.action_mouse, "value": 'CLICK', "shift": True, "ctrl": True}, {"properties": [("extend", True)]}), diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index aa06a239e11..1856445c4c4 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3179,6 +3179,12 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool keyframe_jump_poll(bContext *C) +{ + /* There is a keyframe jump operator specifically for the Graph Editor. */ + return ED_operator_screenactive_norender(C) && CTX_wm_area(C)->spacetype != SPACE_GRAPH; +} + static void SCREEN_OT_keyframe_jump(wmOperatorType *ot) { ot->name = "Jump to Keyframe"; @@ -3187,7 +3193,7 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot) ot->exec = keyframe_jump_exec; - ot->poll = ED_operator_screenactive_norender; + ot->poll = keyframe_jump_poll; ot->flag = OPTYPE_UNDO_GROUPED; ot->undo_group = "Frame Change"; diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 653be0d957c..9d4f43fe25b 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -38,6 +38,7 @@ #include "BKE_global.h" #include "BKE_nla.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "DEG_depsgraph_build.h" @@ -2178,6 +2179,104 @@ void GRAPH_OT_frame_jump(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static bool find_closest_frame(const FCurve *fcu, + const float frame, + const bool next, + float *closest_frame) +{ + bool replace; + int bezt_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, frame, fcu->totvert, &replace); + + BezTriple *bezt; + if (next) { + if (replace) { + bezt_index++; + } + if (bezt_index > fcu->totvert - 1) { + return false; + } + bezt = &fcu->bezt[bezt_index]; + } + else { + if (bezt_index - 1 < 0) { + return false; + } + bezt = &fcu->bezt[bezt_index - 1]; + } + + *closest_frame = bezt->vec[1][0]; + return true; +} + +static int keyframe_jump_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + Scene *scene = CTX_data_scene(C); + + bool next = RNA_boolean_get(op->ptr, "next"); + + /* Get editor data. */ + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + ListBase anim_data = {NULL, NULL}; + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | + ANIMFILTER_NODUPLIS); + if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) { + filter |= ANIMFILTER_SEL; + } + + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + float closest_frame = next ? FLT_MAX : -FLT_MAX; + bool found = false; + + const float current_frame = BKE_scene_frame_get(scene); + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + const FCurve *fcu = ale->key_data; + if (!fcu->bezt) { + continue; + } + float closest_fcu_frame; + if (!find_closest_frame(fcu, current_frame, next, &closest_fcu_frame)) { + continue; + } + if ((next && closest_fcu_frame < closest_frame) || + (!next && closest_fcu_frame > closest_frame)) { + closest_frame = closest_fcu_frame; + found = true; + } + } + + if (!found) { + BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction"); + return OPERATOR_CANCELLED; + } + + BKE_scene_frame_set(scene, closest_frame); + + /* Set notifier that things have changed. */ + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene); + return OPERATOR_FINISHED; +} + +void GRAPH_OT_keyframe_jump(wmOperatorType *ot) +{ + ot->name = "Jump to Keyframe"; + ot->description = "Jump to previous/next keyframe"; + ot->idname = "GRAPH_OT_keyframe_jump"; + + ot->exec = keyframe_jump_exec; + + ot->poll = graphkeys_framejump_poll; + ot->flag = OPTYPE_UNDO_GROUPED; + ot->undo_group = "Frame Change"; + + /* properties */ + RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", ""); +} + /* snap 2D cursor value to the average value of selected keyframe */ static int graphkeys_snap_cursor_value_exec(bContext *C, wmOperator *UNUSED(op)) { diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index 13ddee98051..d6ac7b3b769 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -130,6 +130,7 @@ void GRAPH_OT_extrapolation_type(struct wmOperatorType *ot); void GRAPH_OT_easing_type(struct wmOperatorType *ot); void GRAPH_OT_frame_jump(struct wmOperatorType *ot); +void GRAPH_OT_keyframe_jump(struct wmOperatorType *ot); void GRAPH_OT_snap_cursor_value(struct wmOperatorType *ot); void GRAPH_OT_snap(struct wmOperatorType *ot); void GRAPH_OT_equalize_handles(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index d4a5076cd7a..f40e5812dc4 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -448,6 +448,7 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_equalize_handles); WM_operatortype_append(GRAPH_OT_mirror); WM_operatortype_append(GRAPH_OT_frame_jump); + WM_operatortype_append(GRAPH_OT_keyframe_jump); WM_operatortype_append(GRAPH_OT_snap_cursor_value); WM_operatortype_append(GRAPH_OT_handle_type); WM_operatortype_append(GRAPH_OT_interpolation_type);