From 2808c411d749760f014faa75dbd5a1b768f9ff05 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 16 Oct 2025 06:34:09 +0000 Subject: [PATCH] Fix #148026: Box select pinned UV's fails with select-sync enabled Implement pinned with select-sync (technically not a bug), more an oversight in !138197. Some subtle functional changes have been made. - Select pinned now only works in vertex select mode since previously it was possible to select vertices in edge/face modes where the selection wasn't displayed. - The island selection option is ignored when selecting pinned. - The select pinned operator wasn't working with select sync edge/face modes. Exits with an error instead. Ref !148181 --- .../blender/editors/uvedit/uvedit_select.cc | 92 +++++++++++-------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/source/blender/editors/uvedit/uvedit_select.cc b/source/blender/editors/uvedit/uvedit_select.cc index d9b09dd0630..f53748df2ec 100644 --- a/source/blender/editors/uvedit/uvedit_select.cc +++ b/source/blender/editors/uvedit/uvedit_select.cc @@ -382,7 +382,7 @@ void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMesh *bm, const bool s } } -static void uvedit_vertex_select_tagged(BMesh *bm, Scene *scene, bool select) +static void uvedit_vertex_select_tagged(BMesh *bm, const Scene *scene, bool select) { BMFace *efa; BMLoop *l; @@ -1591,6 +1591,15 @@ BMLoop *uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, /** \name Helper functions for UV selection. * \{ */ +static bool uvedit_select_pin_ok_or_report(const Scene *scene, ReportList *reports) +{ + if (ED_uvedit_select_mode_get(scene) != UV_SELECT_VERT) { + BKE_report(reports, RPT_ERROR, "Pinned vertices can be selected in Vertex Mode only"); + return false; + } + return true; +} + void uvedit_select_prepare_custom_data(const Scene *scene, BMesh *bm) { const ToolSettings *ts = scene->toolsettings; @@ -2455,7 +2464,7 @@ static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, c /** \name Select Linked * \{ */ -static void uv_select_linked_multi(Scene *scene, +static void uv_select_linked_multi(const Scene *scene, const Span objects, UvNearestHit *hit, const bool extend, @@ -2747,7 +2756,7 @@ static void uv_select_linked_multi(Scene *scene, /** * A wrapper for #uv_select_linked_multi that uses defaults for UV island selection. */ -static void uv_select_linked_multi_for_select_island(Scene *scene, +static void uv_select_linked_multi_for_select_island(const Scene *scene, const Span objects, Object *obedit, BMFace *efa, @@ -4480,11 +4489,39 @@ static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMesh *bm) /** \name Box Select Operator * \{ */ +static wmOperatorStatus uv_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const Scene *scene = CTX_data_scene(C); + const bool pinned = RNA_boolean_get(op->ptr, "pinned"); + if (pinned) { + if (!uvedit_select_pin_ok_or_report(scene, op->reports)) { + return OPERATOR_CANCELLED; + } + } + return WM_gesture_box_invoke(C, op, event); +} + static wmOperatorStatus uv_box_select_exec(bContext *C, wmOperator *op) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); const ToolSettings *ts = scene->toolsettings; + + const bool pinned = RNA_boolean_get(op->ptr, "pinned"); + const bool use_face_center = ((ts->uv_flag & UV_FLAG_SELECT_SYNC) ? + (ts->selectmode == SCE_SELECT_FACE) : + (ts->uv_selectmode == UV_SELECT_FACE)); + const bool use_edge = ((ts->uv_flag & UV_FLAG_SELECT_SYNC) ? + (ts->selectmode == SCE_SELECT_EDGE) : + (ts->uv_selectmode == UV_SELECT_EDGE)); + const bool use_select_linked = pinned ? false : ED_uvedit_select_island_check(ts); + + if (pinned) { + if (!uvedit_select_pin_ok_or_report(scene, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); const ARegion *region = CTX_wm_region(C); BMFace *efa; @@ -4492,13 +4529,6 @@ static wmOperatorStatus uv_box_select_exec(bContext *C, wmOperator *op) BMIter iter, liter; float *luv; rctf rectf; - const bool use_face_center = ((ts->uv_flag & UV_FLAG_SELECT_SYNC) ? - (ts->selectmode == SCE_SELECT_FACE) : - (ts->uv_selectmode == UV_SELECT_FACE)); - const bool use_edge = ((ts->uv_flag & UV_FLAG_SELECT_SYNC) ? - (ts->selectmode == SCE_SELECT_EDGE) : - (ts->uv_selectmode == UV_SELECT_EDGE)); - const bool use_select_linked = ED_uvedit_select_island_check(ts); /* get rectangle from operator */ WM_operator_properties_border_to_rctf(op, &rectf); @@ -4508,8 +4538,6 @@ static wmOperatorStatus uv_box_select_exec(bContext *C, wmOperator *op) const bool select = (sel_op != SEL_OP_SUB); const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); - const bool pinned = RNA_boolean_get(op->ptr, "pinned"); - bool changed_multi = false; Vector objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( @@ -4526,20 +4554,20 @@ static wmOperatorStatus uv_box_select_exec(bContext *C, wmOperator *op) bool changed = false; if (ts->uv_flag & UV_FLAG_SELECT_SYNC) { - /* NOTE: sync selection can't do pinned. */ uvedit_select_prepare_sync_select(scene, bm); } else { uvedit_select_prepare_custom_data(scene, bm); - if (pinned) { - const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2); - BM_uv_map_attr_pin_ensure_named(bm, active_uv_name); - } } + const BMUVOffsets offsets = BM_uv_map_offsets_get(bm); /* do actual selection */ - if (use_face_center && !pinned) { + if (pinned && offsets.pin == -1) { + /* Special case, nothing is pinned so it's known in advance that nothing will be selected. + * Still run the code after this block finishes as the UV's may have been de-selected. */ + } + else if (use_face_center) { /* handle face selection mode */ if (use_select_linked) { @@ -4576,7 +4604,7 @@ static wmOperatorStatus uv_box_select_exec(bContext *C, wmOperator *op) uv_select_flush_from_tag_face(scene, obedit, select); } } - else if (use_edge && !pinned) { + else if (use_edge) { bool do_second_pass = true; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { @@ -4645,20 +4673,13 @@ static wmOperatorStatus uv_box_select_exec(bContext *C, wmOperator *op) BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv); if (select != uvedit_uv_select_test(scene, bm, l, offsets)) { - if (!pinned || (ts->uv_flag & UV_FLAG_SELECT_SYNC)) { - /* UV_FLAG_SELECT_SYNC - can't do pinned selection */ - if (BLI_rctf_isect_pt_v(&rectf, luv)) { + if (BLI_rctf_isect_pt_v(&rectf, luv)) { + if (!pinned || BM_ELEM_CD_GET_BOOL(l, offsets.pin)) { uvedit_uv_select_set(scene, bm, l, select); BM_elem_flag_enable(l->v, BM_ELEM_TAG); has_selected = true; } } - else if (pinned) { - if (BM_ELEM_CD_GET_BOOL(l, offsets.pin) && BLI_rctf_isect_pt_v(&rectf, luv)) { - uvedit_uv_select_set(scene, bm, l, select); - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - } - } } } if (has_selected && use_select_linked) { @@ -4695,7 +4716,7 @@ void UV_OT_select_box(wmOperatorType *ot) ot->idname = "UV_OT_select_box"; /* API callbacks. */ - ot->invoke = WM_gesture_box_invoke; + ot->invoke = uv_box_select_invoke; ot->exec = uv_box_select_exec; ot->modal = WM_gesture_box_modal; ot->poll = ED_operator_uvedit_space_image; /* requires space image */ @@ -5214,16 +5235,13 @@ void UV_OT_select_lasso(wmOperatorType *ot) static wmOperatorStatus uv_select_pinned_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + const Scene *scene = CTX_data_scene(C); const ToolSettings *ts = scene->toolsettings; /* Use this operator only in vertex mode, since it is not guaranteed that pinned vertices may * form higher selection states (like edges/faces/islands) in other modes. */ - if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0) { - if (ts->uv_selectmode != UV_SELECT_VERT) { - BKE_report(op->reports, RPT_ERROR, "Pinned vertices can be selected in Vertex Mode only"); - return OPERATOR_CANCELLED; - } + if (!uvedit_select_pin_ok_or_report(scene, op->reports)) { + return OPERATOR_CANCELLED; } Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);