View 3D: support for select passthrough when picking selected items

Currently this isn't used in the key-map, it will eventually
allow the 3D viewports tweak tool to match the behavior of other
editors that support tweaking a selection without first de-selecting
all other elements.
This commit is contained in:
Campbell Barton 2022-03-17 14:37:20 +11:00
parent ea0c86e961
commit 1d88aeb95f
Notes: blender-bot 2023-02-14 06:00:44 +01:00
Referenced by commit 4046f61b39, Fix pose-bone menu selecting before activating the menu
11 changed files with 231 additions and 152 deletions

View File

@ -965,14 +965,20 @@ bool ED_armature_edit_select_pick_bone(bContext *C,
}
}
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* Deselect everything. */
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
view_layer, v3d, &bases_len);
ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
changed = true;
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) &&
(ED_armature_ebone_selectflag_get(ebone) & selmask)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
view_layer, v3d, &bases_len);
ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
changed = true;
}
}
if (found) {

View File

@ -136,19 +136,25 @@ bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
}
}
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* Don't use 'BKE_object_pose_base_array_get_unique'
* because we may be selecting from object mode. */
FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
Object *ob_iter = base_iter->object;
if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
ED_pose_bone_select_tag_update(ob_iter);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (bone->flag & BONE_SELECTED)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
/* Don't use 'BKE_object_pose_base_array_get_unique'
* because we may be selecting from object mode. */
FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
Object *ob_iter = base_iter->object;
if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
ED_pose_bone_select_tag_update(ob_iter);
}
}
}
FOREACH_VISIBLE_BASE_END;
changed = true;
}
FOREACH_VISIBLE_BASE_END;
changed = true;
}
if (found) {

View File

@ -4740,23 +4740,29 @@ bool ED_curve_editnurb_select_pick(bContext *C,
ED_view3d_viewcontext_init(C, &vc, depsgraph);
copy_v2_v2_int(vc.mval, mval);
const bool found = ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact);
bool found = ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
ED_curve_deselect_all(((Curve *)ob_iter->data)->editnurb);
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) &&
(((bezt ? (&bezt->f1)[hand] : bp->f1) & SELECT) != 0)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
ED_curve_deselect_all(((Curve *)ob_iter->data)->editnurb);
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
}
MEM_freeN(objects);
changed = true;
}
MEM_freeN(objects);
changed = true;
}
if (found) {

View File

@ -82,6 +82,12 @@ struct SelectPick_Params {
eSelectOp sel_op;
/** Deselect all, even when there is nothing found at the cursor location. */
bool deselect_all;
/**
* When selecting an element that is already selected, do nothing (passthrough).
* don't even make it active.
* Use to implement tweaking to move the selection without first de-selecting.
*/
bool select_passthrough;
};
/**

View File

@ -623,22 +623,27 @@ bool ED_lattice_select_pick(bContext *C, const int mval[2], const struct SelectP
vc.mval[1] = mval[1];
bp = findnearestLattvert(&vc, true, &basact);
const bool found = (bp != NULL);
bool found = (bp != NULL);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
if (ED_lattice_flags_set(ob, 0)) {
DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
}
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (bp->f1 & SELECT)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
if (ED_lattice_flags_set(ob, 0)) {
DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
}
}
MEM_freeN(objects);
changed = true;
}
MEM_freeN(objects);
changed = true;
}
if (found) {

View File

@ -389,8 +389,14 @@ bool paintface_mouse_select(struct bContext *C,
}
}
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
changed |= paintface_deselect_all_visible(C, ob, SEL_DESELECT, false);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (mpoly_sel->flag & ME_FACE_SEL)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
changed |= paintface_deselect_all_visible(C, ob, SEL_DESELECT, false);
}
}
if (found) {

View File

@ -2034,19 +2034,24 @@ bool EDBM_select_pick(bContext *C, const int mval[2], const struct SelectPick_Pa
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
bool changed = false;
const bool found = unified_findnearest(
&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa);
bool found = unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* Deselect everything. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base_iter = bases[base_index];
Object *ob_iter = base_iter->object;
EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT);
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
if (params->sel_op == SEL_OP_SET) {
BMElem *ele = efa ? (BMElem *)efa : (eed ? (BMElem *)eed : (BMElem *)eve);
if ((found && params->select_passthrough) && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base_iter = bases[base_index];
Object *ob_iter = base_iter->object;
EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT);
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
}
changed = true;
}
changed = true;
}
if (found) {

View File

@ -854,9 +854,14 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPic
bool found = ed_mball_findnearest_metaelem(C, mval, true, &base, &ml, &selmask);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* Deselect everything. */
changed |= ED_mball_deselect_all_multi(C);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (ml->flag & SELECT)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
changed |= ED_mball_deselect_all_multi(C);
}
}
if (found) {

View File

@ -1879,10 +1879,16 @@ bool PE_mouse_particles(bContext *C, const int mval[2], const struct SelectPick_
PTCacheEditKey *key;
bool changed = false;
const bool found = pe_nearest_point_and_key(C, mval, &point, &key);
bool found = pe_nearest_point_and_key(C, mval, &point, &key);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
changed |= PE_deselect_all_visible_ex(edit);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (key->flag & PEK_SELECT)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
changed |= PE_deselect_all_visible_ex(edit);
}
}
if (found) {

View File

@ -43,6 +43,7 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_context.h"
#include "BKE_curve.h"
@ -2345,9 +2346,10 @@ static bool ed_object_select_pick(bContext *C,
// TIMEIT_END(select_time);
const bool has_bones = (object && hits > 0) ? false : selectbuffer_has_bones(buffer, hits);
if (hits > 0) {
/* NOTE: bundles are handling in the same way as bones. */
const bool has_bones = object ? false : selectbuffer_has_bones(buffer, hits);
/* NOTE: shift+alt goes to group-flush-selecting. */
if (enumerate) {
@ -2362,75 +2364,86 @@ static bool ed_object_select_pick(bContext *C,
basact = mouse_select_eval_buffer(
&vc, buffer, hits, startbase, has_bones, do_nearest, NULL);
}
}
if (has_bones && basact) {
if (basact->object->type == OB_CAMERA) {
MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
if (clip != NULL && oldbasact == basact) {
bool track_changed = false;
if (((hits > 0) && has_bones) ||
/* Special case, even when there are no hits, pose logic may de-select all bones. */
((hits == 0) && is_pose_mode)) {
for (int i = 0; i < hits; i++) {
const int hitresult = buffer[i].id;
if (basact && (has_bones && (basact->object->type == OB_CAMERA))) {
MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
if (clip != NULL && oldbasact == basact) {
bool track_changed = false;
/* if there's bundles in buffer select bundles first,
* so non-camera elements should be ignored in buffer */
if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) {
continue;
}
for (int i = 0; i < hits; i++) {
const int hitresult = buffer[i].id;
/* index of bundle is 1<<16-based. if there's no "bone" index
* in height word, this buffer value belongs to camera. not to bundle
*/
if (hitresult & 0xFFFF0000) {
const bool extend = params->sel_op == SEL_OP_ADD;
MovieTracking *tracking = &clip->tracking;
ListBase *tracksbase;
MovieTrackingTrack *track;
track = BKE_tracking_track_get_indexed(
&clip->tracking, hitresult >> 16, &tracksbase);
if (TRACK_SELECTED(track) && extend) {
track_changed = false;
BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
}
else {
int oldsel = TRACK_SELECTED(track) ? 1 : 0;
if (!extend) {
deselect_all_tracks(tracking);
}
BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, extend);
if (oldsel != (TRACK_SELECTED(track) ? 1 : 0)) {
track_changed = true;
}
}
ED_object_base_select(basact, BA_SELECT);
changed = true;
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
DEG_id_tag_update(&clip->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
break;
}
/* if there's bundles in buffer select bundles first,
* so non-camera elements should be ignored in buffer */
if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) {
continue;
}
if (!track_changed) {
/* fallback to regular object selection if no new bundles were selected,
* allows to select object parented to reconstruction object */
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest, NULL);
/* index of bundle is 1<<16-based. if there's no "bone" index
* in height word, this buffer value belongs to camera. not to bundle
*/
if (hitresult & 0xFFFF0000) {
const bool extend = params->sel_op == SEL_OP_ADD;
MovieTracking *tracking = &clip->tracking;
ListBase *tracksbase;
MovieTrackingTrack *track;
track = BKE_tracking_track_get_indexed(
&clip->tracking, hitresult >> 16, &tracksbase);
if (TRACK_SELECTED(track) && extend) {
track_changed = false;
BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
}
else {
int oldsel = TRACK_SELECTED(track) ? 1 : 0;
if (!extend) {
deselect_all_tracks(tracking);
}
BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, extend);
if (oldsel != (TRACK_SELECTED(track) ? 1 : 0)) {
track_changed = true;
}
}
ED_object_base_select(basact, BA_SELECT);
changed = true;
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
DEG_id_tag_update(&clip->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
break;
}
}
}
else if (ED_armature_pose_select_pick_with_buffer(
view_layer, v3d, basact, buffer, hits, params, do_nearest)) {
/* then bone is found */
if (!track_changed) {
/* fallback to regular object selection if no new bundles were selected,
* allows to select object parented to reconstruction object */
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest, NULL);
}
}
}
else if (ED_armature_pose_select_pick_with_buffer(view_layer,
v3d,
basact ? basact : (Base *)oldbasact,
buffer,
hits,
params,
do_nearest)) {
/* When there is no `baseact` this will have operated on `oldbasact`,
* no object operations are needed. */
if (basact != NULL) {
/* then bone is found */
/* we make the armature selected:
* not-selected active object in posemode won't work well for tools */
ED_object_base_select(basact, BA_SELECT);
@ -2457,17 +2470,17 @@ static bool ed_object_select_pick(bContext *C,
basact = NULL;
}
}
}
if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
if (is_obedit == false) {
if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
if (object_mode == OB_MODE_OBJECT) {
struct Main *bmain = CTX_data_main(C);
ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
}
if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
basact = NULL;
}
if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
if (is_obedit == false) {
if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
if (object_mode == OB_MODE_OBJECT) {
struct Main *bmain = CTX_data_main(C);
ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
}
if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
basact = NULL;
}
}
}
@ -2490,31 +2503,31 @@ static bool ed_object_select_pick(bContext *C,
/* Ensure code above doesn't change the active base. */
BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL));
bool found = (basact != NULL);
if (vc.obedit) {
/* Edit-mode, pass. */
}
else if (is_pose_mode && (basact == NULL || (basact->object->mode & OB_MODE_POSE))) {
/* Pose-mode, pass (or moved into pose mode). */
if (changed == false) {
/* Pose selection handles this but it wont run if there are no bones under the cursor. */
const bool found = false;
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
changed |= ED_pose_deselect_all_multi(C, SEL_DESELECT, false);
}
}
}
else {
/* Object-mode. */
const bool found = (basact != NULL);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
/* `basact` may be NULL. */
changed |= object_deselect_all_except(view_layer, basact);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (basact->flag & BASE_SELECTED)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
/* `basact` may be NULL. */
changed |= object_deselect_all_except(view_layer, basact);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
}
}
}
/* so, do we have something selected? */
if (basact) {
if (found) {
changed = true;
if (vc.obedit) {
@ -2626,8 +2639,14 @@ static bool ed_wpaint_vertex_select_pick(bContext *C,
bool found = ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index);
if ((params->sel_op == SEL_OP_SET) && (found || params->deselect_all)) {
changed |= paintface_deselect_all_visible(C, obact, SEL_DESELECT, false);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (me->mvert[index].flag & SELECT)) {
found = false;
}
else if (found || params->deselect_all) {
/* Deselect everything. */
changed |= paintface_deselect_all_visible(C, obact, SEL_DESELECT, false);
}
}
if (found) {
@ -2686,6 +2705,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
RNA_boolean_get(op->ptr, "deselect"),
RNA_boolean_get(op->ptr, "toggle")),
.deselect_all = RNA_boolean_get(op->ptr, "deselect_all"),
.select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough"),
};
bool center = RNA_boolean_get(op->ptr, "center");

View File

@ -518,6 +518,14 @@ void WM_operator_properties_mouse_select(wmOperatorType *ot)
"Deselect On Nothing",
"Deselect all when nothing under the cursor");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
/* TODO: currently only used for the 3D viewport. */
prop = RNA_def_boolean(ot->srna,
"select_passthrough",
false,
"Only Select Unselected",
"Ignore the select action when the element is already selected");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
void WM_operator_properties_checker_interval(wmOperatorType *ot, bool nth_can_disable)