Allow select range in animation editor #104565
|
@ -3463,7 +3463,9 @@ def km_animation_channels(params):
|
|||
items.extend([
|
||||
# Click select.
|
||||
("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
|
||||
("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
|
||||
{"properties": [("extend_range", True)]}),
|
||||
("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
|
||||
{"properties": [("extend", True)]}),
|
||||
("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
|
||||
{"properties": [("children_only", True)]}),
|
||||
|
|
|
@ -214,7 +214,64 @@ void ANIM_set_active_channel(bAnimContext *ac,
|
|||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
static void select_pchan_for_action_group(bAnimContext *ac, bActionGroup *agrp, bAnimListElem *ale)
|
||||
bool ANIM_is_active_channel(bAnimListElem *ale)
|
||||
|
||||
{
|
||||
switch (ale->type) {
|
||||
dr.sybren marked this conversation as resolved
Sybren A. Stüvel
commented
You don't need this You don't need this `bool` variable. Since it's only set once, never changes, and then is used to return a value, you can replace all the `is_active_found = ...` with `return ...`.
|
||||
case ANIMTYPE_FILLACTD: /* Action Expander */
|
||||
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
|
||||
case ANIMTYPE_DSLAM:
|
||||
case ANIMTYPE_DSCAM:
|
||||
case ANIMTYPE_DSCACHEFILE:
|
||||
case ANIMTYPE_DSCUR:
|
||||
case ANIMTYPE_DSSKEY:
|
||||
case ANIMTYPE_DSWOR:
|
||||
case ANIMTYPE_DSPART:
|
||||
case ANIMTYPE_DSMBALL:
|
||||
case ANIMTYPE_DSARM:
|
||||
case ANIMTYPE_DSMESH:
|
||||
case ANIMTYPE_DSNTREE:
|
||||
case ANIMTYPE_DSTEX:
|
||||
case ANIMTYPE_DSLAT:
|
||||
case ANIMTYPE_DSLINESTYLE:
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
case ANIMTYPE_DSMCLIP:
|
||||
case ANIMTYPE_DSHAIR:
|
||||
case ANIMTYPE_DSPOINTCLOUD:
|
||||
case ANIMTYPE_DSVOLUME:
|
||||
case ANIMTYPE_NLAACTION:
|
||||
case ANIMTYPE_DSSIMULATION: {
|
||||
if (ale->adt) {
|
||||
return ale->adt->flag & ADT_UI_ACTIVE;
|
||||
}
|
||||
}
|
||||
case ANIMTYPE_GROUP: {
|
||||
bActionGroup *argp = (bActionGroup *)ale->data;
|
||||
return argp->flag & AGRP_ACTIVE;
|
||||
}
|
||||
case ANIMTYPE_FCURVE:
|
||||
case ANIMTYPE_NLACURVE: {
|
||||
FCurve *fcu = (FCurve *)ale->data;
|
||||
return fcu->flag & FCURVE_ACTIVE;
|
||||
}
|
||||
case ANIMTYPE_GPLAYER: {
|
||||
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
||||
return gpl->flag & GP_LAYER_ACTIVE;
|
||||
}
|
||||
/* These channel types do not have active flags. */
|
||||
Sybren A. Stüvel
commented
This This `return` is still in the wrong place...
Sybren A. Stüvel
commented
👍 now it's instantly clear that these were not forgotten. 👍 now it's instantly clear that these were not forgotten.
|
||||
case ANIMTYPE_MASKLAYER:
|
||||
case ANIMTYPE_SHAPEKEY:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
Sybren A. Stüvel
commented
This line is never reached, and the function has no Just end the function with This line is never reached, and the function has no `return` statement for when there is no matching `case`.
Just end the function with `return false;`
|
||||
}
|
||||
|
||||
/* change_active determines whether to change the active bone of the armature when selecting pose
|
||||
* channels. It is false during range selection otherwise true. */
|
||||
static void select_pchan_for_action_group(bAnimContext *ac,
|
||||
bActionGroup *agrp,
|
||||
bAnimListElem *ale,
|
||||
const bool change_active)
|
||||
{
|
||||
/* Armatures-Specific Feature:
|
||||
* See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (#38737)
|
||||
|
@ -230,11 +287,12 @@ static void select_pchan_for_action_group(bAnimContext *ac, bActionGroup *agrp,
|
|||
* TODO: check the first F-Curve or so to be sure...
|
||||
*/
|
||||
bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
|
||||
|
||||
if (agrp->flag & AGRP_SELECTED) {
|
||||
ED_pose_bone_select(ob, pchan, true);
|
||||
ED_pose_bone_select(ob, pchan, true, change_active);
|
||||
}
|
||||
else {
|
||||
ED_pose_bone_select(ob, pchan, false);
|
||||
ED_pose_bone_select(ob, pchan, false, change_active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -342,10 +400,15 @@ static void anim_channels_select_set(bAnimContext *ac,
|
|||
eAnimChannels_SetFlag sel)
|
||||
{
|
||||
bAnimListElem *ale;
|
||||
/* Boolean to keep active channel status during range selection. */
|
||||
const bool change_active = (sel != ACHANNEL_SETFLAG_EXTEND_RANGE);
|
||||
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
switch (ale->type) {
|
||||
case ANIMTYPE_SCENE: {
|
||||
if (change_active) {
|
||||
break;
|
||||
}
|
||||
Scene *scene = (Scene *)ale->data;
|
||||
|
||||
ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
|
||||
|
@ -372,8 +435,10 @@ static void anim_channels_select_set(bAnimContext *ac,
|
|||
case ANIMTYPE_GROUP: {
|
||||
bActionGroup *agrp = (bActionGroup *)ale->data;
|
||||
ACHANNEL_SET_FLAG(agrp, sel, AGRP_SELECTED);
|
||||
select_pchan_for_action_group(ac, agrp, ale);
|
||||
agrp->flag &= ~AGRP_ACTIVE;
|
||||
select_pchan_for_action_group(ac, agrp, ale, change_active);
|
||||
if (change_active) {
|
||||
agrp->flag &= ~AGRP_ACTIVE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ANIMTYPE_FCURVE:
|
||||
|
@ -381,7 +446,7 @@ static void anim_channels_select_set(bAnimContext *ac,
|
|||
FCurve *fcu = (FCurve *)ale->data;
|
||||
|
||||
ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED);
|
||||
if ((fcu->flag & FCURVE_SELECTED) == 0) {
|
||||
if (!(fcu->flag & FCURVE_SELECTED) && change_active) {
|
||||
/* Only erase the ACTIVE flag when deselecting. This ensures that "select all curves"
|
||||
* retains the currently active curve. */
|
||||
fcu->flag &= ~FCURVE_ACTIVE;
|
||||
|
@ -428,7 +493,9 @@ static void anim_channels_select_set(bAnimContext *ac,
|
|||
/* need to verify that this data is valid for now */
|
||||
if (ale->adt) {
|
||||
ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
|
||||
ale->adt->flag &= ~ADT_UI_ACTIVE;
|
||||
if (change_active) {
|
||||
ale->adt->flag &= ~ADT_UI_ACTIVE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2747,7 +2814,7 @@ static void box_select_anim_channels(bAnimContext *ac, rcti *rect, short selectm
|
|||
switch (ale->type) {
|
||||
case ANIMTYPE_GROUP: {
|
||||
bActionGroup *agrp = (bActionGroup *)ale->data;
|
||||
select_pchan_for_action_group(ac, agrp, ale);
|
||||
select_pchan_for_action_group(ac, agrp, ale, true);
|
||||
/* always clear active flag after doing this */
|
||||
agrp->flag &= ~AGRP_ACTIVE;
|
||||
break;
|
||||
|
@ -3027,6 +3094,70 @@ static int click_select_channel_scene(bAnimListElem *ale,
|
|||
return (ND_ANIMCHAN | NA_SELECTED);
|
||||
}
|
||||
|
||||
/* Return whether active channel of given type is present. */
|
||||
static bool animchannel_has_active_of_type(bAnimContext *ac, const eAnim_ChannelType type)
|
||||
{
|
||||
ListBase anim_data = anim_channels_for_selection(ac);
|
||||
bool is_active_found = false;
|
||||
|
||||
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
|
||||
if (ale->type != type) {
|
||||
continue;
|
||||
}
|
||||
is_active_found = ANIM_is_active_channel(ale);
|
||||
if (is_active_found) {
|
||||
break;
|
||||
Sybren A. Stüvel
commented
This function is now very similar to If there is still need for this function, document well what the differences are, and why these differences are relevant. Also the This function is now very similar to `anim_channels_select_set()`, except that that one also handles some other types, for example `ANIMTYPE_SCENE` and `ANIMTYPE_NLATRACK`.
If there is still need for this function, document well what the differences are, and why these differences are relevant.
Also the `switch` should have a `case` for all items in `eAnim_ChannelType`. Without that, a compiler can complain that there are missing items (which is a good mechanic to detect issues when a new enum item is added, so silencing the warning with a `default:` case is usually not recommended).
Pratik Borhade
commented
Right. I mentioned this earlier about replacing > This function is now very similar to anim_channels_select_set(), except that that one also handles some other types, for example ANIMTYPE_SCENE and ANIMTYPE_NLATRACK
Right. I mentioned this earlier about replacing `animchannel_clear_selection` with the existing function: https://projects.blender.org/blender/blender/pulls/104565#issuecomment-886697 :)
Pratik Borhade
commented
make sense to add switch case for other channels when clearing selection flag. > Also the switch should have a case for all items in eAnim_ChannelType
make sense to add switch case for other channels when clearing selection flag.
But do you agree about replacing clear_selection function with `anim_channels_select_set`?
Sybren A. Stüvel
commented
Yeah, for sure! 👍 Yeah, for sure! :+1:
Pratik Borhade
commented
Thanks, replaced it with the existing function (with minor changes). Thanks, replaced it with the existing function (with minor changes).
|
||||
}
|
||||
}
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return is_active_found;
|
||||
}
|
||||
|
||||
/* Select channels that lies between active channel and cursor_elem. */
|
||||
static void animchannel_select_range(bAnimContext *ac, bAnimListElem *cursor_elem)
|
||||
{
|
||||
ListBase anim_data = anim_channels_for_selection(ac);
|
||||
bool in_selection_range = false;
|
||||
|
||||
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
|
||||
|
||||
/* Allow selection when active channel and `cursor_elem` are of same type. */
|
||||
if (ale->type != cursor_elem->type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_cursor_elem = (ale->data == cursor_elem->data);
|
||||
const bool is_active_elem = ANIM_is_active_channel(ale);
|
||||
|
||||
/* Restrict selection when active element is not found and group-channels are excluded from the
|
||||
* selection. */
|
||||
if (is_active_elem || is_cursor_elem) {
|
||||
/* Select first and last element from the range. Reverse selection status on extremes. */
|
||||
ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, ACHANNEL_SETFLAG_ADD);
|
||||
in_selection_range = !in_selection_range;
|
||||
if (ale->type == ANIMTYPE_GROUP) {
|
||||
select_pchan_for_action_group(ac, (bActionGroup *)ale->data, ale, false);
|
||||
}
|
||||
}
|
||||
else if (in_selection_range) {
|
||||
/* Select elements between the range. */
|
||||
ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_SELECT, ACHANNEL_SETFLAG_ADD);
|
||||
if (ale->type == ANIMTYPE_GROUP) {
|
||||
select_pchan_for_action_group(ac, (bActionGroup *)ale->data, ale, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_active_elem && is_cursor_elem) {
|
||||
/* Selection range is only one element when active channel and clicked channel are same. So
|
||||
* exit out of the loop when this condition is hit. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
dr.sybren marked this conversation as resolved
Sybren A. Stüvel
commented
I think the "last clicked channel" is actually the As an illustration of possible use it's fine to mention this, but then it should be in a second sentence and not the primary definition of what the code does. I think the "last clicked channel" is actually the `cursor_elem` parameter? If so, don't reference UI interaction here, as it's irrelevant to the functionality of this function how that `cursor_elem` was passed.
As an illustration of possible use it's fine to mention this, but then it should be in a second sentence and not the primary definition of what the code does.
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
static int click_select_channel_object(bContext *C,
|
||||
bAnimContext *ac,
|
||||
bAnimListElem *ale,
|
||||
|
@ -3050,8 +3181,13 @@ static int click_select_channel_object(bContext *C,
|
|||
adt->flag ^= ADT_UI_SELECTED;
|
||||
}
|
||||
Sybren A. Stüvel
commented
`is_active_elem` isn't used before this point, so just declare it as `const bool is_active_elem = ANIM_is_active_channel(ale);`
|
||||
}
|
||||
else if (selectmode == SELECT_EXTEND_RANGE) {
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_EXTEND_RANGE);
|
||||
animchannel_select_range(ac, ale);
|
||||
}
|
||||
else {
|
||||
/* deselect all */
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
|
||||
BKE_view_layer_synced_ensure(scene, view_layer);
|
||||
/* TODO: should this deselect all other types of channels too? */
|
||||
LISTBASE_FOREACH (Base *, b, BKE_view_layer_object_bases_get(view_layer)) {
|
||||
|
@ -3074,7 +3210,8 @@ static int click_select_channel_object(bContext *C,
|
|||
* to avoid getting stuck there, see: #48747. */
|
||||
ED_object_base_activate_with_mode_exit_if_needed(C, base); /* adds notifier */
|
||||
dr.sybren marked this conversation as resolved
Outdated
Sybren A. Stüvel
commented
I think I think `selected` is a bit of a confusing name. The fact that something was selected or not is secondary; this variable indicates whether the loop is inside the to-be-selected range. `in_selection_range` would be better.
|
||||
|
||||
if ((adt) && (adt->flag & ADT_UI_SELECTED)) {
|
||||
/* Similar to outliner, do not change active element when selecting elements in range.*/
|
||||
if ((adt) && (adt->flag & ADT_UI_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
|
||||
adt->flag |= ADT_UI_ACTIVE;
|
||||
dr.sybren marked this conversation as resolved
Outdated
Sybren A. Stüvel
commented
Same question as above: why only operate on FCurves? Same question as above: why only operate on FCurves?
|
||||
}
|
||||
|
||||
|
@ -3094,14 +3231,18 @@ static int click_select_channel_dummy(bAnimContext *ac,
|
|||
/* inverse selection status of this AnimData block only */
|
||||
ale->adt->flag ^= ADT_UI_SELECTED;
|
||||
}
|
||||
else if (selectmode == SELECT_EXTEND_RANGE) {
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_EXTEND_RANGE);
|
||||
animchannel_select_range(ac, ale);
|
||||
}
|
||||
else {
|
||||
/* select AnimData block by itself */
|
||||
PratikPB2123 marked this conversation as resolved
Outdated
Sybren A. Stüvel
commented
No No `break` outside the braces, see [the style guide](https://wiki.blender.org/wiki/Style_Guide/C_Cpp#Switch_Statement).
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
|
||||
ale->adt->flag |= ADT_UI_SELECTED;
|
||||
}
|
||||
|
||||
/* set active? */
|
||||
if (ale->adt->flag & ADT_UI_SELECTED) {
|
||||
/* Similar to outliner, do not change active element when selecting elements in range. */
|
||||
if ((ale->adt->flag & ADT_UI_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
|
||||
ale->adt->flag |= ADT_UI_ACTIVE;
|
||||
}
|
||||
|
||||
|
@ -3147,6 +3288,10 @@ static int click_select_channel_group(bAnimContext *ac,
|
|||
/* inverse selection status of this group only */
|
||||
agrp->flag ^= AGRP_SELECTED;
|
||||
}
|
||||
else if (selectmode == SELECT_EXTEND_RANGE) {
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_EXTEND_RANGE);
|
||||
animchannel_select_range(ac, ale);
|
||||
}
|
||||
else if (selectmode == -1) {
|
||||
/* select all in group (and deselect everything else) */
|
||||
FCurve *fcu;
|
||||
|
@ -3173,17 +3318,22 @@ static int click_select_channel_group(bAnimContext *ac,
|
|||
agrp->flag |= AGRP_SELECTED;
|
||||
}
|
||||
|
||||
/* if group is selected now, make group the 'active' one in the visible list */
|
||||
/* if group is selected now, make group the 'active' one in the visible list.
|
||||
* Similar to outliner, do not change active element when selecting elements in range. */
|
||||
if (agrp->flag & AGRP_SELECTED) {
|
||||
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
|
||||
if (pchan) {
|
||||
ED_pose_bone_select(ob, pchan, true);
|
||||
if (selectmode != SELECT_EXTEND_RANGE) {
|
||||
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
|
||||
if (pchan) {
|
||||
ED_pose_bone_select(ob, pchan, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
|
||||
if (pchan) {
|
||||
ED_pose_bone_select(ob, pchan, false);
|
||||
if (selectmode != SELECT_EXTEND_RANGE) {
|
||||
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
|
||||
if (pchan) {
|
||||
ED_pose_bone_select(ob, pchan, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3202,14 +3352,19 @@ static int click_select_channel_fcurve(bAnimContext *ac,
|
|||
/* inverse selection status of this F-Curve only */
|
||||
fcu->flag ^= FCURVE_SELECTED;
|
||||
}
|
||||
else if (selectmode == SELECT_EXTEND_RANGE) {
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_EXTEND_RANGE);
|
||||
animchannel_select_range(ac, ale);
|
||||
}
|
||||
else {
|
||||
/* select F-Curve by itself */
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
|
||||
fcu->flag |= FCURVE_SELECTED;
|
||||
}
|
||||
|
||||
Sybren A. Stüvel
commented
Same questions/remarks as above. Same questions/remarks as above.
|
||||
/* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
|
||||
if (fcu->flag & FCURVE_SELECTED) {
|
||||
/* if F-Curve is selected now, make F-Curve the 'active' one in the visible list.
|
||||
* Similar to outliner, do not change active element when selecting elements in range. */
|
||||
if ((fcu->flag & FCURVE_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
|
||||
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
|
||||
}
|
||||
|
||||
|
@ -3277,14 +3432,19 @@ static int click_select_channel_gplayer(bContext *C,
|
|||
/* invert selection status of this layer only */
|
||||
gpl->flag ^= GP_LAYER_SELECT;
|
||||
}
|
||||
else if (selectmode == SELECT_EXTEND_RANGE) {
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_EXTEND_RANGE);
|
||||
animchannel_select_range(ac, ale);
|
||||
}
|
||||
else {
|
||||
/* select layer by itself */
|
||||
ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR);
|
||||
gpl->flag |= GP_LAYER_SELECT;
|
||||
}
|
||||
|
||||
/* change active layer, if this is selected (since we must always have an active layer) */
|
||||
if (gpl->flag & GP_LAYER_SELECT) {
|
||||
/* change active layer, if this is selected (since we must always have an active layer).
|
||||
* Similar to outliner, do not change active element when selecting elements in range. */
|
||||
if ((gpl->flag & GP_LAYER_SELECT) && (selectmode != SELECT_EXTEND_RANGE)) {
|
||||
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER);
|
||||
/* update other layer status */
|
||||
BKE_gpencil_layer_active_set(gpd, gpl);
|
||||
|
@ -3333,7 +3493,7 @@ static int click_select_channel_masklayer(bAnimContext *ac,
|
|||
static int mouse_anim_channels(bContext *C,
|
||||
bAnimContext *ac,
|
||||
const int channel_index,
|
||||
const short /* eEditKeyframes_Select or -1 */ selectmode)
|
||||
short /* eEditKeyframes_Select or -1 */ selectmode)
|
||||
{
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
|
@ -3371,6 +3531,11 @@ static int mouse_anim_channels(bContext *C,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Change selection mode to single when no active element is found. */
|
||||
if ((selectmode == SELECT_EXTEND_RANGE) && !animchannel_has_active_of_type(ac, ale->type)) {
|
||||
selectmode = SELECT_INVERT;
|
||||
}
|
||||
|
||||
/* action to take depends on what channel we've got */
|
||||
/* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
|
||||
switch (ale->type) {
|
||||
|
@ -3474,6 +3639,9 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmE
|
|||
if (RNA_boolean_get(op->ptr, "extend")) {
|
||||
selectmode = SELECT_INVERT;
|
||||
}
|
||||
else if (RNA_boolean_get(op->ptr, "extend_range")) {
|
||||
selectmode = SELECT_EXTEND_RANGE;
|
||||
}
|
||||
else if (RNA_boolean_get(op->ptr, "children_only")) {
|
||||
/* this is a bit of a special case for ActionGroups only...
|
||||
* should it be removed or extended to all instead? */
|
||||
|
@ -3527,6 +3695,13 @@ static void ANIM_OT_channels_click(wmOperatorType *ot)
|
|||
prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", "");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna,
|
||||
"extend_range",
|
||||
false,
|
||||
"Extend Range",
|
||||
"Selection of active channel to clicked channel");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
/* Key-map: Enable with `Ctrl-Shift`. */
|
||||
prop = RNA_def_boolean(ot->srna, "children_only", false, "Select Children Only", "");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
|
|
@ -92,7 +92,7 @@ void ED_pose_bone_select_tag_update(Object *ob)
|
|||
DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
|
||||
}
|
||||
|
||||
void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
|
||||
void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select, bool change_active)
|
||||
{
|
||||
bArmature *arm;
|
||||
|
||||
|
@ -109,11 +109,15 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
|
|||
/* change selection state - activate too if selected */
|
||||
if (select) {
|
||||
pchan->bone->flag |= BONE_SELECTED;
|
||||
arm->act_bone = pchan->bone;
|
||||
if (change_active) {
|
||||
Sybren A. Stüvel
commented
I think a no-op assignment when
Same below. I think a no-op assignment when `change_active == false` is error-prone. Better to use a bit more code and write:
```c
if (change_active) {
arm->act_bone = pchan->bone;
}
```
Same below.
|
||||
arm->act_bone = pchan->bone;
|
||||
}
|
||||
}
|
||||
else {
|
||||
pchan->bone->flag &= ~BONE_SELECTED;
|
||||
arm->act_bone = NULL;
|
||||
if (change_active) {
|
||||
arm->act_bone = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: select and activate corresponding vgroup? */
|
||||
|
|
|
@ -530,6 +530,8 @@ typedef enum eAnimChannels_SetFlag {
|
|||
ACHANNEL_SETFLAG_INVERT = 2,
|
||||
/** some on -> all off / all on */
|
||||
ACHANNEL_SETFLAG_TOGGLE = 3,
|
||||
/** turn off, keep active flag **/
|
||||
ACHANNEL_SETFLAG_EXTEND_RANGE = 4,
|
||||
} eAnimChannels_SetFlag;
|
||||
|
||||
/* types of settings for AnimChannels */
|
||||
|
@ -696,6 +698,11 @@ void ANIM_set_active_channel(bAnimContext *ac,
|
|||
void *channel_data,
|
||||
eAnim_ChannelType channel_type);
|
||||
|
||||
/**
|
||||
* Return whether channel is active.
|
||||
*/
|
||||
bool ANIM_is_active_channel(bAnimListElem *ale);
|
||||
|
||||
/**
|
||||
* Delete the F-Curve from the given AnimData block (if possible),
|
||||
* as appropriate according to animation context.
|
||||
|
|
|
@ -356,8 +356,13 @@ bool ED_pose_deselect_all(struct Object *ob, int select_mode, bool ignore_visibi
|
|||
void ED_pose_bone_select_tag_update(struct Object *ob);
|
||||
/**
|
||||
* Utility method for changing the selection status of a bone.
|
||||
* change_active determines whether to change the active bone of the armature when selecting pose
|
||||
Sybren A. Stüvel
commented
Document what Document what `change_active` means.
|
||||
* channels. It is false during range selection otherwise true.
|
||||
Sybren A. Stüvel
commented
Remove Remove `const`, it has no meaning in declarations of pass-by-value parameters.
|
||||
*/
|
||||
void ED_pose_bone_select(struct Object *ob, struct bPoseChannel *pchan, bool select);
|
||||
void ED_pose_bone_select(struct Object *ob,
|
||||
struct bPoseChannel *pchan,
|
||||
bool select,
|
||||
bool change_active);
|
||||
|
||||
/* meshlaplacian.cc */
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ typedef enum eEditKeyframes_Select {
|
|||
SELECT_SUBTRACT = (1 << 2),
|
||||
/* flip ok status of keyframes based on key status */
|
||||
SELECT_INVERT = (1 << 3),
|
||||
SELECT_EXTEND_RANGE = (1 << 4),
|
||||
} eEditKeyframes_Select;
|
||||
|
||||
/* "selection map" building modes */
|
||||
|
|
|
@ -345,7 +345,7 @@ bool ED_object_jump_to_bone(bContext *C,
|
|||
|
||||
/* Select it. */
|
||||
ED_pose_deselect_all(ob, SEL_DESELECT, true);
|
||||
ED_pose_bone_select(ob, pchan, true);
|
||||
ED_pose_bone_select(ob, pchan, true, true);
|
||||
|
||||
arm->act_bone = pchan->bone;
|
||||
|
||||
|
|
Document what the
change_active
parameter does. Booleans are notoriously tricky to understand.