diff options
19 files changed, 471 insertions, 204 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index abbc8ad103e..cc4e3e269ed 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1411,13 +1411,9 @@ def km_graph_editor(params): {"properties": [("mode", 'RIGHT'), ("extend", False)]}), *_template_items_select_actions(params, "graph.select_all"), ("graph.select_box", {"type": 'B', "value": 'PRESS'}, - {"properties": [("axis_range", False), ("include_handles", False)]}), + {"properties": [("axis_range", False)]}), ("graph.select_box", {"type": 'B', "value": 'PRESS', "alt": True}, - {"properties": [("axis_range", True), ("include_handles", False)]}), - ("graph.select_box", {"type": 'B', "value": 'PRESS', "ctrl": True}, - {"properties": [("axis_range", False), ("include_handles", True)]}), - ("graph.select_box", {"type": 'B', "value": 'PRESS', "ctrl": True, "alt": True}, - {"properties": [("axis_range", True), ("include_handles", True)]}), + {"properties": [("axis_range", True)]}), ("graph.select_box", {"type": params.select_tweak, "value": 'ANY'}, {"properties": [("tweak", True), ("mode", 'SET')]}), ("graph.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True}, diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 2fa747b98ab..7d8ada2d117 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -860,19 +860,15 @@ def km_graph_editor(params): ("graph.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}), ("graph.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}), ("graph.select_box", {"type": 'Q', "value": 'PRESS'}, - {"properties": [("axis_range", False), ("include_handles", False)]}), + {"properties": [("axis_range", False)]}), ("graph.select_box", {"type": 'Q', "value": 'PRESS', "alt": True}, - {"properties": [("axis_range", True), ("include_handles", False)]}), - ("graph.select_box", {"type": 'Q', "value": 'PRESS', "ctrl": True}, - {"properties": [("axis_range", False), ("include_handles", True)]}), - ("graph.select_box", {"type": 'Q', "value": 'PRESS', "ctrl": True, "alt": True}, - {"properties": [("axis_range", True), ("include_handles", True)]}), + {"properties": [("axis_range", True)]}), ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, - {"properties":[("tweak", True), ("axis_range", False), ("include_handles", False), ("mode", 'SET')]}), + {"properties":[("tweak", True), ("axis_range", False), ("mode", 'SET')]}), ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, - {"properties":[("tweak", True), ("axis_range", False), ("include_handles", False), ("mode", 'ADD')]}), + {"properties":[("tweak", True), ("axis_range", False), ("mode", 'ADD')]}), ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, - {"properties":[("tweak", True), ("axis_range", False),("include_handles", False), ("mode", 'SUB')]}), + {"properties":[("tweak", True), ("axis_range", False), ("mode", 'SUB')]}), ("graph.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None), ("graph.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None), ("graph.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None), diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index 630c5fa1856..e03780cc027 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -35,6 +35,8 @@ struct Path; struct TextBox; struct rctf; +typedef int eBezTriple_Flag__Alias; + typedef struct CurveCache { ListBase disp; ListBase bev; @@ -242,6 +244,12 @@ void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *next, const bool is_fcurve, const char smoothing); +void BKE_nurb_handle_calc_ex(struct BezTriple *bezt, + struct BezTriple *prev, + struct BezTriple *next, + const eBezTriple_Flag__Alias handle_sel_flag, + const bool is_fcurve, + const char smoothing); void BKE_nurb_handle_calc_simple(struct Nurb *nu, struct BezTriple *bezt); void BKE_nurb_handle_calc_simple_auto(struct Nurb *nu, struct BezTriple *bezt); @@ -249,7 +257,9 @@ void BKE_nurb_handle_smooth_fcurve(struct BezTriple *bezt, int total, bool cycli void BKE_nurb_handles_calc(struct Nurb *nu); void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag); -void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, const bool use_handle); +void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, + const eBezTriple_Flag__Alias sel_flag, + const bool use_handle); void BKE_nurb_handles_test(struct Nurb *nu, const bool use_handles); /* **** Depsgraph evaluation **** */ diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 5be9a35b168..426e0ed4b0e 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -328,7 +328,8 @@ eFCU_Cycle_Type BKE_fcurve_get_cycle_type(struct FCurve *fcu); /* -------- Curve Sanity -------- */ void calchandles_fcurve(struct FCurve *fcu); -void testhandles_fcurve(struct FCurve *fcu, const bool use_handle); +void calchandles_fcurve_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag); +void testhandles_fcurve(struct FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle); void sort_time_fcurve(struct FCurve *fcu); short test_time_fcurve(struct FCurve *fcu); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index d81d250a305..12bb7b573bd 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -3201,6 +3201,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render) static void calchandleNurb_intern(BezTriple *bezt, const BezTriple *prev, const BezTriple *next, + eBezTriple_Flag handle_sel_flag, bool is_fcurve, bool skip_align, char fcurve_smoothing) @@ -3402,7 +3403,7 @@ static void calchandleNurb_intern(BezTriple *bezt, len_ratio = len_a / len_b; - if (bezt->f1 & SELECT) { /* order of calculation */ + if (bezt->f1 & handle_sel_flag) { /* order of calculation */ if (ELEM(bezt->h2, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) { /* aligned */ if (len_a > eps) { len = 1.0f / len_ratio; @@ -3443,7 +3444,7 @@ static void calchandleNurb_intern(BezTriple *bezt, #undef p2_h2 } -static void calchandlesNurb_intern(Nurb *nu, bool skip_align) +static void calchandlesNurb_intern(Nurb *nu, eBezTriple_Flag handle_sel_flag, bool skip_align) { BezTriple *bezt, *prev, *next; int a; @@ -3466,7 +3467,7 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align) next = bezt + 1; while (a--) { - calchandleNurb_intern(bezt, prev, next, 0, skip_align, 0); + calchandleNurb_intern(bezt, prev, next, handle_sel_flag, 0, skip_align, 0); prev = bezt; if (a == 1) { if (nu->flagu & CU_NURB_CYCLIC) { @@ -4038,15 +4039,36 @@ void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cycle) } } +/** + * Recalculate the handles of a nurb bezier-triple. Acts based on handle selection with `SELECT` + * flag. To use a different flag, use #BKE_nurb_handle_calc_ex(). + */ void BKE_nurb_handle_calc( BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve, const char smoothing) { - calchandleNurb_intern(bezt, prev, next, is_fcurve, false, smoothing); + calchandleNurb_intern(bezt, prev, next, SELECT, is_fcurve, false, smoothing); +} + +/** + * Variant of #BKE_nurb_handle_calc() that allows calculating based on a different select flag. + * + * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`, + * but may want to use a different one at times (if caller does not operate on + * selection). + */ +void BKE_nurb_handle_calc_ex(BezTriple *bezt, + BezTriple *prev, + BezTriple *next, + const eBezTriple_Flag__Alias handle_sel_flag, + const bool is_fcurve, + const char smoothing) +{ + calchandleNurb_intern(bezt, prev, next, handle_sel_flag, is_fcurve, false, smoothing); } void BKE_nurb_handles_calc(Nurb *nu) /* first, if needed, set handle flags */ { - calchandlesNurb_intern(nu, false); + calchandlesNurb_intern(nu, SELECT, false); } /** @@ -4101,11 +4123,21 @@ void BKE_nurb_handle_calc_simple_auto(Nurb *nu, BezTriple *bezt) } /** + * Update selected handle types to ensure valid state, e.g. deduce "Auto" types to concrete ones. + * Thereby \a sel_flag defines what qualifies as selected. * Use when something has changed handle positions. * * The caller needs to recalculate handles. + * + * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`, + * but may want to use a different one at times (if caller does not operate on + * selection). + * \param use_handle: Check selection state of individual handles, otherwise always update both + * handles if the key is selected. */ -void BKE_nurb_bezt_handle_test(BezTriple *bezt, const bool use_handle) +void BKE_nurb_bezt_handle_test(BezTriple *bezt, + const eBezTriple_Flag__Alias sel_flag, + const bool use_handle) { short flag = 0; @@ -4114,18 +4146,18 @@ void BKE_nurb_bezt_handle_test(BezTriple *bezt, const bool use_handle) #define SEL_F3 (1 << 2) if (use_handle) { - if (bezt->f1 & SELECT) { + if (bezt->f1 & sel_flag) { flag |= SEL_F1; } - if (bezt->f2 & SELECT) { + if (bezt->f2 & sel_flag) { flag |= SEL_F2; } - if (bezt->f3 & SELECT) { + if (bezt->f3 & sel_flag) { flag |= SEL_F3; } } else { - flag = (bezt->f2 & SELECT) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0; + flag = (bezt->f2 & sel_flag) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0; } /* check for partial selection */ @@ -4166,7 +4198,7 @@ void BKE_nurb_handles_test(Nurb *nu, const bool use_handle) bezt = nu->bezt; a = nu->pntsu; while (a--) { - BKE_nurb_bezt_handle_test(bezt, use_handle); + BKE_nurb_bezt_handle_test(bezt, SELECT, use_handle); bezt++; } @@ -4639,7 +4671,7 @@ void BKE_curve_nurbs_vert_coords_apply_with_mat4(ListBase *lb, } } - calchandlesNurb_intern(nu, true); + calchandlesNurb_intern(nu, SELECT, true); } } @@ -4677,7 +4709,7 @@ void BKE_curve_nurbs_vert_coords_apply(ListBase *lb, } } - calchandlesNurb_intern(nu, true); + calchandlesNurb_intern(nu, SELECT, true); } } diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 0f4845a32c0..3876033eaaa 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1036,10 +1036,14 @@ static BezTriple *cycle_offset_triple( return out; } -/* This function recalculates the handles of an F-Curve - * If the BezTriples have been rearranged, sort them first before using this. +/** + * Variant of #calchandles_fcurve() that allows calculating based on a different select flag. + * + * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`, + * but may want to use a different one at times (if caller does not operate on + * selection). */ -void calchandles_fcurve(FCurve *fcu) +void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) { BezTriple *bezt, *prev, *next; int a = fcu->totvert; @@ -1075,7 +1079,7 @@ void calchandles_fcurve(FCurve *fcu) } /* calculate auto-handles */ - BKE_nurb_handle_calc(bezt, prev, next, true, fcu->auto_smoothing); + BKE_nurb_handle_calc_ex(bezt, prev, next, handle_sel_flag, true, fcu->auto_smoothing); /* for automatic ease in and out */ if (BEZT_IS_AUTOH(bezt) && !cycle) { @@ -1121,7 +1125,29 @@ void calchandles_fcurve(FCurve *fcu) } } -void testhandles_fcurve(FCurve *fcu, const bool use_handle) +/** + * This function recalculates the handles of an F-Curve. Acts based on selection with `SELECT` + * flag. To use a different flag, use #calchandles_fcurve_ex(). + * + * If the BezTriples have been rearranged, sort them first before using this. + */ +void calchandles_fcurve(FCurve *fcu) +{ + calchandles_fcurve_ex(fcu, SELECT); +} + +/** + * Update handles, making sure the handle-types are valid (e.g. correctly deduced from an "Auto" + * type), and recalculating their position vectors. + * Use when something has changed handle positions. + * + * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`, + * but may want to use a different one at times (if caller does not operate on + * selection). + * \param use_handle: Check selection state of individual handles, otherwise always update both + * handles if the key is selected. + */ +void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle) { BezTriple *bezt; unsigned int a; @@ -1133,11 +1159,11 @@ void testhandles_fcurve(FCurve *fcu, const bool use_handle) /* loop over beztriples */ for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { - BKE_nurb_bezt_handle_test(bezt, use_handle); + BKE_nurb_bezt_handle_test(bezt, sel_flag, use_handle); } /* recalculate handles */ - calchandles_fcurve(fcu); + calchandles_fcurve_ex(fcu, sel_flag); } /* This function sorts BezTriples so that they are arranged in chronological order, diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index f8328e9f07f..dc413e936eb 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -501,10 +501,16 @@ void ANIM_editkeyframes_refresh(bAnimContext *ac) ok |= KEYFRAME_OK_KEY; \ \ if (ked && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES)) { \ - if (check(0)) \ - ok |= KEYFRAME_OK_H1; \ - if (check(2)) \ - ok |= KEYFRAME_OK_H2; \ + /* Only act on visible items, so check handle visiblity state. */ \ + const bool handles_visible = ((ked->iterflags & KEYFRAME_ITER_HANDLES_DEFAULT_INVISIBLE) ? \ + (BEZT_ISSEL_ANY(bezt)) : \ + true); \ + if (handles_visible) { \ + if (check(0)) \ + ok |= KEYFRAME_OK_H1; \ + if (check(2)) \ + ok |= KEYFRAME_OK_H2; \ + } \ } \ } \ (void)0 @@ -1054,7 +1060,11 @@ KeyframeEditFunc ANIM_editkeyframes_mirror(short type) /* Sets the selected bezier handles to type 'auto' */ static short set_bezier_auto(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { - if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { + /* If the key is selected, always apply to both handles. */ + if (bezt->f2 & SELECT) { + bezt->h1 = bezt->h2 = HD_AUTO; + } + else { if (bezt->f1 & SELECT) { bezt->h1 = HD_AUTO; } @@ -1064,6 +1074,7 @@ static short set_bezier_auto(KeyframeEditData *UNUSED(ked), BezTriple *bezt) ENSURE_HANDLES_MATCH(bezt); } + return 0; } @@ -1072,7 +1083,11 @@ static short set_bezier_auto(KeyframeEditData *UNUSED(ked), BezTriple *bezt) */ static short set_bezier_auto_clamped(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { - if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { + /* If the key is selected, always apply to both handles. */ + if (bezt->f2 & SELECT) { + bezt->h1 = bezt->h2 = HD_AUTO_ANIM; + } + else { if (bezt->f1 & SELECT) { bezt->h1 = HD_AUTO_ANIM; } @@ -1082,18 +1097,26 @@ static short set_bezier_auto_clamped(KeyframeEditData *UNUSED(ked), BezTriple *b ENSURE_HANDLES_MATCH(bezt); } + return 0; } /* Sets the selected bezier handles to type 'vector' */ static short set_bezier_vector(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { - if (bezt->f1 & SELECT) { - bezt->h1 = HD_VECT; + /* If the key is selected, always apply to both handles. */ + if (bezt->f2 & SELECT) { + bezt->h1 = bezt->h2 = HD_VECT; } - if (bezt->f3 & SELECT) { - bezt->h2 = HD_VECT; + else { + if (bezt->f1 & SELECT) { + bezt->h1 = HD_VECT; + } + if (bezt->f3 & SELECT) { + bezt->h2 = HD_VECT; + } } + return 0; } @@ -1114,24 +1137,38 @@ static short bezier_isfree(KeyframeEditData *UNUSED(ked), BezTriple *bezt) /* Sets selected bezier handles to type 'align' */ static short set_bezier_align(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { - if (bezt->f1 & SELECT) { - bezt->h1 = HD_ALIGN; + /* If the key is selected, always apply to both handles. */ + if (bezt->f2 & SELECT) { + bezt->h1 = bezt->h2 = HD_ALIGN; } - if (bezt->f3 & SELECT) { - bezt->h2 = HD_ALIGN; + else { + if (bezt->f1 & SELECT) { + bezt->h1 = HD_ALIGN; + } + if (bezt->f3 & SELECT) { + bezt->h2 = HD_ALIGN; + } } + return 0; } /* Sets selected bezier handles to type 'free' */ static short set_bezier_free(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { - if (bezt->f1 & SELECT) { - bezt->h1 = HD_FREE; + /* If the key is selected, always apply to both handles. */ + if (bezt->f2 & SELECT) { + bezt->h1 = bezt->h2 = HD_FREE; } - if (bezt->f3 & SELECT) { - bezt->h2 = HD_FREE; + else { + if (bezt->f1 & SELECT) { + bezt->h1 = HD_FREE; + } + if (bezt->f3 & SELECT) { + bezt->h2 = HD_FREE; + } } + return 0; } @@ -1422,8 +1459,13 @@ KeyframeEditFunc ANIM_editkeyframes_easing(short mode) static short select_bezier_add(KeyframeEditData *ked, BezTriple *bezt) { + /* Only act on visible items, so check handle visiblity state. */ + const bool handles_visible = ked && ((ked->iterflags & KEYFRAME_ITER_HANDLES_DEFAULT_INVISIBLE) ? + (BEZT_ISSEL_ANY(bezt)) : + true); + /* if we've got info on what to select, use it, otherwise select all */ - if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES)) { + if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible) { if (ked->curflags & KEYFRAME_OK_KEY) { bezt->f2 |= SELECT; } @@ -1443,8 +1485,13 @@ static short select_bezier_add(KeyframeEditData *ked, BezTriple *bezt) static short select_bezier_subtract(KeyframeEditData *ked, BezTriple *bezt) { + /* Only act on visible items, so check handle visiblity state. */ + const bool handles_visible = ked && ((ked->iterflags & KEYFRAME_ITER_HANDLES_DEFAULT_INVISIBLE) ? + (BEZT_ISSEL_ANY(bezt)) : + true); + /* if we've got info on what to deselect, use it, otherwise deselect all */ - if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES)) { + if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible) { if (ked->curflags & KEYFRAME_OK_KEY) { bezt->f2 &= ~SELECT; } diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 621a325eabd..8181cebfe3c 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -141,6 +141,12 @@ typedef enum eKeyframeIterFlags { /* Perform NLA time remapping (global -> strip) for the "f2" parameter */ KED_F2_NLA_UNMAP = (1 << 2), + + /* Set this when handles aren't visible by default and you want to perform additional checks to + * get the actual visibility state. E.g. in some cases handles are only drawn if either a handle + * or their control point is selected. The selection state will have to be checked in the + * iterator callbacks then. */ + KEYFRAME_ITER_HANDLES_DEFAULT_INVISIBLE = (1 << 3), } eKeyframeIterFlags; /* --- Generic Properties for Keyframe Edit Tools ----- */ diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 33cb1cb0075..6c95e74e834 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -281,7 +281,7 @@ static void graphedit_activekey_handles_cb(bContext *C, void *fcu_ptr, void *bez bezt->h2 = HD_ALIGN; } else { - BKE_nurb_bezt_handle_test(bezt, true); + BKE_nurb_bezt_handle_test(bezt, SELECT, true); } /* now call standard updates */ diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 4bd738fca95..9c1d63ef68a 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -552,6 +552,10 @@ static void box_select_graphkeys(bAnimContext *ac, ked.data = &scaled_rectf; } + if (sipo->flag & SIPO_SELVHANDLESONLY) { + ked.iterflags |= KEYFRAME_ITER_HANDLES_DEFAULT_INVISIBLE; + } + /* treat handles separately? */ if (incl_handles) { ked.iterflags |= KEYFRAME_ITER_INCL_HANDLES; @@ -722,7 +726,7 @@ void GRAPH_OT_select_box(wmOperatorType *ot) ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", ""); RNA_def_boolean(ot->srna, "include_handles", - 0, + true, "Include Handles", "Are handles tested individually against the selection criteria"); @@ -1417,23 +1421,42 @@ void GRAPH_OT_select_leftright(wmOperatorType *ot) */ /* option 1) select keyframe directly under mouse */ -static void mouse_graph_keys(bAnimContext *ac, - const int mval[2], - short select_mode, - const bool deselect_all, - const bool curves_only) +static int mouse_graph_keys(bAnimContext *ac, + const int mval[2], + eEditKeyframes_Select select_mode, + const bool deselect_all, + const bool curves_only, + bool wait_to_deselect_others) { SpaceGraph *sipo = (SpaceGraph *)ac->sl; tNearestVertInfo *nvi; BezTriple *bezt = NULL; + bool run_modal = false; /* find the beztriple that we're selecting, and the handle that was clicked on */ nvi = find_nearest_fcurve_vert(ac, mval); + if (select_mode != SELECT_REPLACE) { + /* The modal execution to delay deselecting other items is only needed for normal click + * selection, i.e. for SELECT_REPLACE. */ + wait_to_deselect_others = false; + } + + sipo->runtime.flag &= ~(SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT | + SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT); + + const bool already_selected = + (nvi != NULL) && (((nvi->hpoint == NEAREST_HANDLE_KEY) && (nvi->bezt->f2 & SELECT)) || + ((nvi->hpoint == NEAREST_HANDLE_LEFT) && (nvi->bezt->f1 & SELECT)) || + ((nvi->hpoint == NEAREST_HANDLE_RIGHT) && (nvi->bezt->f3 & SELECT))); + + if (wait_to_deselect_others && nvi && already_selected) { + run_modal = true; + } /* For replacing selection, if we have something to select, we have to clear existing selection. * The same goes if we found nothing to select, and deselect_all is true * (deselect on nothing behavior). */ - if ((nvi != NULL && select_mode == SELECT_REPLACE) || (nvi == NULL && deselect_all)) { + else if ((nvi != NULL && select_mode == SELECT_REPLACE) || (nvi == NULL && deselect_all)) { /* reset selection mode */ select_mode = SELECT_ADD; @@ -1450,7 +1473,7 @@ static void mouse_graph_keys(bAnimContext *ac, } if (nvi == NULL) { - return; + return deselect_all ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } /* if points can be selected on this F-Curve */ @@ -1461,17 +1484,9 @@ static void mouse_graph_keys(bAnimContext *ac, bezt = nvi->bezt; /* used to check bezt seletion is set */ /* depends on selection mode */ if (select_mode == SELECT_INVERT) { - /* keyframe - invert select of all */ if (nvi->hpoint == NEAREST_HANDLE_KEY) { - if (BEZT_ISSEL_ANY(bezt)) { - BEZT_DESEL_ALL(bezt); - } - else { - BEZT_SEL_ALL(bezt); - } + bezt->f2 ^= SELECT; } - - /* handles - toggle selection of relevant handle */ else if (nvi->hpoint == NEAREST_HANDLE_LEFT) { /* toggle selection */ bezt->f1 ^= SELECT; @@ -1482,11 +1497,9 @@ static void mouse_graph_keys(bAnimContext *ac, } } else { - /* if the keyframe was clicked on, select all verts of given beztriple */ if (nvi->hpoint == NEAREST_HANDLE_KEY) { - BEZT_SEL_ALL(bezt); + bezt->f2 |= SELECT; } - /* otherwise, select the handle that applied */ else if (nvi->hpoint == NEAREST_HANDLE_LEFT) { bezt->f1 |= SELECT; } @@ -1547,8 +1560,17 @@ static void mouse_graph_keys(bAnimContext *ac, ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, nvi->ctype); } + if (nvi->hpoint == NEAREST_HANDLE_LEFT) { + sipo->runtime.flag |= SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT; + } + else if (nvi->hpoint == NEAREST_HANDLE_RIGHT) { + sipo->runtime.flag |= SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT; + } + /* free temp sample data for filtering */ MEM_freeN(nvi); + + return run_modal ? OPERATOR_RUNNING_MODAL : OPERATOR_FINISHED; } /* Option 2) Selects all the keyframes on either side of the current frame @@ -1556,11 +1578,15 @@ static void mouse_graph_keys(bAnimContext *ac, /* (see graphkeys_select_leftright) */ /* Option 3) Selects all visible keyframes in the same frame as the mouse click */ -static void graphkeys_mselect_column(bAnimContext *ac, const int mval[2], short select_mode) +static int graphkeys_mselect_column(bAnimContext *ac, + const int mval[2], + eEditKeyframes_Select select_mode, + bool wait_to_deselect_others) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; + bool run_modal = false; KeyframeEditFunc select_cb, ok_cb; KeyframeEditData ked; @@ -1572,15 +1598,22 @@ static void graphkeys_mselect_column(bAnimContext *ac, const int mval[2], short /* check if anything to select */ if (nvi == NULL) { - return; + return OPERATOR_CANCELLED; } /* get frame number on which elements should be selected */ // TODO: should we restrict to integer frames only? selx = nvi->frame; - /* if select mode is replace, deselect all keyframes first */ - if (select_mode == SELECT_REPLACE) { + if (select_mode != SELECT_REPLACE) { + /* Doesn't need to deselect anything -> Pass. */ + } + else if (wait_to_deselect_others && (nvi->bezt->f2 & SELECT)) { + run_modal = true; + } + /* If select mode is replace (and we don't do delayed deselection on mouse release), deselect all + * keyframes first. */ + else { /* reset selection mode to add to selection */ select_mode = SELECT_ADD; @@ -1622,12 +1655,14 @@ static void graphkeys_mselect_column(bAnimContext *ac, const int mval[2], short MEM_freeN(nvi); BLI_freelistN(&ked.list); ANIM_animdata_freelist(&anim_data); + + return run_modal ? OPERATOR_RUNNING_MODAL : OPERATOR_FINISHED; } /* ------------------- */ /* handle clicking */ -static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int graphkeys_clickselect_exec(bContext *C, wmOperator *op) { bAnimContext ac; @@ -1639,27 +1674,37 @@ static int graphkeys_clickselect_invoke(bContext *C, wmOperator *op, const wmEve /* select mode is either replace (deselect all, then add) or add/extend */ const short selectmode = RNA_boolean_get(op->ptr, "extend") ? SELECT_INVERT : SELECT_REPLACE; const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + /* See #WM_operator_properties_generic_select() for a detailed description of the how and why of + * this. */ + const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); + int mval[2]; + int ret_val; + + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); /* figure out action to take */ if (RNA_boolean_get(op->ptr, "column")) { /* select all keyframes in the same frame as the one that was under the mouse */ - graphkeys_mselect_column(&ac, event->mval, selectmode); + ret_val = graphkeys_mselect_column(&ac, mval, selectmode, wait_to_deselect_others); } else if (RNA_boolean_get(op->ptr, "curves")) { /* select all keyframes in the same F-Curve as the one under the mouse */ - mouse_graph_keys(&ac, event->mval, selectmode, deselect_all, true); + ret_val = mouse_graph_keys(&ac, mval, selectmode, deselect_all, true, wait_to_deselect_others); } else { /* select keyframe under mouse */ - mouse_graph_keys(&ac, event->mval, selectmode, deselect_all, false); + ret_val = mouse_graph_keys( + &ac, mval, selectmode, deselect_all, false, wait_to_deselect_others); } - /* set notifier that keyframe selection (and also channel selection in some cases) has changed */ + /* set notifier that keyframe selection (and also channel selection in some cases) has + * changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); /* for tweak grab to work */ - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + return ret_val | OPERATOR_PASS_THROUGH; } void GRAPH_OT_clickselect(wmOperatorType *ot) @@ -1672,19 +1717,22 @@ void GRAPH_OT_clickselect(wmOperatorType *ot) ot->description = "Select keyframes by clicking on them"; /* callbacks */ - ot->invoke = graphkeys_clickselect_invoke; ot->poll = graphop_visible_keyframes_poll; + ot->exec = graphkeys_clickselect_exec; + ot->invoke = WM_generic_select_invoke; + ot->modal = WM_generic_select_modal; /* flags */ ot->flag = OPTYPE_UNDO; /* properties */ - prop = RNA_def_boolean( - ot->srna, - "extend", - 0, - "Extend Select", - "Toggle keyframe selection instead of leaving newly selected keyframes only"); // SHIFTKEY + WM_operator_properties_generic_select(ot); + prop = RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend Select", + "Toggle keyframe selection instead of leaving newly selected " + "keyframes only"); // SHIFTKEY RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, @@ -1694,12 +1742,12 @@ void GRAPH_OT_clickselect(wmOperatorType *ot) "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean( - ot->srna, - "column", - 0, - "Column Select", - "Select all keyframes that occur on the same frame as the one under the mouse"); // ALTKEY + prop = RNA_def_boolean(ot->srna, + "column", + 0, + "Column Select", + "Select all keyframes that occur on the same frame as the one under " + "the mouse"); // ALTKEY RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 95ad56a35e6..b8d9d3b791f 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -796,6 +796,9 @@ static void graph_refresh(const bContext *C, ScrArea *sa) ED_area_tag_redraw(sa); } + sipo->runtime.flag &= ~(SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT | + SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT); + /* init/adjust F-Curve colors */ graph_refresh_fcurve_colors(C); } diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index b493bbe8867..f3d26f85471 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -2359,6 +2359,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve /* Needed to translate tweak events to mouse buttons. */ t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1; + t->is_launch_event_tweak = event ? ISTWEAK(event->type) : false; /* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate * (which can have type = 0) */ diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 0264069c368..e5e7be025e4 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -213,7 +213,8 @@ typedef struct TransData2D { float ih1[2], ih2[2]; } TransData2D; -/** Used to store 2 handles for each #TransData in case the other handle wasn't selected. */ +/** Used to store 2 handles for each #TransData in case the other handle wasn't selected. Also to + * unset temporary flags. */ typedef struct TransDataCurveHandleFlags { char ih1, ih2; char *h1, *h2; @@ -631,6 +632,9 @@ typedef struct TransInfo { /*************** NEW STUFF *********************/ /** event type used to launch transform. */ short launch_event; + /** Is the actual launch event a tweak event? (launch_event above is set to the corresponding + * mouse button then.) */ + bool is_launch_event_tweak; struct { /** Orientation type when when we're not constrained. diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 0c49d67f7a2..db8f36883f8 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -921,10 +921,17 @@ typedef struct tRetainedKeyframe { size_t del_count; /* number of keyframes of this sort that have been deleted so far */ } tRetainedKeyframe; -/* Called during special_aftertrans_update to make sure selected keyframes replace +/** + * Called during special_aftertrans_update to make sure selected keyframes replace * any other keyframes which may reside on that frame (that is not selected). + * + * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`, + * but may want to use a different one at times (if caller does not operate on + * selection). */ -static void posttrans_fcurve_clean(FCurve *fcu, const bool use_handle) +static void posttrans_fcurve_clean(FCurve *fcu, + const eBezTriple_Flag sel_flag, + const bool use_handle) { /* NOTE: We assume that all keys are sorted */ ListBase retained_keys = {NULL, NULL}; @@ -1036,7 +1043,7 @@ static void posttrans_fcurve_clean(FCurve *fcu, const bool use_handle) } /* 3) Recalculate handles */ - testhandles_fcurve(fcu, use_handle); + testhandles_fcurve(fcu, sel_flag, use_handle); /* cleanup */ BLI_freelistN(&retained_keys); @@ -1064,11 +1071,11 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act) if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - posttrans_fcurve_clean(ale->key_data, false); /* only use handles in graph editor */ + posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - posttrans_fcurve_clean(ale->key_data, false); /* only use handles in graph editor */ + posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ } } @@ -1090,7 +1097,7 @@ typedef struct BeztMap { /* This function converts an FCurve's BezTriple array to a BeztMap array * NOTE: this allocates memory that will need to get freed later */ -static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert, const short UNUSED(use_handle)) +static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert) { BezTriple *bezt = bezts; BezTriple *prevbezt = NULL; @@ -1118,7 +1125,7 @@ static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert, const short UNUS } /* This function copies the code of sort_time_ipocurve, but acts on BeztMap structs instead */ -static void sort_time_beztmaps(BeztMap *bezms, int totvert, const short UNUSED(use_handle)) +static void sort_time_beztmaps(BeztMap *bezms, int totvert) { BeztMap *bezm; int i, ok = 1; @@ -1163,8 +1170,7 @@ static void sort_time_beztmaps(BeztMap *bezms, int totvert, const short UNUSED(u } /* This function firstly adjusts the pointers that the transdata has to each BezTriple */ -static void beztmap_to_data( - TransInfo *t, FCurve *fcu, BeztMap *bezms, int totvert, const short UNUSED(use_handle)) +static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totvert) { BezTriple *bezts = fcu->bezt; BeztMap *bezm; @@ -1270,9 +1276,9 @@ void remake_graph_transdata(TransInfo *t, ListBase *anim_data) /* adjust transform-data pointers */ /* note, none of these functions use 'use_handle', it could be removed */ - bezm = bezt_to_beztmaps(fcu->bezt, fcu->totvert, use_handle); - sort_time_beztmaps(bezm, fcu->totvert, use_handle); - beztmap_to_data(t, fcu, bezm, fcu->totvert, use_handle); + bezm = bezt_to_beztmaps(fcu->bezt, fcu->totvert); + sort_time_beztmaps(bezm, fcu->totvert); + beztmap_to_data(t, fcu, bezm, fcu->totvert); /* free mapping stuff */ MEM_freeN(bezm); @@ -1281,7 +1287,7 @@ void remake_graph_transdata(TransInfo *t, ListBase *anim_data) sort_time_fcurve(fcu); /* make sure handles are all set correctly */ - testhandles_fcurve(fcu, use_handle); + testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle); } } } @@ -1959,11 +1965,11 @@ void special_aftertrans_update(bContext *C, TransInfo *t) if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { if (adt) { ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - posttrans_fcurve_clean(fcu, false); /* only use handles in graph editor */ + posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); } else { - posttrans_fcurve_clean(fcu, false); /* only use handles in graph editor */ + posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ } } } @@ -2103,11 +2109,11 @@ void special_aftertrans_update(bContext *C, TransInfo *t) if ((sipo->flag & SIPO_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { if (adt) { ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - posttrans_fcurve_clean(fcu, use_handle); + posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); } else { - posttrans_fcurve_clean(fcu, use_handle); + posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); } } } diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index f3d7592127c..f6f982e854a 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -1,4 +1,4 @@ -/* +/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -156,21 +156,55 @@ static bool graph_edit_use_local_center(TransInfo *t) return ((t->around == V3D_AROUND_LOCAL_ORIGINS) && (graph_edit_is_translation_mode(t) == false)); } +/** + * Get the effective selection of a triple for transform, i.e. return if the left handle, right + * handle and/or the center point should be affected by transform. + */ +static void graph_bezt_get_transform_selection(const TransInfo *t, + const BezTriple *bezt, + const bool use_handle, + bool *r_left_handle, + bool *r_key, + bool *r_right_handle) +{ + SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first; + bool key = (bezt->f2 & SELECT) != 0; + bool left = use_handle ? ((bezt->f1 & SELECT) != 0) : key; + bool right = use_handle ? ((bezt->f3 & SELECT) != 0) : key; + + if (use_handle && t->is_launch_event_tweak) { + if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT) { + key = right = false; + } + else if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT) { + left = key = false; + } + } + + /* Whenever we move the key, we also move both handles. */ + if (key) { + left = right = true; + } + + *r_key = key; + *r_left_handle = left; + *r_right_handle = right; +} + static void graph_key_shortest_dist( TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle) { int j = 0; TransData *td_iter = td_start; + bool sel_key, sel_left, sel_right; td->dist = FLT_MAX; for (; j < fcu->totvert; j++) { BezTriple *bezt = fcu->bezt + j; if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const bool sel2 = (bezt->f2 & SELECT) != 0; - const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; - const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right); - if (sel1 || sel2 || sel3) { + if (sel_left || sel_key || sel_right) { td->dist = td->rdist = min_ff(td->dist, fabs(td_iter->center[0] - td->center[0])); } @@ -179,6 +213,15 @@ static void graph_key_shortest_dist( } } +/** + * It is important to note that this doesn't always act on the selection (like it's usually done), + * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a + * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the + * selected left or right handles accordingly. + * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve + * functions may need to be made aware of this. It's ugly that these act based on selection state + * anyway. + */ void createTransGraphEditData(bContext *C, TransInfo *t) { SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first; @@ -198,11 +241,11 @@ void createTransGraphEditData(bContext *C, TransInfo *t) BezTriple *bezt; int count = 0, i; float mtx[3][3], smtx[3][3]; - const bool is_translation_mode = graph_edit_is_translation_mode(t); const bool use_handle = !(sipo->flag & SIPO_NOHANDLES); const bool use_local_center = graph_edit_use_local_center(t); const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS; + bool sel_key, sel_left, sel_right; /* determine what type of data we are operating on */ if (ANIM_animdata_get_context(C, &ac) == 0) { @@ -253,33 +296,29 @@ void createTransGraphEditData(bContext *C, TransInfo *t) cfra = (float)CFRA; } - /* Only include BezTriples whose 'keyframe' - * occurs on the same side of the current frame as mouse. */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + /* Only include BezTriples whose 'keyframe' + * occurs on the same side of the current frame as mouse. */ if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const bool sel2 = (bezt->f2 & SELECT) != 0; - const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; - const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right); if (is_prop_edit) { curvecount += 3; - if (sel2 || sel1 || sel3) { + if (sel_key || sel_left || sel_right) { selected = true; } } else { - if (!is_translation_mode || !(sel2)) { - if (sel1) { - count++; - } + if (sel_left) { + count++; + } - if (sel3) { - count++; - } + if (sel_right) { + count++; } /* only include main vert if selected */ - if (sel2 && !use_local_center) { + if (sel_key && !use_local_center) { count++; } } @@ -366,19 +405,21 @@ void createTransGraphEditData(bContext *C, TransInfo *t) unit_scale = ANIM_unit_mapping_get_factor( ac.scene, ale->id, ale->key_data, anim_map_flag, &offset); - /* only include BezTriples whose 'keyframe' occurs on the same side - * of the current frame as mouse (if applicable) */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { - if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const bool sel2 = (bezt->f2 & SELECT) != 0; - const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; - const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + /* Ensure temp flag is cleared for all triples, we use it. */ + bezt->f1 &= ~BEZT_FLAG_TEMP_TAG; + bezt->f2 &= ~BEZT_FLAG_TEMP_TAG; + bezt->f3 &= ~BEZT_FLAG_TEMP_TAG; + /* only include BezTriples whose 'keyframe' occurs on the same side + * of the current frame as mouse (if applicable) */ + if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { TransDataCurveHandleFlags *hdata = NULL; - /* short h1=1, h2=1; */ /* UNUSED */ + + graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right); if (is_prop_edit) { - bool is_sel = (sel2 || sel1 || sel3); + bool is_sel = (sel_key || sel_left || sel_right); /* we always select all handles for proportional editing if central handle is selected */ initTransDataCurveHandles(td, bezt); bezt_to_transdata(td++, @@ -422,69 +463,69 @@ void createTransGraphEditData(bContext *C, TransInfo *t) smtx, unit_scale, offset); + + if (is_sel) { + bezt->f1 |= BEZT_FLAG_TEMP_TAG; + bezt->f2 |= BEZT_FLAG_TEMP_TAG; + bezt->f3 |= BEZT_FLAG_TEMP_TAG; + } } else { /* only include handles if selected, irrespective of the interpolation modes. * also, only treat handles specially if the center point isn't selected. */ - if (!is_translation_mode || !(sel2)) { - if (sel1) { - hdata = initTransDataCurveHandles(td, bezt); - bezt_to_transdata(td++, - td2d++, - tdg++, - adt, - bezt, - 0, - sel1, - true, - intvals, - mtx, - smtx, - unit_scale, - offset); - } - else { - /* h1 = 0; */ /* UNUSED */ - } + if (sel_left) { + hdata = initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, + td2d++, + tdg++, + adt, + bezt, + 0, + sel_left, + true, + intvals, + mtx, + smtx, + unit_scale, + offset); + bezt->f1 |= BEZT_FLAG_TEMP_TAG; + } - if (sel3) { - if (hdata == NULL) { - hdata = initTransDataCurveHandles(td, bezt); - } - bezt_to_transdata(td++, - td2d++, - tdg++, - adt, - bezt, - 2, - sel3, - true, - intvals, - mtx, - smtx, - unit_scale, - offset); - } - else { - /* h2 = 0; */ /* UNUSED */ + if (sel_right) { + if (hdata == NULL) { + hdata = initTransDataCurveHandles(td, bezt); } + bezt_to_transdata(td++, + td2d++, + tdg++, + adt, + bezt, + 2, + sel_right, + true, + intvals, + mtx, + smtx, + unit_scale, + offset); + bezt->f3 |= BEZT_FLAG_TEMP_TAG; } /* only include main vert if selected */ - if (sel2 && !use_local_center) { + if (sel_key && !use_local_center) { /* move handles relative to center */ - if (is_translation_mode) { - if (sel1) { + if (graph_edit_is_translation_mode(t)) { + if (sel_left) { td->flag |= TD_MOVEHANDLE1; } - if (sel3) { + if (sel_right) { td->flag |= TD_MOVEHANDLE2; } } /* if handles were not selected, store their selection status */ - if (!(sel1) || !(sel3)) { + if (!(sel_left) || !(sel_right)) { if (hdata == NULL) { hdata = initTransDataCurveHandles(td, bezt); } @@ -496,13 +537,14 @@ void createTransGraphEditData(bContext *C, TransInfo *t) adt, bezt, 1, - sel2, + sel_key, false, intvals, mtx, smtx, unit_scale, offset); + bezt->f2 |= BEZT_FLAG_TEMP_TAG; } /* Special hack (must be done after #initTransDataCurveHandles(), * as that stores handle settings to restore...): @@ -513,7 +555,7 @@ void createTransGraphEditData(bContext *C, TransInfo *t) */ if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) && ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) { - if (hdata && (sel1) && (sel3)) { + if (hdata && (sel_left) && (sel_right)) { bezt->h1 = HD_ALIGN; bezt->h2 = HD_ALIGN; } @@ -523,7 +565,7 @@ void createTransGraphEditData(bContext *C, TransInfo *t) } /* Sets handles based on the selection */ - testhandles_fcurve(fcu, use_handle); + testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle); } if (is_prop_edit) { @@ -551,15 +593,13 @@ void createTransGraphEditData(bContext *C, TransInfo *t) cfra = (float)CFRA; } - /* only include BezTriples whose 'keyframe' occurs on the - * same side of the current frame as mouse (if applicable) */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + /* only include BezTriples whose 'keyframe' occurs on the + * same side of the current frame as mouse (if applicable) */ if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const bool sel2 = (bezt->f2 & SELECT) != 0; - const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; - const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right); - if (sel1 || sel2) { + if (sel_left || sel_key) { td->dist = td->rdist = 0.0f; } else { @@ -567,7 +607,7 @@ void createTransGraphEditData(bContext *C, TransInfo *t) } td++; - if (sel2) { + if (sel_key) { td->dist = td->rdist = 0.0f; } else { @@ -575,7 +615,7 @@ void createTransGraphEditData(bContext *C, TransInfo *t) } td++; - if (sel3 || sel2) { + if (sel_right || sel_key) { td->dist = td->rdist = 0.0f; } else { diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 0df0b70a56a..548944e2d74 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -448,7 +448,7 @@ static void recalcData_graphedit(TransInfo *t) dosort++; } else { - calchandles_fcurve(fcu); + calchandles_fcurve_ex(fcu, BEZT_FLAG_TEMP_TAG); } /* set refresh tags for objects using this animation, diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 8670d8f6633..576a0a85b6d 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -483,6 +483,13 @@ typedef enum eGraphEdit_Runtime_Flag { SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC = (1 << 0), /** Temporary flag to force fcurves to recalculate colors. */ SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR = (1 << 1), + + /** + * These flags are for the mouse-select code to communicate with the transform code. Click + * dragging (tweaking) a handle sets the according left/right flag which transform code uses then + * to limit translation to this side. */ + SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT = (1 << 2), + SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT = (1 << 3), } eGraphEdit_Runtime_Flag; /** \} */ diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 21636153904..72646718e41 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -396,8 +396,38 @@ void WM_operator_properties_select_operation_simple(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** + * Selecting and tweaking items are overlapping operations. Getting both to work without conflicts + * requires special care. See + * https://wiki.blender.org/wiki/Human_Interface_Guidelines/Selection#Select-tweaking for the + * desired behavior. + * + * For default click selection (with no modifier keys held), the select operators can do the + * following: + * - On a mouse press on an unselected item, change selection and finish immidiately after. + * This sends an undo push and allows transform to take over should a tweak event be caught now. + * - On a mouse press on a selected item, don't change selection state, but start modal execution + * of the operator. Idea is that we wait with deselecting other items until we know that the + * intention wasn't to tweak (mouse press+drag) all selected items. + * - If a tweak is recognized before the release event happens, cancel the operator, so that + * transform can take over and no undo-push is sent. + * - If the release event occurs rather than a tweak one, deselect all items but the one under the + * cursor, and finish the modal operator. + * + * This utility, together with #WM_generic_select_invoke() and #WM_generic_select_modal() should + * help getting the wanted behavior to work. Most generic logic should be handled in these, so that + * the select operators only have to care for the case dependent handling. + * + * Every select operator has slightly diferent requirements, e.g. VSE strip selection also needs to + * account for handle selection. This should be the baseline behavior though. + */ void WM_operator_properties_generic_select(wmOperatorType *ot) { + /* On the initial mouse press, this is set by #WM_generic_select_modal() to let the select + * operator exec callback know that it should not __yet__ deselect other items when clicking on + * an already selected one. Instead should make sure the operator executes modal then (see + * #WM_generic_select_modal()), so that the exec callback can be called a second time on the + * mouse release event to do this part. */ PropertyRNA *prop = RNA_def_boolean( ot->srna, "wait_to_deselect_others", false, "Wait to Deselect Others", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 83551a86211..9c3c433d675 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -721,6 +721,13 @@ void WM_operator_properties_free(PointerRNA *ptr) /** \name Default Operator Callbacks * \{ */ +/** + * Helper to get select and tweak-transform to work conflict free and as desired. See + * #WM_operator_properties_generic_select() for details. + * + * To be used together with #WM_generic_select_invoke() and + * #WM_operator_properties_generic_select(). + */ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event) { PropertyRNA *wait_to_deselect_prop = RNA_struct_find_property(op->ptr, @@ -787,6 +794,13 @@ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; } +/** + * Helper to get select and tweak-transform to work conflict free and as desired. See + * #WM_operator_properties_generic_select() for details. + * + * To be used together with #WM_generic_select_modal() and + * #WM_operator_properties_generic_select(). + */ int WM_generic_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { RNA_int_set(op->ptr, "mouse_x", event->mval[0]); |