From 2ea565b0ec94136d01fb3ff5bdc31cecfe1c2440 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 12 Jul 2021 16:15:03 +0200 Subject: Cleanup: improve naming and comments of scene frame/ctime functions Confusingly, BKE_scene_frame_get did not match the frame number as expected by BKE_scene_frame_set. Instead it return the value after time remapping, which is commonly named "ctime". * Rename BKE_scene_frame_get to BKE_scene_ctime_get * Add a new BKE_scene_frame_get that matches BKE_scene_frame_set * Use int/float depending if fractional frame is expected --- source/blender/editors/animation/time_scrub_ui.c | 2 +- source/blender/editors/include/ED_keyframing.h | 2 +- source/blender/editors/space_action/action_data.c | 4 ++-- source/blender/editors/space_view3d/view3d_draw.c | 2 +- source/blender/editors/transform/transform_convert_object.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 034378399b9..6af033f3cf2 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -109,7 +109,7 @@ static void draw_current_frame(const Scene *scene, if (draw_line) { /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen. */ - const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_frame_get(scene)); + const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 0493832c06f..673f629d6ef 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -468,7 +468,7 @@ bool fcurve_is_changed(struct PointerRNA ptr, * Checks whether a keyframe exists for the given ID-block one the given frame. * - It is recommended to call this method over the other keyframe-checkers directly, * in case some detail of the implementation changes... - * - frame: the value of this is quite often result of #BKE_scene_frame_get() + * - frame: the value of this is quite often result of #BKE_scene_ctime_get() */ bool id_frame_has_keyframe(struct ID *id, float frame, short filter); diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 9e69b0a72db..d69c7ab8d48 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -813,7 +813,7 @@ static int action_layer_next_exec(bContext *C, wmOperator *op) NlaTrack *act_track; Scene *scene = CTX_data_scene(C); - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); /* Get active track */ act_track = BKE_nlatrack_find_tweaked(adt); @@ -925,7 +925,7 @@ static int action_layer_prev_exec(bContext *C, wmOperator *op) NlaTrack *nlt; Scene *scene = CTX_data_scene(C); - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); /* Sanity Check */ if (adt == NULL) { diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index ea9d9a8c010..c97ba7ba7e9 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1404,7 +1404,7 @@ static void draw_selected_name( /* color depends on whether there is a keyframe */ if (id_frame_has_keyframe( - (ID *)ob, /* BKE_scene_frame_get(scene) */ (float)cfra, ANIMFILTER_KEYS_LOCAL)) { + (ID *)ob, /* BKE_scene_ctime_get(scene) */ (float)cfra, ANIMFILTER_KEYS_LOCAL)) { UI_FontThemeColor(font_id, TH_TIME_KEYFRAME); } else if (ED_gpencil_has_keyframe_v3d(scene, ob, cfra)) { diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index c217478bd04..ee6cb391fdc 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -153,7 +153,7 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob) if (t->mode != TFM_DUMMY && ob->rigidbody_object) { float rot[3][3], scale[3]; - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); /* only use rigid body transform if simulation is running, * avoids problems with initial setup of rigid bodies */ @@ -978,7 +978,7 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) /* restore rigid body transform */ if (ob->rigidbody_object && canceled) { - float ctime = BKE_scene_frame_get(t->scene); + float ctime = BKE_scene_ctime_get(t->scene); if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) { BKE_rigidbody_aftertrans_update(ob, td->ext->oloc, -- cgit v1.2.3 From bfa3dc91b75407b063f2ac991b176d98c050f92d Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 29 Jun 2021 09:34:50 -0300 Subject: Depsgraph: Implement 'ID_RECALC_GEOMETRY_DEFORM' During a mesh transformation in edit mode (Move, Rotate...), only part of the batch cache needs to be updated. This commit allows only update only the drawn batches seen in `BKE_object_data_eval_batch_cache_deform_tag` if the new `ID_RECALC_GEOMETRY_DEFORM` flag is used. This new flag is used in the transforms operation for edit-mesh and results in 1.6x overall speedup in heavy subdiv cube. Differential Revision: https://developer.blender.org/D11599 --- source/blender/editors/transform/transform_convert_mesh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index c1ee6edfef6..18cd62a0baf 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -2094,7 +2094,7 @@ void recalcData_mesh(TransInfo *t) tc_mesh_partial_types_calc(t, &partial_state); FOREACH_TRANS_DATA_CONTAINER (t, tc) { - DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY_DEFORM); tc_mesh_partial_update(t, tc, &partial_state); } -- cgit v1.2.3 From 2e7e7a6fb6835f5b6671ef06752aa21d7768f108 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 13:01:28 +1000 Subject: Fix object "Set Origin" operating on linked library data Regression in d25747ee751096de2e417a7da1316bf5bf81c25a --- source/blender/editors/object/object_transform.c | 47 ++++++++++++------------ 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index e350653e178..86871a02a72 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1195,32 +1195,33 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) else if (ID_IS_LINKED(ob->data)) { tot_lib_error++; } + else if (ob->type == OB_MESH) { + if (obedit == NULL) { + Mesh *me = ob->data; - if (obedit == NULL && ob->type == OB_MESH) { - Mesh *me = ob->data; - - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else if (centermode == ORIGIN_TO_CENTER_OF_MASS_SURFACE) { - BKE_mesh_center_of_surface(me, cent); - } - else if (centermode == ORIGIN_TO_CENTER_OF_MASS_VOLUME) { - BKE_mesh_center_of_volume(me, cent); - } - else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_mesh_center_bounds(me, cent); - } - else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_mesh_center_median(me, cent); - } + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ + } + else if (centermode == ORIGIN_TO_CENTER_OF_MASS_SURFACE) { + BKE_mesh_center_of_surface(me, cent); + } + else if (centermode == ORIGIN_TO_CENTER_OF_MASS_VOLUME) { + BKE_mesh_center_of_volume(me, cent); + } + else if (around == V3D_AROUND_CENTER_BOUNDS) { + BKE_mesh_center_bounds(me, cent); + } + else { /* #V3D_AROUND_CENTER_MEDIAN. */ + BKE_mesh_center_median(me, cent); + } - negate_v3_v3(cent_neg, cent); - BKE_mesh_translate(me, cent_neg, 1); + negate_v3_v3(cent_neg, cent); + BKE_mesh_translate(me, cent_neg, 1); - tot_change++; - me->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; + tot_change++; + me->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + } } else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { Curve *cu = ob->data; -- cgit v1.2.3 From 7a084c2eee266cac01ade0fed3527586b9e2e0d2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 15:05:39 +1000 Subject: Cleanup: minor changes to edit-mesh API calls Rename: - EDBM_mesh_free -> EDBM_mesh_free_data BKE_editmesh_free -> BKE_editmesh_free_data Since this doesn't free the edit-mesh pointer. - BKE_editmesh_free_derivedmesh -> BKE_editmesh_free_derived_caches Since this no longer uses derived-mesh, match naming for the related object function BKE_object_free_derived_caches. Also remove `do_tessellate` argument from BKE_editmesh_create, since the caller can explicitly do this if it's needed, with the advantage that it can be combined with normal calculation which is faster on high-poly meshes. --- source/blender/editors/include/ED_mesh.h | 2 +- .../blender/editors/mesh/editmesh_mask_extract.c | 4 ++-- source/blender/editors/mesh/editmesh_undo.c | 6 +++-- source/blender/editors/mesh/editmesh_utils.c | 26 +++++++++------------- source/blender/editors/object/object_edit.c | 2 +- 5 files changed, 19 insertions(+), 21 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 667540b8f63..2b73194afb2 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -86,7 +86,7 @@ void EDBM_mesh_clear(struct BMEditMesh *em); void EDBM_selectmode_to_scene(struct bContext *C); void EDBM_mesh_make(struct Object *ob, const int select_mode, const bool add_key_index); -void EDBM_mesh_free(struct BMEditMesh *em); +void EDBM_mesh_free_data(struct BMEditMesh *em); void EDBM_mesh_load_ex(struct Main *bmain, struct Object *ob, bool free_data); void EDBM_mesh_load(struct Main *bmain, struct Object *ob); diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 993905462db..cccfc7e934c 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -126,7 +126,7 @@ static int geometry_extract_apply(bContext *C, .calc_face_normal = true, })); - BMEditMesh *em = BKE_editmesh_create(bm, false); + BMEditMesh *em = BKE_editmesh_create(bm); /* Generate the tags for deleting geometry in the extracted object. */ tag_fn(bm, params); @@ -206,7 +206,7 @@ static int geometry_extract_apply(bContext *C, }), mesh); - BKE_editmesh_free(em); + BKE_editmesh_free_data(em); MEM_freeN(em); if (new_mesh->totvert == 0) { diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 112de68b52c..613c58e87c8 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -671,7 +671,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * em->bm->shapenr = um->shapenr; - EDBM_mesh_free(em); + EDBM_mesh_free_data(em); bm = BM_mesh_create(&allocsize, &((struct BMeshCreateParams){ @@ -685,9 +685,11 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * .active_shapekey = um->shapenr, })); - em_tmp = BKE_editmesh_create(bm, true); + em_tmp = BKE_editmesh_create(bm); *em = *em_tmp; + BKE_editmesh_looptri_calc(em); + em->selectmode = um->selectmode; bm->selectmode = um->selectmode; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 4f416c94f62..a909186c088 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -305,17 +305,13 @@ void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index) if (me->edit_mesh) { /* this happens when switching shape keys */ - EDBM_mesh_free(me->edit_mesh); + EDBM_mesh_free_data(me->edit_mesh); MEM_freeN(me->edit_mesh); } - /* currently executing operators re-tessellates, so we can avoid doing here - * but at some point it may need to be added back. */ -#if 0 - me->edit_mesh = BKE_editmesh_create(bm, true); -#else - me->edit_mesh = BKE_editmesh_create(bm, false); -#endif + /* Executing operators re-tessellates, + * so we can avoid doing here but at some point it may need to be added back. */ + me->edit_mesh = BKE_editmesh_create(bm); me->edit_mesh->selectmode = me->edit_mesh->bm->selectmode = select_mode; me->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0; @@ -372,8 +368,8 @@ void EDBM_mesh_clear(BMEditMesh *em) /* clear bmesh */ BM_mesh_clear(em->bm); - /* free derived meshes */ - BKE_editmesh_free_derivedmesh(em); + /* Free evaluated meshes & cache. */ + BKE_editmesh_free_derived_caches(em); /* free tessellation data */ em->tottri = 0; @@ -389,9 +385,9 @@ void EDBM_mesh_load(Main *bmain, Object *ob) } /** - * Should only be called on the active editmesh, otherwise call #BKE_editmesh_free + * Should only be called on the active edit-mesh, otherwise call #BKE_editmesh_free_data. */ -void EDBM_mesh_free(BMEditMesh *em) +void EDBM_mesh_free_data(BMEditMesh *em) { /* These tables aren't used yet, so it's not strictly necessary * to 'end' them but if someone tries to start using them, @@ -399,7 +395,7 @@ void EDBM_mesh_free(BMEditMesh *em) ED_mesh_mirror_spatial_table_end(NULL); ED_mesh_mirror_topo_table_end(NULL); - BKE_editmesh_free(em); + BKE_editmesh_free_data(em); } /** \} */ @@ -1471,8 +1467,8 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) BM_lnorspace_invalidate(em->bm, false); em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET; } - /* don't keep stale derivedMesh data around, see: T38872. */ - BKE_editmesh_free_derivedmesh(em); + /* Don't keep stale evaluated mesh data around, see: T38872. */ + BKE_editmesh_free_derived_caches(em); #ifdef DEBUG { diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 1cb6a5c8ff5..6108691b2f1 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -557,7 +557,7 @@ static bool ED_object_editmode_load_free_ex(Main *bmain, } if (free_data) { - EDBM_mesh_free(me->edit_mesh); + EDBM_mesh_free_data(me->edit_mesh); MEM_freeN(me->edit_mesh); me->edit_mesh = NULL; } -- cgit v1.2.3 From 22c4323b2169e700fa124b3dfe56b750bc532e49 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 15:27:38 +1000 Subject: Cleanup: remove commented code Replace with brief note in warning. --- source/blender/editors/mesh/editmesh_utils.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index a909186c088..85c646d689c 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -322,7 +322,8 @@ void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index) /** * \warning This can invalidate the #Mesh runtime cache of other objects (for linked duplicates). - * Most callers should run #DEG_id_tag_update on \a ob->data, see: T46738, T46913 + * Most callers should run #DEG_id_tag_update on `ob->data`, see: T46738, T46913. + * This ensures #BKE_object_free_derived_caches runs on all objects that use this mesh. */ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) { @@ -342,25 +343,6 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) .calc_object_remap = true, .update_shapekey_indices = !free_data, })); - - /* Free derived mesh. usually this would happen through depsgraph but there - * are exceptions like file save that will not cause this, and we want to - * avoid ending up with an invalid derived mesh then. - * - * Do it for all objects which shares the same mesh datablock, since their - * derived meshes might also be referencing data which was just freed, - * - * Annoying enough, but currently seems most efficient way to avoid access - * of freed data on scene update, especially in cases when there are dependency - * cycles. - */ -#if 0 - for (Object *other_object = bmain->objects.first; other_object != NULL; other_object = other_object->id.next) { - if (other_object->data == ob->data) { - BKE_object_free_derived_caches(other_object); - } - } -#endif } void EDBM_mesh_clear(BMEditMesh *em) -- cgit v1.2.3 From f9c9e000ca42c8cc169dec22c702904944ee2513 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 16:51:06 +1000 Subject: Undo: optimize edit-mode undo - Tag the object data instead of the object when decoding (this avoids duplicating mesh object-data on each undo-step). - Calculate face normals as part of multi-threaded tessellation. This gives ~11% speedup with 1.5x million polygons. --- source/blender/editors/armature/editarmature_undo.c | 2 +- source/blender/editors/curve/editcurve_undo.c | 2 +- source/blender/editors/curve/editfont_undo.c | 2 +- source/blender/editors/lattice/editlattice_undo.c | 2 +- source/blender/editors/mesh/editmesh_undo.c | 12 +++++++++--- source/blender/editors/metaball/editmball_undo.c | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index 725945f8edc..832e75b2a8b 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -206,7 +206,7 @@ static void armature_undosys_step_decode(struct bContext *C, } undoarm_to_editarm(&elem->data, arm); arm->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&arm->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c index 88f6398567d..210411c6eb5 100644 --- a/source/blender/editors/curve/editcurve_undo.c +++ b/source/blender/editors/curve/editcurve_undo.c @@ -267,7 +267,7 @@ static void curve_undosys_step_decode(struct bContext *C, } undocurve_to_editcurve(bmain, &elem->data, obedit->data, &obedit->shapenr); cu->editnurb->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c index b61506d9346..6eaf8971eb0 100644 --- a/source/blender/editors/curve/editfont_undo.c +++ b/source/blender/editors/curve/editfont_undo.c @@ -379,7 +379,7 @@ static void font_undosys_step_decode(struct bContext *C, Curve *cu = obedit->data; undofont_to_editfont(&us->data, cu); - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY); ED_undo_object_set_active_or_warn( CTX_data_scene(C), CTX_data_view_layer(C), obedit, us_p->name, &LOG); diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c index d92a81179cc..23eaf991fd3 100644 --- a/source/blender/editors/lattice/editlattice_undo.c +++ b/source/blender/editors/lattice/editlattice_undo.c @@ -240,7 +240,7 @@ static void lattice_undosys_step_decode(struct bContext *C, } undolatt_to_editlatt(&elem->data, lt->editlatt); lt->editlatt->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(<->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 613c58e87c8..7a89dbd831d 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -681,14 +681,20 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * BM_mesh_bm_from_me(bm, &um->me, (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, + /* Handled with tessellation. */ + .calc_face_normal = false, .active_shapekey = um->shapenr, })); em_tmp = BKE_editmesh_create(bm); *em = *em_tmp; - BKE_editmesh_looptri_calc(em); + /* Calculate face normals and tessellation at once since it's multi-threaded. + * The vertex normals are stored in the undo-mesh, so this doesn't need to be updated. */ + BKE_editmesh_looptri_calc_ex(em, + &(const struct BMeshCalcTessellation_Params){ + .face_normals = true, + }); em->selectmode = um->selectmode; bm->selectmode = um->selectmode; @@ -867,7 +873,7 @@ static void mesh_undosys_step_decode(struct bContext *C, BMEditMesh *em = me->edit_mesh; undomesh_to_editmesh(&elem->data, obedit, em, me->key); em->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c index a8b471a7c92..f7b53b5513f 100644 --- a/source/blender/editors/metaball/editmball_undo.c +++ b/source/blender/editors/metaball/editmball_undo.c @@ -215,7 +215,7 @@ static void mball_undosys_step_decode(struct bContext *C, } undomball_to_editmball(&elem->data, mb); mb->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&mb->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ -- cgit v1.2.3 From 1b4d5c7a35597a70411515f721a405416244b540 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 17:28:07 +1000 Subject: Undo System: avoid redundant decoding on undo In most cases the undo system was loading undo steps twice. This was needed since some undo systems (sculpt, paint, text) require stepping out of the current undo step. Use a flag to limit this to the undo systems that need it. This improves performance for other undo systems. This gives around 1.96x speedup in edit-mesh for high-poly objects. Reviewed by: mont29 Ref D11893 --- source/blender/editors/sculpt_paint/sculpt_undo.c | 2 +- source/blender/editors/space_image/image_undo.c | 2 +- source/blender/editors/space_text/text_undo.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index fe7029c7457..f21c900941b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1608,7 +1608,7 @@ void ED_sculpt_undosys_type(UndoType *ut) ut->step_decode = sculpt_undosys_step_decode; ut->step_free = sculpt_undosys_step_free; - ut->flags = 0; + ut->flags = UNDOTYPE_FLAG_DECODE_ACTIVE_STEP; ut->step_size = sizeof(SculptUndoStep); } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index 082f66b57af..cc6effd0f71 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -1006,7 +1006,7 @@ void ED_image_undosys_type(UndoType *ut) * specific case, see `image_undosys_step_encode` code. We cannot specify * `UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE` though, as it can be called with a NULL context by * current code. */ - ut->flags = 0; + ut->flags = UNDOTYPE_FLAG_DECODE_ACTIVE_STEP; ut->step_size = sizeof(ImageUndoStep); } diff --git a/source/blender/editors/space_text/text_undo.c b/source/blender/editors/space_text/text_undo.c index f55db8c3cc9..80af7d8c9f6 100644 --- a/source/blender/editors/space_text/text_undo.c +++ b/source/blender/editors/space_text/text_undo.c @@ -265,7 +265,7 @@ void ED_text_undosys_type(UndoType *ut) ut->step_foreach_ID_ref = text_undosys_foreach_ID_ref; - ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE; + ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE | UNDOTYPE_FLAG_DECODE_ACTIVE_STEP; ut->step_size = sizeof(TextUndoStep); } -- cgit v1.2.3 From 8839b4c32a44ba2c50f08ccd815d450ac71c454a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 8 Jul 2021 16:23:41 +1000 Subject: UI: support persistent state during number/slider interaction Support for begin/update/end callbacks allowing state to be cached and reused while dragging a number button or slider. This is done using `UI_block_interaction_set` to set callbacks. - Dragging multiple buttons at once is supported, passing multiple unique events into the update function. - Update is only called once even when multiple buttons are edited. - The update callback can detect the difference between click & drag actions so situations to support skipping cache creation and freeing for situations where it's not beneficial. Reviewed by: Severin, HooglyBoogly Ref D11861 --- source/blender/editors/include/UI_interface.h | 49 +++++ .../blender/editors/interface/interface_handlers.c | 198 +++++++++++++++++++++ .../blender/editors/interface/interface_intern.h | 3 + 3 files changed, 250 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 6901176a587..802c175492f 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -58,6 +58,7 @@ struct bNodeTree; struct bScreen; struct rctf; struct rcti; +struct uiBlockInteraction_Handle; struct uiButSearch; struct uiFontStyle; struct uiList; @@ -514,6 +515,54 @@ typedef int (*uiButPushedStateFunc)(struct uiBut *but, const void *arg); typedef void (*uiBlockHandleFunc)(struct bContext *C, void *arg, int event); +/* -------------------------------------------------------------------- */ +/** \name Custom Interaction + * + * Sometimes it's useful to create data that remains available + * while the user interacts with a button. + * + * A common case is dragging a number button or slider + * however this could be used in other cases too. + * \{ */ + +struct uiBlockInteraction_Params { + /** + * When true, this interaction is not modal + * (user clicking on a number button arrows or pasting a value for example). + */ + bool is_click; + /** + * Array of unique event ID's (values from #uiBut.retval). + * There may be more than one for multi-button editing (see #UI_BUT_DRAG_MULTI). + */ + int *unique_retval_ids; + uint unique_retval_ids_len; +}; + +/** Returns 'user_data', freed by #uiBlockInteractionEndFn. */ +typedef void *(*uiBlockInteractionBeginFn)(struct bContext *C, + const struct uiBlockInteraction_Params *params, + void *arg1); +typedef void (*uiBlockInteractionEndFn)(struct bContext *C, + const struct uiBlockInteraction_Params *params, + void *arg1, + void *user_data); +typedef void (*uiBlockInteractionUpdateFn)(struct bContext *C, + const struct uiBlockInteraction_Params *params, + void *arg1, + void *user_data); + +typedef struct uiBlockInteraction_CallbackData { + uiBlockInteractionBeginFn begin_fn; + uiBlockInteractionEndFn end_fn; + uiBlockInteractionUpdateFn update_fn; + void *arg1; +} uiBlockInteraction_CallbackData; + +void UI_block_interaction_set(uiBlock *block, uiBlockInteraction_CallbackData *callbacks); + +/** \} */ + /* Menu Callbacks */ typedef void (*uiMenuCreateFunc)(struct bContext *C, struct uiLayout *layout, void *arg1); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 743f646d4df..5d3e9e42f29 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -35,10 +35,12 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "BLI_array_utils.h" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" +#include "BLI_sort_utils.h" #include "BLI_string.h" #include "BLI_string_cursor_utf8.h" #include "BLI_string_utf8.h" @@ -170,6 +172,20 @@ static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str); static void button_tooltip_timer_reset(bContext *C, uiBut *but); +static void ui_block_interaction_begin_ensure(bContext *C, + uiBlock *block, + struct uiHandleButtonData *data, + const bool is_click); +static struct uiBlockInteraction_Handle *ui_block_interaction_begin(struct bContext *C, + uiBlock *block, + const bool is_click); +static void ui_block_interaction_end(struct bContext *C, + uiBlockInteraction_CallbackData *callbacks, + struct uiBlockInteraction_Handle *interaction); +static void ui_block_interaction_update(struct bContext *C, + uiBlockInteraction_CallbackData *callbacks, + struct uiBlockInteraction_Handle *interaction); + #ifdef USE_KEYNAV_LIMIT static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event); static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event); @@ -225,6 +241,19 @@ typedef enum uiMenuScrollType { MENU_SCROLL_BOTTOM, } uiMenuScrollType; +typedef struct uiBlockInteraction_Handle { + struct uiBlockInteraction_Params params; + void *user_data; + /** + * This is shared between #uiHandleButtonData and #uiAfterFunc, + * the last user runs the end callback and frees the data. + * + * This is needed as the order of freeing changes depending on + * accepting/canceling the operation. + */ + int user_count; +} uiBlockInteraction_Handle; + #ifdef USE_ALLSELECT /* Unfortunately there's no good way handle more generally: @@ -430,6 +459,8 @@ typedef struct uiHandleButtonData { uiSelectContextStore select_others; #endif + struct uiBlockInteraction_Handle *custom_interaction_handle; + /* Text field undo. */ struct uiUndoStack_Text *undo_stack_text; @@ -471,6 +502,9 @@ typedef struct uiAfterFunc { void *search_arg; uiFreeArgFunc search_arg_free_fn; + uiBlockInteraction_CallbackData custom_interaction_callbacks; + uiBlockInteraction_Handle *custom_interaction_handle; + bContextStore *context; char undostr[BKE_UNDO_STR_MAX]; @@ -827,6 +861,27 @@ static void ui_apply_but_func(bContext *C, uiBut *but) search_but->arg = NULL; } + if (but->active != NULL) { + uiHandleButtonData *data = but->active; + if (data->custom_interaction_handle != NULL) { + after->custom_interaction_callbacks = block->custom_interaction_callbacks; + after->custom_interaction_handle = data->custom_interaction_handle; + + /* Ensure this callback runs once and last. */ + uiAfterFunc *after_prev = after->prev; + if (after_prev && + (after_prev->custom_interaction_handle == data->custom_interaction_handle)) { + after_prev->custom_interaction_handle = NULL; + memset(&after_prev->custom_interaction_callbacks, + 0x0, + sizeof(after_prev->custom_interaction_callbacks)); + } + else { + after->custom_interaction_handle->user_count++; + } + } + } + if (but->context) { after->context = CTX_store_copy(but->context); } @@ -997,6 +1052,18 @@ static void ui_apply_but_funcs_after(bContext *C) after.search_arg_free_fn(after.search_arg); } + if (after.custom_interaction_handle != NULL) { + after.custom_interaction_handle->user_count--; + BLI_assert(after.custom_interaction_handle->user_count >= 0); + if (after.custom_interaction_handle->user_count == 0) { + ui_block_interaction_update( + C, &after.custom_interaction_callbacks, after.custom_interaction_handle); + ui_block_interaction_end( + C, &after.custom_interaction_callbacks, after.custom_interaction_handle); + } + after.custom_interaction_handle = NULL; + } + ui_afterfunc_update_preferences_dirty(&after); if (after.undostr[0]) { @@ -2283,6 +2350,11 @@ static void ui_apply_but( uiButCurveProfile *but_profile = (uiButCurveProfile *)but; but_profile->edit_profile = editprofile; } + + if (data->custom_interaction_handle != NULL) { + ui_block_interaction_update( + C, &block->custom_interaction_callbacks, data->custom_interaction_handle); + } } /** \} */ @@ -4852,6 +4924,8 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, return changed; } + ui_block_interaction_begin_ensure(but->block->evil_C, but->block, data, false); + if (ui_but_is_cursor_warp(but)) { const float softmin = but->softmin; const float softmax = but->softmax; @@ -5362,6 +5436,8 @@ static bool ui_numedit_but_SLI(uiBut *but, return changed; } + ui_block_interaction_begin_ensure(but->block->evil_C, but->block, data, false); + const PropertyScaleType scale_type = ui_but_scale_type(but); softmin = but->softmin; @@ -8239,6 +8315,16 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s but->flag &= ~UI_SELECT; } + if (state == BUTTON_STATE_TEXT_EDITING) { + ui_block_interaction_begin_ensure(C, but->block, data, true); + } + else if (state == BUTTON_STATE_EXIT) { + if (data->state == BUTTON_STATE_NUM_EDITING) { + /* This happens on pasting values for example. */ + ui_block_interaction_begin_ensure(C, but->block, data, true); + } + } + data->state = state; if (state != BUTTON_STATE_EXIT) { @@ -8467,6 +8553,21 @@ static void button_activate_exit( ED_region_tag_redraw_no_rebuild(data->region); ED_region_tag_refresh_ui(data->region); + if ((but->flag & UI_BUT_DRAG_MULTI) == 0) { + if (data->custom_interaction_handle != NULL) { + /* Should only set when the button is modal. */ + BLI_assert(but->active != NULL); + data->custom_interaction_handle->user_count--; + + BLI_assert(data->custom_interaction_handle->user_count >= 0); + if (data->custom_interaction_handle->user_count == 0) { + ui_block_interaction_end( + C, &but->block->custom_interaction_callbacks, data->custom_interaction_handle); + } + data->custom_interaction_handle = NULL; + } + } + /* clean up button */ if (but->active) { MEM_freeN(but->active); @@ -11314,3 +11415,100 @@ bool UI_but_active_drop_color(bContext *C) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Block Interaction API + * \{ */ + +void UI_block_interaction_set(uiBlock *block, uiBlockInteraction_CallbackData *callbacks) +{ + block->custom_interaction_callbacks = *callbacks; +} + +static uiBlockInteraction_Handle *ui_block_interaction_begin(bContext *C, + uiBlock *block, + const bool is_click) +{ + BLI_assert(block->custom_interaction_callbacks.begin_fn != NULL); + uiBlockInteraction_Handle *interaction = MEM_callocN(sizeof(*interaction), __func__); + + int unique_retval_ids_len = 0; + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) { + unique_retval_ids_len++; + } + } + + int *unique_retval_ids = MEM_mallocN(sizeof(*unique_retval_ids) * unique_retval_ids_len, + __func__); + unique_retval_ids_len = 0; + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) { + unique_retval_ids[unique_retval_ids_len++] = but->retval; + } + } + + if (unique_retval_ids_len > 1) { + qsort(unique_retval_ids, unique_retval_ids_len, sizeof(int), BLI_sortutil_cmp_int); + unique_retval_ids_len = BLI_array_deduplicate_ordered(unique_retval_ids, + unique_retval_ids_len); + unique_retval_ids = MEM_reallocN(unique_retval_ids, + sizeof(*unique_retval_ids) * unique_retval_ids_len); + } + + interaction->params.is_click = is_click; + interaction->params.unique_retval_ids = unique_retval_ids; + interaction->params.unique_retval_ids_len = unique_retval_ids_len; + + interaction->user_data = block->custom_interaction_callbacks.begin_fn( + C, &interaction->params, block->custom_interaction_callbacks.arg1); + return interaction; +} + +static void ui_block_interaction_end(bContext *C, + uiBlockInteraction_CallbackData *callbacks, + uiBlockInteraction_Handle *interaction) +{ + BLI_assert(callbacks->end_fn != NULL); + callbacks->end_fn(C, &interaction->params, callbacks->arg1, interaction->user_data); + MEM_freeN(interaction->params.unique_retval_ids); + MEM_freeN(interaction); +} + +static void ui_block_interaction_update(bContext *C, + uiBlockInteraction_CallbackData *callbacks, + uiBlockInteraction_Handle *interaction) +{ + BLI_assert(callbacks->update_fn != NULL); + callbacks->update_fn(C, &interaction->params, callbacks->arg1, interaction->user_data); +} + +/** + * \note #ui_block_interaction_begin cannot be called when setting the button state + * (e.g. #BUTTON_STATE_NUM_EDITING) for the following reasons. + * + * - Other buttons may still be activated using #UI_BUT_DRAG_MULTI + * which is necessary before gathering all the #uiBut.retval values to initialize + * #uiBlockInteraction_Params.unique_retval_ids. + * - When clicking on a number button it's not known if the event is a click or a drag. + * + * Instead, it must be called immediately before the drag action begins. + */ +static void ui_block_interaction_begin_ensure(bContext *C, + uiBlock *block, + uiHandleButtonData *data, + const bool is_click) +{ + if (data->custom_interaction_handle) { + return; + } + if (block->custom_interaction_callbacks.begin_fn == NULL) { + return; + } + + uiBlockInteraction_Handle *interaction = ui_block_interaction_begin(C, block, is_click); + interaction->user_count = 1; + data->custom_interaction_handle = interaction; +} + +/** \} */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index a05afb6e542..40a9c67c630 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -511,6 +511,9 @@ struct uiBlock { uiBlockHandleFunc handle_func; void *handle_func_arg; + /** Custom interaction data. */ + uiBlockInteraction_CallbackData custom_interaction_callbacks; + /** Custom extra event handling. */ int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *); -- cgit v1.2.3 From 25c2875e0f2cda224e82bf7339b85ea162e5d565 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 20:14:58 +1000 Subject: Edit Mesh: use partial updates editing vertices with number buttons Use the same partial-update functions used by transform when editing vertex locations with the number buttons. This avoids unnecessary calculations for normals and tessellation. This gives around 1.44x overall speedup on high poly meshes. --- source/blender/editors/include/UI_interface.h | 1 + .../blender/editors/space_view3d/view3d_buttons.c | 105 ++++++++++++++++++++- 2 files changed, 103 insertions(+), 3 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 802c175492f..5f3df1748d0 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -531,6 +531,7 @@ struct uiBlockInteraction_Params { * (user clicking on a number button arrows or pasting a value for example). */ bool is_click; + /** * Array of unique event ID's (values from #uiBut.retval). * There may be more than one for multi-button editing (see #UI_BUT_DRAG_MULTI). diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 2e203d06b12..554b6c0f34f 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -39,6 +39,8 @@ #include "BLT_translation.h" +#include "BLI_array_utils.h" +#include "BLI_bitmap.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -112,10 +114,94 @@ typedef struct { float ob_dims[3]; /* Floats only (treated as an array). */ TransformMedian ve_median, median; + bool tag_for_update; } TransformProperties; #define TRANSFORM_MEDIAN_ARRAY_LEN (sizeof(TransformMedian) / sizeof(float)) +static TransformProperties *v3d_transform_props_ensure(View3D *v3d); + +/* -------------------------------------------------------------------- */ +/** \name Edit Mesh Partial Updates + * \{ */ + +static void *editmesh_partial_update_begin_fn(struct bContext *UNUSED(C), + const struct uiBlockInteraction_Params *params, + void *arg1) +{ + const int retval_test = B_TRANSFORM_PANEL_MEDIAN; + if (BLI_array_findindex( + params->unique_retval_ids, params->unique_retval_ids_len, &retval_test) == -1) { + return NULL; + } + + BMEditMesh *em = arg1; + + int verts_mask_count = 0; + BMIter iter; + BMVert *eve; + int i; + + BLI_bitmap *verts_mask = BLI_BITMAP_NEW(em->bm->totvert, __func__); + BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { + if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + continue; + } + BLI_BITMAP_ENABLE(verts_mask, i); + verts_mask_count += 1; + } + + BMPartialUpdate *bmpinfo = BM_mesh_partial_create_from_verts_group_single( + em->bm, + &(BMPartialUpdate_Params){ + .do_tessellate = true, + .do_normals = true, + }, + verts_mask, + verts_mask_count); + + MEM_freeN(verts_mask); + + return bmpinfo; +} + +static void editmesh_partial_update_end_fn(struct bContext *UNUSED(C), + const struct uiBlockInteraction_Params *UNUSED(params), + void *UNUSED(arg1), + void *user_data) +{ + BMPartialUpdate *bmpinfo = user_data; + if (bmpinfo == NULL) { + return; + } + BM_mesh_partial_destroy(bmpinfo); +} + +static void editmesh_partial_update_update_fn( + struct bContext *C, + const struct uiBlockInteraction_Params *UNUSED(params), + void *arg1, + void *user_data) +{ + BMPartialUpdate *bmpinfo = user_data; + if (bmpinfo == NULL) { + return; + } + + View3D *v3d = CTX_wm_view3d(C); + TransformProperties *tfp = v3d_transform_props_ensure(v3d); + if (tfp->tag_for_update == false) { + return; + } + tfp->tag_for_update = false; + + BMEditMesh *em = arg1; + + BKE_editmesh_looptri_and_normals_calc_with_partial(em, bmpinfo); +} + +/** \} */ + /* Helper function to compute a median changed value, * when the value should be clamped in [0.0, 1.0]. * Returns either 0.0, 1.0 (both can be applied directly), a positive scale factor @@ -840,6 +926,20 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } UI_block_align_end(block); + + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_mesh; + if (em != NULL) { + UI_block_interaction_set(block, + &(uiBlockInteraction_CallbackData){ + .begin_fn = editmesh_partial_update_begin_fn, + .end_fn = editmesh_partial_update_end_fn, + .update_fn = editmesh_partial_update_update_fn, + .arg1 = em, + }); + } + } } else { /* apply */ memcpy(&ve_median_basis, &tfp->ve_median, sizeof(tfp->ve_median)); @@ -927,9 +1027,8 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } if (apply_vcos) { - /* TODO: use the #BKE_editmesh_looptri_and_normals_calc_with_partial - * This requires begin/end states for UI interaction (which currently aren't supported). */ - BKE_editmesh_looptri_and_normals_calc(em); + /* Tell the update callback to run. */ + tfp->tag_for_update = true; } /* Edges */ -- cgit v1.2.3 From d374469f4c045355b2e5b17d7ba5dae9c979c8c7 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 13 Jul 2021 12:53:56 +0200 Subject: VSE: Make pasted strip active When adding texts or various simple effects I often copy-paste strips to reuse properties from a template such as font or position. I assume this is common workflow. Issue with this workflow is, that active strip is not changed after pasting, so when adjusting property, it is original strip that is being modified. This is not issue when duplicating strips - selection state is transfered to duplicate strips, such that duplicate of active strip is set to be active and duplicate of selected strip is set to selected. Implement same selection transfering behavior in paste operator, that exists in duplicate operator. Since strip can be deleted after copying, it is not possible to rely on sequencer state. This is true even when pasting strips to different scene. Therefore active strip name must be stored in clipboard. Reviewed By: sergey, Severin Differential Revision: https://developer.blender.org/D11781 --- source/blender/editors/space_sequencer/sequencer_edit.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 45c6931364d..75cf8542f67 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2414,6 +2414,7 @@ static int sequencer_copy_exec(bContext *C, wmOperator *op) (LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_FREE_NO_MAIN)); seqbase_clipboard_frame = scene->r.cfra; + SEQ_clipboard_active_seq_name_store(scene); /* Remove anything that references the current scene. */ LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) { @@ -2504,6 +2505,10 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) BLI_movelisttolist(ed->seqbasep, &nseqbase); for (iseq = iseq_first; iseq; iseq = iseq->next) { + if (SEQ_clipboard_pasted_seq_was_active(iseq)) { + SEQ_select_active_set(scene, iseq); + } + /* Make sure, that pasted strips have unique names. */ SEQ_ensure_unique_name(iseq, scene); /* Translate after name has been changed, otherwise this will affect animdata of original -- cgit v1.2.3 From 6a5e1bf9a1b9862c5ca9049eed2a83706cd45a03 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 20:59:04 +1000 Subject: Cleanup: improve BMEditMesh docstrings Also remove white-space added last commit. --- source/blender/editors/include/UI_interface.h | 1 - 1 file changed, 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 5f3df1748d0..802c175492f 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -531,7 +531,6 @@ struct uiBlockInteraction_Params { * (user clicking on a number button arrows or pasting a value for example). */ bool is_click; - /** * Array of unique event ID's (values from #uiBut.retval). * There may be more than one for multi-button editing (see #UI_BUT_DRAG_MULTI). -- cgit v1.2.3 From 10428ca4728265649b7666a79c49518723398f73 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 22:11:57 +1000 Subject: Cleanup: reduce indentation --- .../blender/editors/interface/interface_handlers.c | 138 +++--- source/blender/editors/object/object_transform.c | 513 +++++++++++---------- source/blender/editors/space_view3d/view3d_snap.c | 70 +-- 3 files changed, 362 insertions(+), 359 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 5d3e9e42f29..0e45970a165 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -803,93 +803,95 @@ static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but) (block->handle && block->handle->popup_op)); } +/** + * These functions are postponed and only executed after all other + * handling is done, i.e. menus are closed, in order to avoid conflicts + * with these functions removing the buttons we are working with. + */ static void ui_apply_but_func(bContext *C, uiBut *but) { uiBlock *block = but->block; + if (!ui_afterfunc_check(block, but)) { + return; + } - /* these functions are postponed and only executed after all other - * handling is done, i.e. menus are closed, in order to avoid conflicts - * with these functions removing the buttons we are working with */ - - if (ui_afterfunc_check(block, but)) { - uiAfterFunc *after = ui_afterfunc_new(); - - if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) { - /* exception, this will crash due to removed button otherwise */ - but->func(C, but->func_arg1, but->func_arg2); - } - else { - after->func = but->func; - } + uiAfterFunc *after = ui_afterfunc_new(); - after->func_arg1 = but->func_arg1; - after->func_arg2 = but->func_arg2; + if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) { + /* exception, this will crash due to removed button otherwise */ + but->func(C, but->func_arg1, but->func_arg2); + } + else { + after->func = but->func; + } - after->funcN = but->funcN; - after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL; + after->func_arg1 = but->func_arg1; + after->func_arg2 = but->func_arg2; - after->rename_func = but->rename_func; - after->rename_arg1 = but->rename_arg1; - after->rename_orig = but->rename_orig; /* needs free! */ + after->funcN = but->funcN; + after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL; - after->handle_func = block->handle_func; - after->handle_func_arg = block->handle_func_arg; - after->retval = but->retval; + after->rename_func = but->rename_func; + after->rename_arg1 = but->rename_arg1; + after->rename_orig = but->rename_orig; /* needs free! */ - if (but->type == UI_BTYPE_BUT_MENU) { - after->butm_func = block->butm_func; - after->butm_func_arg = block->butm_func_arg; - after->a2 = but->a2; - } + after->handle_func = block->handle_func; + after->handle_func_arg = block->handle_func_arg; + after->retval = but->retval; - if (block->handle) { - after->popup_op = block->handle->popup_op; - } + if (but->type == UI_BTYPE_BUT_MENU) { + after->butm_func = block->butm_func; + after->butm_func_arg = block->butm_func_arg; + after->a2 = but->a2; + } - after->optype = but->optype; - after->opcontext = but->opcontext; - after->opptr = but->opptr; + if (block->handle) { + after->popup_op = block->handle->popup_op; + } - after->rnapoin = but->rnapoin; - after->rnaprop = but->rnaprop; + after->optype = but->optype; + after->opcontext = but->opcontext; + after->opptr = but->opptr; - if (but->type == UI_BTYPE_SEARCH_MENU) { - uiButSearch *search_but = (uiButSearch *)but; - after->search_arg_free_fn = search_but->arg_free_fn; - after->search_arg = search_but->arg; - search_but->arg_free_fn = NULL; - search_but->arg = NULL; - } + after->rnapoin = but->rnapoin; + after->rnaprop = but->rnaprop; - if (but->active != NULL) { - uiHandleButtonData *data = but->active; - if (data->custom_interaction_handle != NULL) { - after->custom_interaction_callbacks = block->custom_interaction_callbacks; - after->custom_interaction_handle = data->custom_interaction_handle; + if (but->type == UI_BTYPE_SEARCH_MENU) { + uiButSearch *search_but = (uiButSearch *)but; + after->search_arg_free_fn = search_but->arg_free_fn; + after->search_arg = search_but->arg; + search_but->arg_free_fn = NULL; + search_but->arg = NULL; + } - /* Ensure this callback runs once and last. */ - uiAfterFunc *after_prev = after->prev; - if (after_prev && - (after_prev->custom_interaction_handle == data->custom_interaction_handle)) { - after_prev->custom_interaction_handle = NULL; - memset(&after_prev->custom_interaction_callbacks, - 0x0, - sizeof(after_prev->custom_interaction_callbacks)); - } - else { - after->custom_interaction_handle->user_count++; - } + if (but->active != NULL) { + uiHandleButtonData *data = but->active; + if (data->custom_interaction_handle != NULL) { + after->custom_interaction_callbacks = block->custom_interaction_callbacks; + after->custom_interaction_handle = data->custom_interaction_handle; + + /* Ensure this callback runs once and last. */ + uiAfterFunc *after_prev = after->prev; + if (after_prev && + (after_prev->custom_interaction_handle == data->custom_interaction_handle)) { + after_prev->custom_interaction_handle = NULL; + memset(&after_prev->custom_interaction_callbacks, + 0x0, + sizeof(after_prev->custom_interaction_callbacks)); + } + else { + after->custom_interaction_handle->user_count++; } } + } - if (but->context) { - after->context = CTX_store_copy(but->context); - } - - but->optype = NULL; - but->opcontext = 0; - but->opptr = NULL; + if (but->context) { + after->context = CTX_store_copy(but->context); } + + but->optype = NULL; + but->opcontext = 0; + but->opptr = NULL; } /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 86871a02a72..4c4727f51ee 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1152,336 +1152,337 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) for (int object_index = 0; object_index < num_objects; object_index++) { Object *ob = objects[object_index]; + if (ob->flag & OB_DONE) { + continue; + } - if ((ob->flag & OB_DONE) == 0) { - bool do_inverse_offset = false; - ob->flag |= OB_DONE; - - if (centermode == ORIGIN_TO_CURSOR) { - copy_v3_v3(cent, cursor); - invert_m4_m4(ob->imat, ob->obmat); - mul_m4_v3(ob->imat, cent); - } + bool do_inverse_offset = false; + ob->flag |= OB_DONE; - if (ob->data == NULL) { - /* special support for dupligroups */ - if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection && - (ob->instance_collection->id.tag & LIB_TAG_DOIT) == 0) { - if (ID_IS_LINKED(ob->instance_collection)) { - tot_lib_error++; - } - else { - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else { - float min[3], max[3]; - /* only bounds support */ - INIT_MINMAX(min, max); - BKE_object_minmax_dupli(depsgraph, scene, ob, min, max, true); - mid_v3_v3v3(cent, min, max); - invert_m4_m4(ob->imat, ob->obmat); - mul_m4_v3(ob->imat, cent); - } - - add_v3_v3(ob->instance_collection->instance_offset, cent); + if (centermode == ORIGIN_TO_CURSOR) { + copy_v3_v3(cent, cursor); + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_v3(ob->imat, cent); + } - tot_change++; - ob->instance_collection->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; - } + if (ob->data == NULL) { + /* special support for dupligroups */ + if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection && + (ob->instance_collection->id.tag & LIB_TAG_DOIT) == 0) { + if (ID_IS_LINKED(ob->instance_collection)) { + tot_lib_error++; } - } - else if (ID_IS_LINKED(ob->data)) { - tot_lib_error++; - } - else if (ob->type == OB_MESH) { - if (obedit == NULL) { - Mesh *me = ob->data; - + else { if (centermode == ORIGIN_TO_CURSOR) { /* done */ } - else if (centermode == ORIGIN_TO_CENTER_OF_MASS_SURFACE) { - BKE_mesh_center_of_surface(me, cent); - } - else if (centermode == ORIGIN_TO_CENTER_OF_MASS_VOLUME) { - BKE_mesh_center_of_volume(me, cent); - } - else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_mesh_center_bounds(me, cent); - } - else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_mesh_center_median(me, cent); + else { + float min[3], max[3]; + /* only bounds support */ + INIT_MINMAX(min, max); + BKE_object_minmax_dupli(depsgraph, scene, ob, min, max, true); + mid_v3_v3v3(cent, min, max); + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_v3(ob->imat, cent); } - negate_v3_v3(cent_neg, cent); - BKE_mesh_translate(me, cent_neg, 1); + add_v3_v3(ob->instance_collection->instance_offset, cent); tot_change++; - me->id.tag |= LIB_TAG_DOIT; + ob->instance_collection->id.tag |= LIB_TAG_DOIT; do_inverse_offset = true; } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { - Curve *cu = ob->data; + } + else if (ID_IS_LINKED(ob->data)) { + tot_lib_error++; + } + else if (ob->type == OB_MESH) { + if (obedit == NULL) { + Mesh *me = ob->data; if (centermode == ORIGIN_TO_CURSOR) { /* done */ } + else if (centermode == ORIGIN_TO_CENTER_OF_MASS_SURFACE) { + BKE_mesh_center_of_surface(me, cent); + } + else if (centermode == ORIGIN_TO_CENTER_OF_MASS_VOLUME) { + BKE_mesh_center_of_volume(me, cent); + } else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_curve_center_bounds(cu, cent); + BKE_mesh_center_bounds(me, cent); } else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_curve_center_median(cu, cent); - } - - /* don't allow Z change if curve is 2D */ - if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) { - cent[2] = 0.0; + BKE_mesh_center_median(me, cent); } negate_v3_v3(cent_neg, cent); - BKE_curve_translate(cu, cent_neg, 1); + BKE_mesh_translate(me, cent_neg, 1); tot_change++; - cu->id.tag |= LIB_TAG_DOIT; + me->id.tag |= LIB_TAG_DOIT; do_inverse_offset = true; + } + } + else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + Curve *cu = ob->data; - if (obedit) { - if (centermode == GEOMETRY_TO_ORIGIN) { - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); - } - break; + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ + } + else if (around == V3D_AROUND_CENTER_BOUNDS) { + BKE_curve_center_bounds(cu, cent); + } + else { /* #V3D_AROUND_CENTER_MEDIAN. */ + BKE_curve_center_median(cu, cent); + } + + /* don't allow Z change if curve is 2D */ + if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) { + cent[2] = 0.0; + } + + negate_v3_v3(cent_neg, cent); + BKE_curve_translate(cu, cent_neg, 1); + + tot_change++; + cu->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + + if (obedit) { + if (centermode == GEOMETRY_TO_ORIGIN) { + DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); } + break; } - else if (ob->type == OB_FONT) { - /* Get from bounding-box. */ + } + else if (ob->type == OB_FONT) { + /* Get from bounding-box. */ - Curve *cu = ob->data; + Curve *cu = ob->data; - if (ob->runtime.bb == NULL && (centermode != ORIGIN_TO_CURSOR)) { - /* Do nothing. */ + if (ob->runtime.bb == NULL && (centermode != ORIGIN_TO_CURSOR)) { + /* Do nothing. */ + } + else { + if (centermode == ORIGIN_TO_CURSOR) { + /* Done. */ } else { - if (centermode == ORIGIN_TO_CURSOR) { - /* Done. */ - } - else { - /* extra 0.5 is the height o above line */ - cent[0] = 0.5f * (ob->runtime.bb->vec[4][0] + ob->runtime.bb->vec[0][0]); - cent[1] = 0.5f * (ob->runtime.bb->vec[0][1] + ob->runtime.bb->vec[2][1]); - } + /* extra 0.5 is the height o above line */ + cent[0] = 0.5f * (ob->runtime.bb->vec[4][0] + ob->runtime.bb->vec[0][0]); + cent[1] = 0.5f * (ob->runtime.bb->vec[0][1] + ob->runtime.bb->vec[2][1]); + } - cent[2] = 0.0f; + cent[2] = 0.0f; - cu->xof = cu->xof - cent[0]; - cu->yof = cu->yof - cent[1]; + cu->xof = cu->xof - cent[0]; + cu->yof = cu->yof - cent[1]; - tot_change++; - cu->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; - } + tot_change++; + cu->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; } - else if (ob->type == OB_ARMATURE) { - bArmature *arm = ob->data; + } + else if (ob->type == OB_ARMATURE) { + bArmature *arm = ob->data; - if (ID_REAL_USERS(arm) > 1) { + if (ID_REAL_USERS(arm) > 1) { #if 0 BKE_report(op->reports, RPT_ERROR, "Cannot apply to a multi user armature"); return; #endif - tot_multiuser_arm_error++; - } - else { - /* Function to recenter armatures in editarmature.c - * Bone + object locations are handled there. - */ - ED_armature_origin_set(bmain, ob, cursor, centermode, around); + tot_multiuser_arm_error++; + } + else { + /* Function to recenter armatures in editarmature.c + * Bone + object locations are handled there. + */ + ED_armature_origin_set(bmain, ob, cursor, centermode, around); - tot_change++; - arm->id.tag |= LIB_TAG_DOIT; - /* do_inverse_offset = true; */ /* docenter_armature() handles this */ + tot_change++; + arm->id.tag |= LIB_TAG_DOIT; + /* do_inverse_offset = true; */ /* docenter_armature() handles this */ - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - BKE_object_transform_copy(ob_eval, ob); - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); - BKE_object_where_is_calc(depsgraph, scene, ob_eval); - BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */ + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + BKE_object_transform_copy(ob_eval, ob); + BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_object_where_is_calc(depsgraph, scene, ob_eval); + BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */ - ignore_parent_tx(bmain, depsgraph, scene, ob); + ignore_parent_tx(bmain, depsgraph, scene, ob); - if (obedit) { - break; - } + if (obedit) { + break; } } - else if (ob->type == OB_MBALL) { - MetaBall *mb = ob->data; + } + else if (ob->type == OB_MBALL) { + MetaBall *mb = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_mball_center_bounds(mb, cent); - } - else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_mball_center_median(mb, cent); - } + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ + } + else if (around == V3D_AROUND_CENTER_BOUNDS) { + BKE_mball_center_bounds(mb, cent); + } + else { /* #V3D_AROUND_CENTER_MEDIAN. */ + BKE_mball_center_median(mb, cent); + } - negate_v3_v3(cent_neg, cent); - BKE_mball_translate(mb, cent_neg); + negate_v3_v3(cent_neg, cent); + BKE_mball_translate(mb, cent_neg); - tot_change++; - mb->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; + tot_change++; + mb->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; - if (obedit) { - if (centermode == GEOMETRY_TO_ORIGIN) { - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); - } - break; + if (obedit) { + if (centermode == GEOMETRY_TO_ORIGIN) { + DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); } + break; } - else if (ob->type == OB_LATTICE) { - Lattice *lt = ob->data; + } + else if (ob->type == OB_LATTICE) { + Lattice *lt = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_lattice_center_bounds(lt, cent); - } - else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_lattice_center_median(lt, cent); - } + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ + } + else if (around == V3D_AROUND_CENTER_BOUNDS) { + BKE_lattice_center_bounds(lt, cent); + } + else { /* #V3D_AROUND_CENTER_MEDIAN. */ + BKE_lattice_center_median(lt, cent); + } - negate_v3_v3(cent_neg, cent); - BKE_lattice_translate(lt, cent_neg, 1); + negate_v3_v3(cent_neg, cent); + BKE_lattice_translate(lt, cent_neg, 1); - tot_change++; - lt->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; - } - else if (ob->type == OB_GPENCIL) { - bGPdata *gpd = ob->data; - float gpcenter[3]; - if (gpd) { - if (centermode == ORIGIN_TO_GEOMETRY) { - zero_v3(gpcenter); - BKE_gpencil_centroid_3d(gpd, gpcenter); - add_v3_v3(gpcenter, ob->obmat[3]); - } - if (centermode == ORIGIN_TO_CURSOR) { - copy_v3_v3(gpcenter, cursor); - } - if (ELEM(centermode, ORIGIN_TO_GEOMETRY, ORIGIN_TO_CURSOR)) { - bGPDspoint *pt; - float imat[3][3], bmat[3][3]; - float offset_global[3]; - float offset_local[3]; - int i; - - sub_v3_v3v3(offset_global, gpcenter, ob->obmat[3]); - copy_m3_m4(bmat, obact->obmat); - invert_m3_m3(imat, bmat); - mul_m3_v3(imat, offset_global); - mul_v3_m3v3(offset_local, imat, offset_global); - - float diff_mat[4][4]; - float inverse_diff_mat[4][4]; - - /* recalculate all strokes - * (all layers are considered without evaluating lock attributes) */ - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - /* calculate difference matrix */ - BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); - /* undo matrix */ - invert_m4_m4(inverse_diff_mat, diff_mat); - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float mpt[3]; - mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); - sub_v3_v3(mpt, offset_local); - mul_v3_m4v3(&pt->x, diff_mat, mpt); - } + tot_change++; + lt->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + float gpcenter[3]; + if (gpd) { + if (centermode == ORIGIN_TO_GEOMETRY) { + zero_v3(gpcenter); + BKE_gpencil_centroid_3d(gpd, gpcenter); + add_v3_v3(gpcenter, ob->obmat[3]); + } + if (centermode == ORIGIN_TO_CURSOR) { + copy_v3_v3(gpcenter, cursor); + } + if (ELEM(centermode, ORIGIN_TO_GEOMETRY, ORIGIN_TO_CURSOR)) { + bGPDspoint *pt; + float imat[3][3], bmat[3][3]; + float offset_global[3]; + float offset_local[3]; + int i; + + sub_v3_v3v3(offset_global, gpcenter, ob->obmat[3]); + copy_m3_m4(bmat, obact->obmat); + invert_m3_m3(imat, bmat); + mul_m3_v3(imat, offset_global); + mul_v3_m3v3(offset_local, imat, offset_global); + + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* recalculate all strokes + * (all layers are considered without evaluating lock attributes) */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* calculate difference matrix */ + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); + /* undo matrix */ + invert_m4_m4(inverse_diff_mat, diff_mat); + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float mpt[3]; + mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); + sub_v3_v3(mpt, offset_local); + mul_v3_m4v3(&pt->x, diff_mat, mpt); } } } - tot_change++; - if (centermode == ORIGIN_TO_GEOMETRY) { - copy_v3_v3(ob->loc, gpcenter); - } - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - - ob->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; } - else { - BKE_report(op->reports, - RPT_WARNING, - "Grease Pencil Object does not support this set origin option"); + tot_change++; + if (centermode == ORIGIN_TO_GEOMETRY) { + copy_v3_v3(ob->loc, gpcenter); } + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + + ob->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + } + else { + BKE_report(op->reports, + RPT_WARNING, + "Grease Pencil Object does not support this set origin option"); } } + } - /* offset other selected objects */ - if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) { - float obmat[4][4]; + /* offset other selected objects */ + if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) { + float obmat[4][4]; - /* was the object data modified - * NOTE: the functions above must set 'cent'. */ + /* was the object data modified + * NOTE: the functions above must set 'cent'. */ - /* convert the offset to parent space */ - BKE_object_to_mat4(ob, obmat); - mul_v3_mat3_m4v3(centn, obmat, cent); /* omit translation part */ + /* convert the offset to parent space */ + BKE_object_to_mat4(ob, obmat); + mul_v3_mat3_m4v3(centn, obmat, cent); /* omit translation part */ - add_v3_v3(ob->loc, centn); + add_v3_v3(ob->loc, centn); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - BKE_object_transform_copy(ob_eval, ob); - BKE_object_where_is_calc(depsgraph, scene, ob_eval); - if (ob->type == OB_ARMATURE) { - /* needed for bone parents */ - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); - BKE_pose_where_is(depsgraph, scene, ob_eval); - } - - ignore_parent_tx(bmain, depsgraph, scene, ob); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + BKE_object_transform_copy(ob_eval, ob); + BKE_object_where_is_calc(depsgraph, scene, ob_eval); + if (ob->type == OB_ARMATURE) { + /* needed for bone parents */ + BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_pose_where_is(depsgraph, scene, ob_eval); + } - /* other users? */ - // CTX_DATA_BEGIN (C, Object *, ob_other, selected_editable_objects) - //{ - - /* use existing context looper */ - for (int other_object_index = 0; other_object_index < num_objects; other_object_index++) { - Object *ob_other = objects[other_object_index]; - - if ((ob_other->flag & OB_DONE) == 0 && - ((ob->data && (ob->data == ob_other->data)) || - (ob->instance_collection == ob_other->instance_collection && - (ob->transflag | ob_other->transflag) & OB_DUPLICOLLECTION))) { - ob_other->flag |= OB_DONE; - DEG_id_tag_update(&ob_other->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - mul_v3_mat3_m4v3(centn, ob_other->obmat, cent); /* omit translation part */ - add_v3_v3(ob_other->loc, centn); - - Object *ob_other_eval = DEG_get_evaluated_object(depsgraph, ob_other); - BKE_object_transform_copy(ob_other_eval, ob_other); - BKE_object_where_is_calc(depsgraph, scene, ob_other_eval); - if (ob_other->type == OB_ARMATURE) { - /* needed for bone parents */ - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); - BKE_pose_where_is(depsgraph, scene, ob_other_eval); - } - ignore_parent_tx(bmain, depsgraph, scene, ob_other); + ignore_parent_tx(bmain, depsgraph, scene, ob); + + /* other users? */ + // CTX_DATA_BEGIN (C, Object *, ob_other, selected_editable_objects) + //{ + + /* use existing context looper */ + for (int other_object_index = 0; other_object_index < num_objects; other_object_index++) { + Object *ob_other = objects[other_object_index]; + + if ((ob_other->flag & OB_DONE) == 0 && + ((ob->data && (ob->data == ob_other->data)) || + (ob->instance_collection == ob_other->instance_collection && + (ob->transflag | ob_other->transflag) & OB_DUPLICOLLECTION))) { + ob_other->flag |= OB_DONE; + DEG_id_tag_update(&ob_other->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + mul_v3_mat3_m4v3(centn, ob_other->obmat, cent); /* omit translation part */ + add_v3_v3(ob_other->loc, centn); + + Object *ob_other_eval = DEG_get_evaluated_object(depsgraph, ob_other); + BKE_object_transform_copy(ob_other_eval, ob_other); + BKE_object_where_is_calc(depsgraph, scene, ob_other_eval); + if (ob_other->type == OB_ARMATURE) { + /* needed for bone parents */ + BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_pose_where_is(depsgraph, scene, ob_other_eval); } + ignore_parent_tx(bmain, depsgraph, scene, ob_other); } - // CTX_DATA_END; } + // CTX_DATA_END; } } MEM_freeN(objects); diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index a19e92f229a..4482e5897ca 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -511,47 +511,47 @@ static int snap_selected_to_location(bContext *C, for (int ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; + if (ob->parent && BKE_object_flag_test_recursive(ob->parent, OB_DONE)) { + continue; + } - if ((ob->parent && BKE_object_flag_test_recursive(ob->parent, OB_DONE)) == 0) { - - float cursor_parent[3]; /* parent-relative */ - - if (use_offset) { - add_v3_v3v3(cursor_parent, ob->obmat[3], offset_global); - } - else { - copy_v3_v3(cursor_parent, snap_target_global); - } + float cursor_parent[3]; /* parent-relative */ - sub_v3_v3(cursor_parent, ob->obmat[3]); + if (use_offset) { + add_v3_v3v3(cursor_parent, ob->obmat[3], offset_global); + } + else { + copy_v3_v3(cursor_parent, snap_target_global); + } - if (ob->parent) { - float originmat[3][3], parentmat[4][4]; - /* Use the evaluated object here because sometimes - * `ob->parent->runtime.curve_cache` is required. */ - BKE_scene_graph_evaluated_ensure(depsgraph, bmain); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + sub_v3_v3(cursor_parent, ob->obmat[3]); - BKE_object_get_parent_matrix(ob_eval, ob_eval->parent, parentmat); - mul_m3_m4m4(originmat, parentmat, ob->parentinv); - invert_m3_m3(imat, originmat); - mul_m3_v3(imat, cursor_parent); - } - if ((ob->protectflag & OB_LOCK_LOCX) == 0) { - ob->loc[0] += cursor_parent[0]; - } - if ((ob->protectflag & OB_LOCK_LOCY) == 0) { - ob->loc[1] += cursor_parent[1]; - } - if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { - ob->loc[2] += cursor_parent[2]; - } + if (ob->parent) { + float originmat[3][3], parentmat[4][4]; + /* Use the evaluated object here because sometimes + * `ob->parent->runtime.curve_cache` is required. */ + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + + BKE_object_get_parent_matrix(ob_eval, ob_eval->parent, parentmat); + mul_m3_m4m4(originmat, parentmat, ob->parentinv); + invert_m3_m3(imat, originmat); + mul_m3_v3(imat, cursor_parent); + } + if ((ob->protectflag & OB_LOCK_LOCX) == 0) { + ob->loc[0] += cursor_parent[0]; + } + if ((ob->protectflag & OB_LOCK_LOCY) == 0) { + ob->loc[1] += cursor_parent[1]; + } + if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { + ob->loc[2] += cursor_parent[2]; + } - /* auto-keyframing */ - ED_autokeyframe_object(C, scene, ob, ks); + /* auto-keyframing */ + ED_autokeyframe_object(C, scene, ob, ks); - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - } + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } if (objects) { -- cgit v1.2.3 From 6a0fe79db5a5fc55797d772e08813bd7e7e970f4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 22:21:47 +1000 Subject: Cleanup: replace BKE_customdata.h in BKE_editmesh.h Only DNA_customdata_types.h is needed for BMEditMesh. --- source/blender/editors/mesh/editmesh_knife_project.c | 1 + source/blender/editors/mesh/editmesh_path.c | 1 + source/blender/editors/mesh/editmesh_select.c | 1 + source/blender/editors/mesh/editmesh_select_similar.c | 1 + source/blender/editors/mesh/editmesh_tools.c | 1 + source/blender/editors/mesh/editmesh_undo.c | 1 + source/blender/editors/mesh/mesh_data.c | 1 + source/blender/editors/transform/transform_convert_mesh_edge.c | 1 + source/blender/editors/transform/transform_convert_mesh_uv.c | 1 + source/blender/editors/uvedit/uvedit_islands.c | 1 + 10 files changed, 10 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 942fe143787..09b17acf56d 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -30,6 +30,7 @@ #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index 593545ddcef..30a453a32ee 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -29,6 +29,7 @@ #include "DNA_windowmanager_types.h" #ifdef WITH_FREESTYLE +# include "BKE_customdata.h" # include "DNA_meshdata_types.h" #endif diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 6753c235a8d..3304a1c8683 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -35,6 +35,7 @@ #include "BLI_utildefines_stack.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_report.h" diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index ff4db96061f..c48e0fdc52b 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -29,6 +29,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_material.h" diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 627d67a5254..eca8adeba34 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -44,6 +44,7 @@ #include "BLI_string.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_key.h" diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 7a89dbd831d..fc9e1aa8b1a 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -33,6 +33,7 @@ #include "BLI_listbase.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_key.h" #include "BKE_layer.h" diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 2b78d83a9f5..73b3fb9724e 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -34,6 +34,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" #include "BKE_report.h" diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c index 3b1191a3401..2db3e259153 100644 --- a/source/blender/editors/transform/transform_convert_mesh_edge.c +++ b/source/blender/editors/transform/transform_convert_mesh_edge.c @@ -28,6 +28,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index d91a2a8be4b..61397b6ef4b 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -30,6 +30,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh_mapping.h" diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 93948b5ae1b..56bcbc63de1 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -36,6 +36,7 @@ #include "BLI_math.h" #include "BLI_rect.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "DEG_depsgraph.h" -- cgit v1.2.3 From 2373a2196e21b35998961fff2f53c5fa98036cd8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 13 Jul 2021 22:41:06 +1000 Subject: Cleanup: duplicate checks, unused initialization --- .../blender/editors/interface/interface_widgets.c | 3 +- .../tree/tree_display_override_library.cc | 34 ++++++++++------------ .../editors/space_sequencer/sequencer_scopes.c | 6 ++-- 3 files changed, 19 insertions(+), 24 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 7b2e0fec1d0..72847fb2476 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1377,8 +1377,6 @@ static int ui_but_draw_menu_icon(const uiBut *but) static void widget_draw_icon( const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const uchar mono_color[4]) { - float xs = 0.0f, ys = 0.0f; - if (but->flag & UI_BUT_ICON_PREVIEW) { GPU_blend(GPU_BLEND_ALPHA); widget_draw_preview(icon, alpha, rect); @@ -1420,6 +1418,7 @@ static void widget_draw_icon( if (icon && icon != ICON_BLANK1) { const float ofs = 1.0f / aspect; + float xs, ys; if (but->drawflag & UI_BUT_ICON_LEFT) { /* special case - icon_only pie buttons */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc index 3059f8bfe0c..a17bf174a74 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc @@ -150,27 +150,25 @@ TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar, } /* Create data-block list parent element on demand. */ - if (id != nullptr) { - TreeElement *ten; + TreeElement *ten; - if (filter_id_type) { - ten = tenlib; - } - else { - ten = outliner_add_element( - &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0); - ten->directdata = lbarray[a]; - ten->name = outliner_idcode_to_plural(GS(id->name)); - } + if (filter_id_type) { + ten = tenlib; + } + else { + ten = outliner_add_element( + &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0); + ten->directdata = lbarray[a]; + ten->name = outliner_idcode_to_plural(GS(id->name)); + } - for (ID *id : List(lbarray[a])) { - if (override_library_id_filter_poll(lib, id)) { - TreeElement *override_tree_element = outliner_add_element( - &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0); + for (ID *id : List(lbarray[a])) { + if (override_library_id_filter_poll(lib, id)) { + TreeElement *override_tree_element = outliner_add_element( + &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0); - if (BLI_listbase_is_empty(&override_tree_element->subtree)) { - outliner_free_tree_element(override_tree_element, &ten->subtree); - } + if (BLI_listbase_is_empty(&override_tree_element->subtree)) { + outliner_free_tree_element(override_tree_element, &ten->subtree); } } } diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index 8cc8b4a007b..5d857f62b47 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -624,8 +624,6 @@ static void vectorscope_put_cross(uchar r, uchar g, uchar b, char *tgt, int w, i { float rgb[3], yuv[3]; char *p; - int x = 0; - int y = 0; rgb[0] = (float)r / 255.0f; rgb[1] = (float)g / 255.0f; @@ -638,8 +636,8 @@ static void vectorscope_put_cross(uchar r, uchar g, uchar b, char *tgt, int w, i r = 255; } - for (y = -size; y <= size; y++) { - for (x = -size; x <= size; x++) { + for (int y = -size; y <= size; y++) { + for (int x = -size; x <= size; x++) { char *q = p + 4 * (y * w + x); q[0] = r; q[1] = g; -- cgit v1.2.3 From 52b94049f2a71a74f52247f83657cf3a5c8712b4 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 13 Jul 2021 12:51:29 -0300 Subject: Fix 'Correct Face Attributes' option With this option enabled, updating the geometry is no longer deform only. --- source/blender/editors/transform/transform_convert_mesh.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 18cd62a0baf..d740f7e3adb 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -2094,7 +2094,12 @@ void recalcData_mesh(TransInfo *t) tc_mesh_partial_types_calc(t, &partial_state); FOREACH_TRANS_DATA_CONTAINER (t, tc) { - DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY_DEFORM); + const bool use_cd_layer_correct = + tc->custom.type.data && + ((struct TransCustomDataMesh *)tc->custom.type.data)->cd_layer_correct; + + DEG_id_tag_update(tc->obedit->data, + use_cd_layer_correct ? ID_RECALC_GEOMETRY : ID_RECALC_GEOMETRY_DEFORM); tc_mesh_partial_update(t, tc, &partial_state); } -- cgit v1.2.3 From 3b6ee8cee7080af200e25e944fe30d310240e138 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 13 Jul 2021 12:10:34 -0400 Subject: Refactor: Move vertex group names to object data This commit moves the storage of `bDeformGroup` and the active index to `Mesh`, `Lattice`, and `bGPdata` instead of `Object`. Utility functions are added to allow easy access to the vertex groups given an object or an ID. As explained in T88951, the list of vertex group names is currently stored separately per object, even though vertex group data is stored on the geometry. This tends to complicate code and cause bugs, especially as geometry is created procedurally and tied less closely to an object. The "Copy Vertex Groups to Linked" operator is removed, since they are stored on the geometry anyway. This patch leaves the object-level python API for vertex groups in place. Creating a geometry-level RNA API can be a separate step; the changes in this commit are invasive enough as it is. Note that opening a file saved in 3.0 in an earlier version means the vertex groups will not be available. Differential Revision: https://developer.blender.org/D11689 --- .../blender/editors/armature/armature_skinning.c | 2 +- source/blender/editors/gpencil/gpencil_armature.c | 4 +- source/blender/editors/gpencil/gpencil_data.c | 51 +++--- source/blender/editors/gpencil/gpencil_fill.c | 4 +- source/blender/editors/gpencil/gpencil_paint.c | 4 +- source/blender/editors/gpencil/gpencil_primitive.c | 5 +- .../blender/editors/gpencil/gpencil_sculpt_paint.c | 4 +- source/blender/editors/gpencil/gpencil_utils.c | 16 +- .../blender/editors/gpencil/gpencil_weight_paint.c | 6 +- .../blender/editors/lattice/editlattice_select.c | 2 +- source/blender/editors/mesh/editmesh_select.c | 4 +- .../blender/editors/mesh/editmesh_select_similar.c | 12 +- source/blender/editors/mesh/editmesh_tools.c | 2 +- source/blender/editors/mesh/meshtools.c | 29 ++-- .../blender/editors/object/object_data_transfer.c | 6 +- source/blender/editors/object/object_hook.c | 10 +- source/blender/editors/object/object_intern.h | 1 - source/blender/editors/object/object_ops.c | 1 - source/blender/editors/object/object_vgroup.c | 192 ++++++++++----------- source/blender/editors/sculpt_paint/paint_utils.c | 2 +- source/blender/editors/sculpt_paint/paint_vertex.c | 10 +- .../editors/sculpt_paint/paint_vertex_color_ops.c | 2 +- .../editors/sculpt_paint/paint_vertex_weight_ops.c | 20 +-- .../sculpt_paint/paint_vertex_weight_utils.c | 22 ++- .../editors/space_outliner/outliner_select.c | 5 +- .../blender/editors/space_outliner/outliner_tree.c | 26 +-- .../spreadsheet_data_source_geometry.cc | 2 - .../blender/editors/space_view3d/view3d_buttons.c | 6 +- 28 files changed, 228 insertions(+), 222 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index fc9191967f8..ec5c665402b 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -478,7 +478,7 @@ void ED_object_vgroup_calc_from_armature(ReportList *reports, bArmature *arm = par->data; if (mode == ARM_GROUPS_NAME) { - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const int defbase_tot = BKE_object_defgroup_count(ob); int defbase_add; /* Traverse the bone list, trying to create empty vertex * groups corresponding to the bone. diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index 24fb0873a86..c800851bb08 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -384,7 +384,7 @@ static void gpencil_add_verts_to_dgroups( /* loop groups and assign weight */ for (j = 0; j < numbones; j++) { - int def_nr = BLI_findindex(&ob->defbase, dgrouplist[j]); + int def_nr = BLI_findindex(&gpd->vertex_group_names, dgrouplist[j]); if (def_nr < 0) { continue; } @@ -454,7 +454,7 @@ static void gpencil_object_vgroup_calc_from_armature(const bContext *C, bArmature *arm = ob_arm->data; /* always create groups */ - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const int defbase_tot = BKE_object_defgroup_count(ob); int defbase_add; /* Traverse the bone list, trying to create empty vertex * groups corresponding to the bone. diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index e272f46d13d..b1e57079d28 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -2165,7 +2165,9 @@ static bool gpencil_vertex_group_poll(bContext *C) Object *ob = CTX_data_active_object(C); if ((ob) && (ob->type == OB_GPENCIL)) { - if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + const bGPdata *gpd = (const bGPdata *)ob->data; + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && + !BLI_listbase_is_empty(&gpd->vertex_group_names)) { if (ELEM(ob->mode, OB_MODE_EDIT_GPENCIL, OB_MODE_SCULPT_GPENCIL)) { return true; } @@ -2180,7 +2182,9 @@ static bool gpencil_vertex_group_weight_poll(bContext *C) Object *ob = CTX_data_active_object(C); if ((ob) && (ob->type == OB_GPENCIL)) { - if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + const bGPdata *gpd = (const bGPdata *)ob->data; + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && + !BLI_listbase_is_empty(&gpd->vertex_group_names)) { if (ob->mode == OB_MODE_WEIGHT_GPENCIL) { return true; } @@ -2333,6 +2337,7 @@ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { @@ -2340,8 +2345,9 @@ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) } MDeformVert *dvert; - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + + bDeformGroup *defgroup = BLI_findlink(&gpd->vertex_group_names, def_nr); if (defgroup == NULL) { return OPERATOR_CANCELLED; } @@ -2373,7 +2379,6 @@ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2403,14 +2408,15 @@ static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { return OPERATOR_CANCELLED; } - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + bDeformGroup *defgroup = BLI_findlink(&gpd->vertex_group_names, def_nr); if (defgroup == NULL) { return OPERATOR_CANCELLED; } @@ -2470,7 +2476,6 @@ static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2500,6 +2505,7 @@ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { @@ -2508,8 +2514,8 @@ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) MDeformVert *dvert = NULL; MDeformWeight *dw = NULL; - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + bDeformGroup *defgroup = BLI_findlink(&gpd->vertex_group_names, def_nr); if (defgroup == NULL) { return OPERATOR_CANCELLED; } @@ -2548,7 +2554,6 @@ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2576,6 +2581,7 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); bool lock_active = RNA_boolean_get(op->ptr, "lock_active"); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { @@ -2585,8 +2591,8 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) bDeformGroup *defgroup = NULL; MDeformVert *dvert = NULL; MDeformWeight *dw = NULL; - const int def_nr = ob->actdef - 1; - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const int def_nr = gpd->vertex_group_active_index - 1; + const int defbase_tot = BLI_listbase_count(&gpd->vertex_group_names); if (defbase_tot == 0) { return OPERATOR_CANCELLED; } @@ -2603,7 +2609,7 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) for (int i = 0; i < gps->totpoints; i++) { dvert = &gps->dvert[i]; for (int v = 0; v < defbase_tot; v++) { - defgroup = BLI_findlink(&ob->defbase, v); + defgroup = BLI_findlink(&gpd->vertex_group_names, v); /* skip NULL or locked groups */ if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { continue; @@ -2629,7 +2635,7 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) dvert = &gps->dvert[i]; for (int v = 0; v < defbase_tot; v++) { - defgroup = BLI_findlink(&ob->defbase, v); + defgroup = BLI_findlink(&gpd->vertex_group_names, v); /* skip NULL or locked groups */ if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { continue; @@ -2653,7 +2659,6 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2769,7 +2774,6 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob_active = CTX_data_active_object(C); - bGPdata *gpd_dst = NULL; bool ok = false; /* Ensure we're in right mode and that the active object is correct */ @@ -2807,7 +2811,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - gpd_dst = ob_active->data; + bGPdata *gpd_dst = ob_active->data; Object *ob_dst = ob_active; /* loop and join all data */ @@ -2828,11 +2832,11 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) /* copy vertex groups to the base one's */ int old_idx = 0; - LISTBASE_FOREACH (bDeformGroup *, dg, &ob_iter->defbase) { + LISTBASE_FOREACH (bDeformGroup *, dg, &gpd_src->vertex_group_names) { bDeformGroup *vgroup = MEM_dupallocN(dg); - int idx = BLI_listbase_count(&ob_active->defbase); + int idx = BLI_listbase_count(&gpd_dst->vertex_group_names); BKE_object_defgroup_unique_name(vgroup, ob_active); - BLI_addtail(&ob_active->defbase, vgroup); + BLI_addtail(&gpd_dst->vertex_group_names, vgroup); /* update vertex groups in strokes in original data */ LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd->layers) { LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_src->frames) { @@ -2852,8 +2856,9 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) } old_idx++; } - if (ob_active->defbase.first && ob_active->actdef == 0) { - ob_active->actdef = 1; + if (!BLI_listbase_is_empty(&gpd_dst->vertex_group_names) && + gpd_dst->vertex_group_active_index == 0) { + gpd_dst->vertex_group_active_index = 1; } /* add missing materials reading source materials and checking in destination object */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 091ff2c16b0..67e1bd5294b 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1522,8 +1522,8 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) pt = gps->points; point2D = (tGPspoint *)tgpf->sbuffer; - const int def_nr = tgpf->ob->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&tgpf->ob->defbase, def_nr); + const int def_nr = tgpf->gpd->vertex_group_active_index - 1; + const bool have_weight = (bool)BLI_findlink(&tgpf->gpd->vertex_group_names, def_nr); if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { BKE_gpencil_dvert_ensure(gps); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 67d4b7726b5..d6f6dbb2b10 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -938,8 +938,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) Depsgraph *depsgraph = p->depsgraph; Object *obact = (Object *)p->ownerPtr.data; RegionView3D *rv3d = p->region->regiondata; - const int def_nr = obact->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + const bool have_weight = (bool)BLI_findlink(&gpd->vertex_group_names, def_nr); const char align_flag = ts->gpencil_v3d_align; const bool is_depth = (bool)(align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); const bool is_lock_axis_view = (bool)(ts->gp_sculpt.lock_axis == 0); diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index a2b4e5dee64..78eaab01b1a 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1315,8 +1315,9 @@ static void gpencil_primitive_interaction_end(bContext *C, Brush *brush = tgpi->brush; BrushGpencilSettings *brush_settings = brush->gpencil_settings; - const int def_nr = tgpi->ob->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr); + const int def_nr = tgpi->gpd->vertex_group_active_index - 1; + const ListBase *defbase = BKE_object_defgroup_list(tgpi->ob); + const bool have_weight = (bool)BLI_findlink(defbase, def_nr); /* return to normal cursor and header status */ ED_workspace_status_text(C, NULL); diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index efd0f86df03..462462cf341 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -1177,8 +1177,8 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->object = ob; if (ob) { invert_m4_m4(gso->inv_mat, ob->obmat); - gso->vrgroup = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = gso->gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup)) { gso->vrgroup = -1; } /* Check if some modifier can transform the stroke. */ diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index e6488cfe454..ba3d3b584d7 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1600,8 +1600,8 @@ void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } @@ -1654,8 +1654,8 @@ void ED_gpencil_vgroup_remove(bContext *C, Object *ob) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } @@ -1707,8 +1707,8 @@ void ED_gpencil_vgroup_select(bContext *C, Object *ob) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } @@ -1762,8 +1762,8 @@ void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 6d953a4d8cf..d14322e12b5 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -252,7 +252,7 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso, } } else { - bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); + bDeformGroup *defgroup = BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup); if (defgroup->flag & DG_LOCK_WEIGHT) { return false; } @@ -308,8 +308,8 @@ static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op) gso->scene = scene; gso->object = ob; if (ob) { - gso->vrgroup = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = gso->gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup)) { gso->vrgroup = -1; } } diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c index e73a02840c9..e6f190f335b 100644 --- a/source/blender/editors/lattice/editlattice_select.c +++ b/source/blender/editors/lattice/editlattice_select.c @@ -514,7 +514,7 @@ static int lattice_select_ungrouped_exec(bContext *C, wmOperator *op) BPoint *bp; int a, tot; - if (BLI_listbase_is_empty(&obedit->defbase) || lt->dvert == NULL) { + if (BLI_listbase_is_empty(<->vertex_group_names) || lt->dvert == NULL) { continue; } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 3304a1c8683..830c9abb41e 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -36,6 +36,7 @@ #include "BKE_context.h" #include "BKE_customdata.h" +#include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_report.h" @@ -4740,10 +4741,11 @@ static bool edbm_select_ungrouped_poll(bContext *C) BMEditMesh *em = BKE_editmesh_from_object(obedit); const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); + const ListBase *defbase = BKE_object_defgroup_list(obedit); if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { CTX_wm_operator_poll_msg_set(C, "Must be in vertex selection mode"); } - else if (BLI_listbase_is_empty(&obedit->defbase) || cd_dvert_offset == -1) { + else if (BLI_listbase_is_empty(defbase) || cd_dvert_offset == -1) { CTX_wm_operator_poll_msg_set(C, "No weights/vertex groups on object"); } else { diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index c48e0fdc52b..c452f7a7487 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -30,6 +30,7 @@ #include "BKE_context.h" #include "BKE_customdata.h" +#include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_material.h" @@ -1042,7 +1043,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) if (cd_dvert_offset == -1) { continue; } - defbase_len = BLI_listbase_count(&ob->defbase); + defbase_len = BKE_object_defgroup_count(ob); if (defbase_len == 0) { continue; } @@ -1091,8 +1092,10 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) /* We store the names of the vertex groups, so we can select * vertex groups with the same name in different objects. */ + const ListBase *defbase = BKE_object_defgroup_list(ob); + int i = 0; - LISTBASE_FOREACH (bDeformGroup *, dg, &ob->defbase) { + LISTBASE_FOREACH (bDeformGroup *, dg, defbase) { if (BLI_BITMAP_TEST(defbase_selected, i)) { BLI_gset_add(gset, dg->name); } @@ -1129,7 +1132,8 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) if (cd_dvert_offset == -1) { continue; } - defbase_len = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + defbase_len = BLI_listbase_count(defbase); if (defbase_len == 0) { continue; } @@ -1142,7 +1146,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) GSetIterator gs_iter; GSET_ITER (gs_iter, gset) { const char *name = BLI_gsetIterator_getKey(&gs_iter); - int vgroup_id = BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name)); + int vgroup_id = BLI_findstringindex(defbase, name, offsetof(bDeformGroup, name)); if (vgroup_id != -1) { BLI_BITMAP_ENABLE(defbase_selected, vgroup_id); found_any = true; diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index eca8adeba34..41a9f426798 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -5740,7 +5740,7 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op) float *vweights = MEM_mallocN(sizeof(*vweights) * bm->totvert, __func__); { const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); - const int defbase_act = obedit->actdef - 1; + const int defbase_act = BKE_object_defgroup_active_index_get(obedit) - 1; if (use_vertex_group && (cd_dvert_offset == -1)) { BKE_report(op->reports, RPT_WARNING, "No active vertex group"); diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index ca68c47cf8e..27fb21e1dfb 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -475,16 +475,17 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) me = ob_iter->data; /* Join this object's vertex groups to the base one's */ - for (dg = ob_iter->defbase.first; dg; dg = dg->next) { + for (dg = me->vertex_group_names.first; dg; dg = dg->next) { /* See if this group exists in the object (if it doesn't, add it to the end) */ if (!BKE_object_defgroup_find_name(ob, dg->name)) { odg = MEM_mallocN(sizeof(bDeformGroup), "join deformGroup"); memcpy(odg, dg, sizeof(bDeformGroup)); - BLI_addtail(&ob->defbase, odg); + BLI_addtail(&mesh_active->vertex_group_names, odg); } } - if (ob->defbase.first && ob->actdef == 0) { - ob->actdef = 1; + if (!BLI_listbase_is_empty(&mesh_active->vertex_group_names) && + me->vertex_group_active_index == 0) { + me->vertex_group_active_index = 1; } /* Join this object's face maps to the base one's. */ @@ -1473,19 +1474,21 @@ bool ED_mesh_pick_vert( MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) { - if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH && ob->defbase.first) { + if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH) { Mesh *me = ob->data; - BMesh *bm = me->edit_mesh->bm; - const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); + if (!BLI_listbase_is_empty(&me->vertex_group_names)) { + BMesh *bm = me->edit_mesh->bm; + const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); - if (cd_dvert_offset != -1) { - BMVert *eve = BM_mesh_active_vert_get(bm); + if (cd_dvert_offset != -1) { + BMVert *eve = BM_mesh_active_vert_get(bm); - if (eve) { - if (r_eve) { - *r_eve = eve; + if (eve) { + if (r_eve) { + *r_eve = eve; + } + return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); } - return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); } } } diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index 742c7abb7ff..2109fe2a822 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -33,6 +33,7 @@ #include "BKE_context.h" #include "BKE_data_transfer.h" +#include "BKE_deform.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_remap.h" #include "BKE_mesh_runtime.h" @@ -133,12 +134,13 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, } if (ob_src) { - bDeformGroup *dg; + const bDeformGroup *dg; int i; RNA_enum_item_add_separator(&item, &totitem); - for (i = 0, dg = ob_src->defbase.first; dg; i++, dg = dg->next) { + const ListBase *defbase = BKE_object_defgroup_list(ob_src); + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { tmp_item.value = i; tmp_item.identifier = tmp_item.name = dg->name; RNA_enum_item_add(&item, &totitem, &tmp_item); diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index fcee2818b22..5065a2c00f0 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -105,14 +105,13 @@ static int return_editmesh_indexar(BMEditMesh *em, int *r_tot, int **r_indexar, static bool return_editmesh_vgroup(Object *obedit, BMEditMesh *em, char *r_name, float r_cent[3]) { - const int cd_dvert_offset = obedit->actdef ? + const int active_index = BKE_object_defgroup_active_index_get(obedit); + const int cd_dvert_offset = active_index ? CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT) : -1; - zero_v3(r_cent); - if (cd_dvert_offset != -1) { - const int defgrp_index = obedit->actdef - 1; + const int defgrp_index = active_index - 1; int totvert = 0; MDeformVert *dvert; @@ -129,7 +128,8 @@ static bool return_editmesh_vgroup(Object *obedit, BMEditMesh *em, char *r_name, } } if (totvert) { - bDeformGroup *dg = BLI_findlink(&obedit->defbase, defgrp_index); + const ListBase *defbase = BKE_object_defgroup_list(obedit); + bDeformGroup *dg = BLI_findlink(defbase, defgrp_index); BLI_strncpy(r_name, dg->name, sizeof(dg->name)); mul_v3_fl(r_cent, 1.0f / (float)totvert); return true; diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 5bf04e195fe..5a3a28b5a3f 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -247,7 +247,6 @@ void OBJECT_OT_vertex_group_assign_new(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_remove_from(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_select(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_deselect(struct wmOperatorType *ot); -void OBJECT_OT_vertex_group_copy_to_linked(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_copy_to_selected(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_copy(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_normalize(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index dc60d4df7d1..a438c760d3b 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -201,7 +201,6 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_vertex_group_remove_from); WM_operatortype_append(OBJECT_OT_vertex_group_select); WM_operatortype_append(OBJECT_OT_vertex_group_deselect); - WM_operatortype_append(OBJECT_OT_vertex_group_copy_to_linked); WM_operatortype_append(OBJECT_OT_vertex_group_copy_to_selected); WM_operatortype_append(OBJECT_OT_vertex_group_copy); WM_operatortype_append(OBJECT_OT_vertex_group_normalize); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 37075a29b92..4ea599fd30e 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -130,7 +130,7 @@ bool ED_vgroup_sync_from_pose(Object *ob) if (arm->act_bone) { int def_num = BKE_object_defgroup_name_index(ob, arm->act_bone->name); if (def_num != -1) { - ob->actdef = def_num + 1; + BKE_object_defgroup_active_index_set(ob, def_num + 1); return true; } } @@ -389,11 +389,16 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) int dvert_tot_from; int dvert_tot; int i; - int defbase_tot_from = BLI_listbase_count(&ob_from->defbase); - int defbase_tot = BLI_listbase_count(&ob->defbase); + ListBase *defbase_dst = BKE_object_defgroup_list_mutable(ob); + const ListBase *defbase_src = BKE_object_defgroup_list(ob_from); + + int defbase_tot_from = BLI_listbase_count(defbase_src); + int defbase_tot = BLI_listbase_count(defbase_dst); bool new_vgroup = false; - if (ob == ob_from) { + BLI_assert(ob != ob_from); + + if (ob->data == ob_from->data) { return true; } @@ -429,9 +434,9 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) } /* do the copy */ - BLI_freelistN(&ob->defbase); - BLI_duplicatelist(&ob->defbase, &ob_from->defbase); - ob->actdef = ob_from->actdef; + BLI_freelistN(defbase_dst); + BLI_duplicatelist(defbase_dst, defbase_src); + BKE_object_defgroup_active_index_set(ob, BKE_object_defgroup_active_index_get(ob_from)); if (defbase_tot_from < defbase_tot) { /* correct vgroup indices because the number of vgroups is being reduced. */ @@ -882,7 +887,8 @@ void ED_vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight, /* add the vert to the deform group with the * specified assign mode */ - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); MDeformVert *dv = NULL; int tot; @@ -913,7 +919,8 @@ void ED_vgroup_vert_remove(Object *ob, bDeformGroup *dg, int vertnum) /* TODO(campbell): This is slow in a loop, better pass def_nr directly, * but leave for later. */ - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); if (def_nr != -1) { MDeformVert *dvert = NULL; @@ -989,7 +996,8 @@ static float get_vert_def_nr(Object *ob, const int def_nr, const int vertnum) float ED_vgroup_vert_weight(Object *ob, bDeformGroup *dg, int vertnum) { - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); if (def_nr == -1) { return -1; @@ -1000,9 +1008,9 @@ float ED_vgroup_vert_weight(Object *ob, bDeformGroup *dg, int vertnum) void ED_vgroup_select_by_name(Object *ob, const char *name) { - /* NOTE: ob->actdef==0 signals on painting to create a new one, + /* NOTE: actdef==0 signals on painting to create a new one, * if a bone in posemode is selected */ - ob->actdef = BKE_object_defgroup_name_index(ob, name) + 1; + BKE_object_defgroup_active_index_set(ob, BKE_object_defgroup_name_index(ob, name) + 1); } /** \} */ @@ -1014,9 +1022,10 @@ void ED_vgroup_select_by_name(Object *ob, const char *name) /* only in editmode */ static void vgroup_select_verts(Object *ob, int select) { - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_findlink(defbase, def_nr)) { return; } @@ -1111,7 +1120,9 @@ static void vgroup_duplicate(Object *ob) MDeformVert **dvert_array = NULL; int i, idg, icdg, dvert_tot = 0; - dg = BLI_findlink(&ob->defbase, (ob->actdef - 1)); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if (!dg) { return; } @@ -1127,11 +1138,11 @@ static void vgroup_duplicate(Object *ob) BLI_strncpy(cdg->name, name, sizeof(cdg->name)); BKE_object_defgroup_unique_name(cdg, ob); - BLI_addtail(&ob->defbase, cdg); + BLI_addtail(defbase, cdg); - idg = (ob->actdef - 1); - ob->actdef = BLI_listbase_count(&ob->defbase); - icdg = (ob->actdef - 1); + idg = BKE_object_defgroup_active_index_get(ob) - 1; + BKE_object_defgroup_active_index_set(ob, BLI_listbase_count(defbase)); + icdg = BKE_object_defgroup_active_index_get(ob) - 1; /* TODO(campbell): we might want to allow only copy selected verts here? */ ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); @@ -1157,11 +1168,12 @@ static bool vgroup_normalize(Object *ob) MDeformWeight *dw; MDeformVert *dv, **dvert_array = NULL; int dvert_tot = 0; - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; const bool use_vert_sel = vertex_group_use_vert_sel(ob); - if (!BLI_findlink(&ob->defbase, def_nr)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_findlink(defbase, def_nr)) { return false; } @@ -1639,7 +1651,7 @@ static bool vgroup_normalize_all(Object *ob, { MDeformVert *dv, **dvert_array = NULL; int i, dvert_tot = 0; - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; const bool use_vert_sel = vertex_group_use_vert_sel(ob); @@ -1651,7 +1663,8 @@ static bool vgroup_normalize_all(Object *ob, ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int defbase_tot = BLI_listbase_count(defbase); bool *lock_flags = BKE_object_defgroup_lock_flags_get(ob, defbase_tot); bool changed = false; @@ -1742,7 +1755,7 @@ static const EnumPropertyItem vgroup_lock_mask[] = { static bool *vgroup_selected_get(Object *ob) { - int sel_count = 0, defbase_tot = BLI_listbase_count(&ob->defbase); + int sel_count = 0, defbase_tot = BKE_object_defgroup_count(ob); bool *mask; if (ob->mode & OB_MODE_WEIGHT_PAINT) { @@ -1759,8 +1772,9 @@ static bool *vgroup_selected_get(Object *ob) mask = MEM_callocN(defbase_tot * sizeof(bool), __func__); } - if (sel_count == 0 && ob->actdef >= 1 && ob->actdef <= defbase_tot) { - mask[ob->actdef - 1] = true; + const int actdef = BKE_object_defgroup_active_index_get(ob); + if (sel_count == 0 && actdef >= 1 && actdef <= defbase_tot) { + mask[actdef - 1] = true; } return mask; @@ -1775,11 +1789,12 @@ static void vgroup_lock_all(Object *ob, int action, int mask) if (mask != VGROUP_MASK_ALL) { selected = vgroup_selected_get(ob); } + const ListBase *defbase = BKE_object_defgroup_list(ob); if (action == VGROUP_TOGGLE) { action = VGROUP_LOCK; - for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) { + for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { switch (mask) { case VGROUP_MASK_INVERT_UNSELECTED: case VGROUP_MASK_SELECTED: @@ -1802,7 +1817,7 @@ static void vgroup_lock_all(Object *ob, int action, int mask) } } - for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) { + for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { switch (mask) { case VGROUP_MASK_SELECTED: if (!selected[i]) { @@ -2379,13 +2394,15 @@ void ED_vgroup_mirror(Object *ob, MDeformVert *dvert, *dvert_mirr; char sel, sel_mirr; int *flip_map = NULL, flip_map_len; - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; int totmirr = 0, totfail = 0; *r_totmirr = *r_totfail = 0; + const ListBase *defbase = BKE_object_defgroup_list(ob); + if ((mirror_weights == false && flip_vgroups == false) || - (BLI_findlink(&ob->defbase, def_nr) == NULL)) { + (BLI_findlink(defbase, def_nr) == NULL)) { return; } @@ -2569,7 +2586,8 @@ cleanup: static void vgroup_delete_active(Object *ob) { - bDeformGroup *dg = BLI_findlink(&ob->defbase, ob->actdef - 1); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if (!dg) { return; } @@ -2580,9 +2598,10 @@ static void vgroup_delete_active(Object *ob) /* only in editmode */ static void vgroup_assign_verts(Object *ob, const float weight) { - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_findlink(defbase, def_nr)) { return; } @@ -2700,7 +2719,8 @@ static bool vertex_group_poll_ex(bContext *C, Object *ob) return false; } - if (BLI_listbase_is_empty(&ob->defbase)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (BLI_listbase_is_empty(defbase)) { CTX_wm_operator_poll_msg_set(C, "Object has no vertex groups"); return false; } @@ -2823,8 +2843,10 @@ static bool vertex_group_vert_select_unlocked_poll(bContext *C) return false; } - if (ob->actdef != 0) { - bDeformGroup *dg = BLI_findlink(&ob->defbase, ob->actdef - 1); + const int def_nr = BKE_object_defgroup_active_index_get(ob); + if (def_nr != 0) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + const bDeformGroup *dg = BLI_findlink(defbase, def_nr - 1); if (dg) { return !(dg->flag & DG_LOCK_WEIGHT); } @@ -3025,8 +3047,8 @@ static int vertex_group_remove_from_exec(bContext *C, wmOperator *op) } } else { - bDeformGroup *dg = BLI_findlink(&ob->defbase, ob->actdef - 1); - + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if ((dg == NULL) || (BKE_object_defgroup_clear(ob, dg, !use_all_verts) == false)) { return OPERATOR_CANCELLED; } @@ -3859,55 +3881,6 @@ void OBJECT_OT_vertex_group_mirror(wmOperatorType *ot) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Vertex Group Copy to Linked Operator - * \{ */ - -static int vertex_group_copy_to_linked_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *scene = CTX_data_scene(C); - Object *ob_active = ED_object_context(C); - int retval = OPERATOR_CANCELLED; - - FOREACH_SCENE_OBJECT_BEGIN (scene, ob_iter) { - if (ob_iter->type == ob_active->type) { - if (ob_iter != ob_active && ob_iter->data == ob_active->data) { - BLI_freelistN(&ob_iter->defbase); - BLI_duplicatelist(&ob_iter->defbase, &ob_active->defbase); - ob_iter->actdef = ob_active->actdef; - - DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob_iter); - WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob_iter->data); - - retval = OPERATOR_FINISHED; - } - } - } - FOREACH_SCENE_OBJECT_END; - - return retval; -} - -void OBJECT_OT_vertex_group_copy_to_linked(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy Vertex Groups to Linked"; - ot->idname = "OBJECT_OT_vertex_group_copy_to_linked"; - ot->description = - "Replace vertex groups of all users of the same geometry data by vertex groups of active " - "object"; - - /* api callbacks */ - ot->poll = vertex_group_poll; - ot->exec = vertex_group_copy_to_linked_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Vertex Group Copy to Selected Operator * \{ */ @@ -3972,7 +3945,7 @@ static int set_active_group_exec(bContext *C, wmOperator *op) int nr = RNA_enum_get(op->ptr, "group"); BLI_assert(nr + 1 >= 0); - ob->actdef = nr + 1; + BKE_object_defgroup_active_index_set(ob, nr + 1); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob); @@ -3999,7 +3972,8 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C, return DummyRNA_NULL_items; } - for (a = 0, def = ob->defbase.first; def; def = def->next, a++) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + for (a = 0, def = defbase->first; def; def = def->next, a++) { tmp.value = a; tmp.icon = ICON_GROUP_VERTEX; tmp.identifier = def->name; @@ -4048,13 +4022,13 @@ void OBJECT_OT_vertex_group_set_active(wmOperatorType *ot) * with the order of vgroups then call vgroup_do_remap after */ static char *vgroup_init_remap(Object *ob) { - bDeformGroup *def; - int defbase_tot = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + int defbase_tot = BLI_listbase_count(defbase); char *name_array = MEM_mallocN(MAX_VGROUP_NAME * sizeof(char) * defbase_tot, "sort vgroups"); char *name; name = name_array; - for (def = ob->defbase.first; def; def = def->next) { + for (const bDeformGroup *def = defbase->first; def; def = def->next) { BLI_strncpy(name, def->name, MAX_VGROUP_NAME); name += MAX_VGROUP_NAME; } @@ -4065,8 +4039,9 @@ static char *vgroup_init_remap(Object *ob) static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) { MDeformVert *dvert = NULL; - bDeformGroup *def; - int defbase_tot = BLI_listbase_count(&ob->defbase); + const bDeformGroup *def; + const ListBase *defbase = BKE_object_defgroup_list(ob); + int defbase_tot = BLI_listbase_count(defbase); /* Needs a dummy index at the start. */ int *sort_map_update = MEM_mallocN(sizeof(int) * (defbase_tot + 1), "sort vgroups"); @@ -4076,8 +4051,8 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) int i; name = name_array; - for (def = ob->defbase.first, i = 0; def; def = def->next, i++) { - sort_map[i] = BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name)); + for (def = defbase->first, i = 0; def; def = def->next, i++) { + sort_map[i] = BLI_findstringindex(defbase, name, offsetof(bDeformGroup, name)); name += MAX_VGROUP_NAME; BLI_assert(sort_map[i] != -1); @@ -4130,8 +4105,9 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) sort_map_update[0] = 0; BKE_object_defgroup_remap_update_users(ob, sort_map_update); - BLI_assert(sort_map_update[ob->actdef] >= 0); - ob->actdef = sort_map_update[ob->actdef]; + BLI_assert(sort_map_update[BKE_object_defgroup_active_index_get(ob)] >= 0); + BKE_object_defgroup_active_index_set(ob, + sort_map_update[BKE_object_defgroup_active_index_get(ob)]); MEM_freeN(sort_map_update); @@ -4159,6 +4135,7 @@ static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase) bonebase = &armature->bonebase; } } + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); if (bonebase != NULL) { Bone *bone; @@ -4167,8 +4144,8 @@ static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase) vgroup_sort_bone_hierarchy(ob, &bone->childbase); if (dg != NULL) { - BLI_remlink(&ob->defbase, dg); - BLI_addhead(&ob->defbase, dg); + BLI_remlink(defbase, dg); + BLI_addhead(defbase, dg); } } } @@ -4189,10 +4166,12 @@ static int vertex_group_sort_exec(bContext *C, wmOperator *op) /* Init remapping. */ name_array = vgroup_init_remap(ob); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + /* Sort vgroup names. */ switch (sort_type) { case SORT_TYPE_NAME: - BLI_listbase_sort(&ob->defbase, vgroup_sort_name); + BLI_listbase_sort(defbase, vgroup_sort_name); break; case SORT_TYPE_BONEHIERARCHY: vgroup_sort_bone_hierarchy(ob, NULL); @@ -4250,14 +4229,16 @@ static int vgroup_move_exec(bContext *C, wmOperator *op) int dir = RNA_enum_get(op->ptr, "direction"); int ret = OPERATOR_FINISHED; - def = BLI_findlink(&ob->defbase, ob->actdef - 1); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + def = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if (!def) { return OPERATOR_CANCELLED; } name_array = vgroup_init_remap(ob); - if (BLI_listbase_link_move(&ob->defbase, def, dir)) { + if (BLI_listbase_link_move(defbase, def, dir)) { ret = vgroup_do_remap(ob, name_array, op); if (ret != OPERATOR_CANCELLED) { @@ -4376,7 +4357,8 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) static bool check_vertex_group_accessible(wmOperator *op, Object *ob, int def_nr) { - bDeformGroup *dg = BLI_findlink(&ob->defbase, def_nr); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, def_nr); if (!dg) { BKE_report(op->reports, RPT_ERROR, "Invalid vertex group index"); @@ -4498,7 +4480,7 @@ static int vertex_weight_set_active_exec(bContext *C, wmOperator *op) const int wg_index = RNA_int_get(op->ptr, "weight_group"); if (wg_index != -1) { - ob->actdef = wg_index + 1; + BKE_object_defgroup_active_index_set(ob, wg_index + 1); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); } diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 2484f382ed4..709e04d807d 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -752,7 +752,7 @@ static int vert_select_ungrouped_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); Mesh *me = ob->data; - if (BLI_listbase_is_empty(&ob->defbase) || (me->dvert == NULL)) { + if (BLI_listbase_is_empty(&me->vertex_group_names) || (me->dvert == NULL)) { BKE_report(op->reports, RPT_ERROR, "No weights/vertex groups on object"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 5c1cc6cadcf..9387b84f437 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1607,13 +1607,13 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo /* check if we are attempting to paint onto a locked vertex group, * and other options disallow it from doing anything useful */ bDeformGroup *dg; - dg = BLI_findlink(&ob->defbase, vgroup_index.active); + dg = BLI_findlink(&me->vertex_group_names, vgroup_index.active); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting"); return false; } if (vgroup_index.mirror != -1) { - dg = BLI_findlink(&ob->defbase, vgroup_index.mirror); + dg = BLI_findlink(&me->vertex_group_names, vgroup_index.mirror); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting"); return false; @@ -1622,7 +1622,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo } /* check that multipaint groups are unlocked */ - defbase_tot = BLI_listbase_count(&ob->defbase); + defbase_tot = BLI_listbase_count(&me->vertex_group_names); defbase_sel = BKE_object_defgroup_selected_get(ob, defbase_tot, &defbase_tot_sel); if (ts->multipaint && defbase_tot_sel > 1) { @@ -1636,7 +1636,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo for (i = 0; i < defbase_tot; i++) { if (defbase_sel[i]) { - dg = BLI_findlink(&ob->defbase, i); + dg = BLI_findlink(&me->vertex_group_names, i); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting"); MEM_freeN(defbase_sel); @@ -2498,7 +2498,7 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) for (psys = ob->particlesystem.first; psys; psys = psys->next) { for (i = 0; i < PSYS_TOT_VG; i++) { - if (psys->vgroup[i] == ob->actdef) { + if (psys->vgroup[i] == BKE_object_defgroup_active_index_get(ob)) { psys->recalc |= ID_RECALC_PSYS_RESET; break; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c index 96d22fe4a21..9f023dd6e63 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -149,7 +149,7 @@ static bool vertex_paint_from_weight(Object *ob) /* TODO: respect selection. */ /* TODO: Do we want to take weights from evaluated mesh instead? 2.7x was not doing it anyway. */ mp = me->mpoly; - vgroup_active = ob->actdef - 1; + vgroup_active = me->vertex_group_active_index - 1; for (int i = 0; i < me->totpoly; i++, mp++) { MLoopCol *lcol = &me->mloopcol[mp->loopstart]; uint j = 0; diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index 5e047283534..cb8dc838422 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -183,7 +183,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); - if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) { + if (me && me->dvert && vc.v3d && vc.rv3d && (me->vertex_group_active_index != 0)) { const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int v_idx_best = -1; uint index; @@ -213,9 +213,9 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even if (v_idx_best != -1) { /* should always be valid */ ToolSettings *ts = vc.scene->toolsettings; Brush *brush = BKE_paint_brush(&ts->wpaint->paint); - const int vgroup_active = vc.obact->actdef - 1; + const int vgroup_active = me->vertex_group_active_index - 1; float vgroup_weight = BKE_defvert_find_weight(&me->dvert[v_idx_best], vgroup_active); - const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); + const int defbase_tot = BLI_listbase_count(&me->vertex_group_names); bool use_lock_relative = ts->wpaint_lock_relative; bool *defbase_locked = NULL, *defbase_unlocked = NULL; @@ -331,8 +331,8 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); - if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { - const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); + if (me && me->dvert && vc.v3d && vc.rv3d && me->vertex_group_names.first) { + const int defbase_tot = BLI_listbase_count(&me->vertex_group_names); const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups"); bool found = false; @@ -372,7 +372,7 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, int totitem = 0; int i = 0; bDeformGroup *dg; - for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) { + for (dg = me->vertex_group_names.first; dg && i < defbase_tot; i++, dg = dg->next) { if (groups[i]) { item_tmp.identifier = item_tmp.name = dg->name; item_tmp.value = i; @@ -401,7 +401,7 @@ static int weight_sample_group_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init(C, &vc, depsgraph); BLI_assert(type + 1 >= 0); - vc.obact->actdef = type + 1; + BKE_object_defgroup_active_index_set(vc.obact, type + 1); DEG_id_tag_update(&vc.obact->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact); @@ -458,7 +458,7 @@ static bool weight_paint_set(Object *ob, float paintweight) return false; } - vgroup_active = ob->actdef - 1; + vgroup_active = BKE_object_defgroup_active_index_get(ob) - 1; /* if mirror painting, find the other group */ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) { @@ -815,7 +815,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) data.sco_start = sco_start; data.sco_end = sco_end; data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end); - data.def_nr = ob->actdef - 1; + data.def_nr = BKE_object_defgroup_active_index_get(ob) - 1; data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; data.vert_cache = vert_cache; data.vert_visit = NULL; @@ -863,7 +863,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) } if (scene->toolsettings->auto_normalize) { - const int vgroup_num = BLI_listbase_count(&ob->defbase); + const int vgroup_num = BLI_listbase_count(&me->vertex_group_names); bool *vgroup_validmap = BKE_object_defgroup_validmap_get(ob, vgroup_num); if (vgroup_validmap != NULL) { MDeformVert *dvert = me->dvert; diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c index d6a118bbd59..19ffa0c952d 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c @@ -79,8 +79,10 @@ bool ED_wpaint_ensure_data(bContext *C, WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } + const ListBase *defbase = BKE_object_defgroup_list(ob); + /* this happens on a Bone select, when no vgroup existed yet */ - if (ob->actdef <= 0) { + if (me->vertex_group_active_index <= 0) { Object *modob; if ((modob = BKE_modifiers_is_deformed_by_armature(ob))) { Bone *actbone = ((bArmature *)modob->data)->act_bone; @@ -94,32 +96,33 @@ bool ED_wpaint_ensure_data(bContext *C, DEG_relations_tag_update(CTX_data_main(C)); } else { - int actdef = 1 + BLI_findindex(&ob->defbase, dg); + + int actdef = 1 + BLI_findindex(defbase, dg); BLI_assert(actdef >= 0); - ob->actdef = actdef; + me->vertex_group_active_index = actdef; } } } } } - if (BLI_listbase_is_empty(&ob->defbase)) { + if (BLI_listbase_is_empty(defbase)) { BKE_object_defgroup_add(ob); DEG_relations_tag_update(CTX_data_main(C)); } /* ensure we don't try paint onto an invalid group */ - if (ob->actdef <= 0) { + if (me->vertex_group_active_index <= 0) { BKE_report(reports, RPT_WARNING, "No active vertex group for painting, aborting"); return false; } if (vgroup_index) { - vgroup_index->active = ob->actdef - 1; + vgroup_index->active = me->vertex_group_active_index - 1; } if (flag & WPAINT_ENSURE_MIRROR) { if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) { - int mirror = ED_wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1); + int mirror = ED_wpaint_mirror_vgroup_ensure(ob, me->vertex_group_active_index - 1); if (vgroup_index) { vgroup_index->mirror = mirror; } @@ -133,7 +136,8 @@ bool ED_wpaint_ensure_data(bContext *C, /* mirror_vgroup is set to -1 when invalid */ int ED_wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) { - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, vgroup_active); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *defgroup = BLI_findlink(defbase, vgroup_active); if (defgroup) { int mirrdef; @@ -143,7 +147,7 @@ int ED_wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) mirrdef = BKE_object_defgroup_name_index(ob, name_flip); if (mirrdef == -1) { if (BKE_object_defgroup_new(ob, name_flip)) { - mirrdef = BLI_listbase_count(&ob->defbase) - 1; + mirrdef = BLI_listbase_count(defbase) - 1; } } diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index f248711633e..35015356f0b 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -42,6 +42,7 @@ #include "BKE_collection.h" #include "BKE_constraint.h" #include "BKE_context.h" +#include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_layer.h" @@ -450,7 +451,7 @@ static void tree_element_defgroup_activate(bContext *C, TreeElement *te, TreeSto /* id in tselem is object */ Object *ob = (Object *)tselem->id; BLI_assert(te->index + 1 >= 0); - ob->actdef = te->index + 1; + BKE_object_defgroup_active_index_set(ob, te->index + 1); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); @@ -830,7 +831,7 @@ static eOLDrawState tree_element_defgroup_state_get(const ViewLayer *view_layer, { const Object *ob = (const Object *)tselem->id; if (ob == OBACT(view_layer)) { - if (ob->actdef == te->index + 1) { + if (BKE_object_defgroup_active_index_get(ob) == te->index + 1) { return OL_DRAWSEL_NORMAL; } } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 334c901b6d3..c08707b7a6a 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.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 @@ -62,6 +62,7 @@ #include "BLT_translation.h" #include "BKE_armature.h" +#include "BKE_deform.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -552,17 +553,20 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } /* vertex groups */ - if (!BLI_listbase_is_empty(&ob->defbase)) { - TreeElement *tenla = outliner_add_element( - space_outliner, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0); - tenla->name = IFACE_("Vertex Groups"); + if (ELEM(ob->type, OB_MESH, OB_GPENCIL, OB_LATTICE)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_listbase_is_empty(defbase)) { + TreeElement *tenla = outliner_add_element( + space_outliner, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0); + tenla->name = IFACE_("Vertex Groups"); - int index; - LISTBASE_FOREACH_INDEX (bDeformGroup *, defgroup, &ob->defbase, index) { - TreeElement *ten = outliner_add_element( - space_outliner, &tenla->subtree, ob, tenla, TSE_DEFGROUP, index); - ten->name = defgroup->name; - ten->directdata = defgroup; + int index; + LISTBASE_FOREACH_INDEX (bDeformGroup *, defgroup, defbase, index) { + TreeElement *ten = outliner_add_element( + space_outliner, &tenla->subtree, ob, tenla, TSE_DEFGROUP, index); + ten->name = defgroup->name; + ten->directdata = defgroup; + } } } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 44b17b8c391..e38c70afd0f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -414,7 +414,6 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread Mesh *mesh = (Mesh *)object_orig->data; mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); } - mesh_component.copy_vertex_group_names_from_object(*object_orig); } else if (object_orig->type == OB_POINTCLOUD) { PointCloud *pointcloud = (PointCloud *)object_orig->data; @@ -432,7 +431,6 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread BKE_mesh_wrapper_ensure_mdata(mesh); MeshComponent &mesh_component = geometry_set.get_component_for_write(); mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); - mesh_component.copy_vertex_group_names_from_object(*object_eval); } else { if (BLI_listbase_count(&sspreadsheet->context_path) == 1) { diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 554b6c0f34f..3428a738dde 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1310,7 +1310,9 @@ static void view3d_panel_vgroup(const bContext *C, Panel *panel) vgroup_validmap = BKE_object_defgroup_subset_from_select_type( ob, subset_type, &vgroup_tot, &subset_count); - for (i = 0, dg = ob->defbase.first; dg; i++, dg = dg->next) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { bool locked = (dg->flag & DG_LOCK_WEIGHT) != 0; if (vgroup_validmap[i]) { MDeformWeight *dw = BKE_defvert_find_index(dv, i); @@ -1336,7 +1338,7 @@ static void view3d_panel_vgroup(const bContext *C, Panel *panel) but_ptr = UI_but_operator_ptr_get(but); RNA_int_set(but_ptr, "weight_group", i); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); - if (ob->actdef != i + 1) { + if (BKE_object_defgroup_active_index_get(ob) != i + 1) { UI_but_flag_enable(but, UI_BUT_INACTIVE); } xco += x; -- cgit v1.2.3 From ae379714e4f1eca74f5f77532a6e959f29445236 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 13 Jul 2021 18:57:46 -0300 Subject: Transform: Identify more safely when the mesh is deform only Depending on the modifiers, geometry can be destructive which is not safe. --- .../editors/transform/transform_convert_mesh.c | 27 ++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index d740f7e3adb..383f9870714 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -2067,6 +2067,27 @@ static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) } } +static bool tc_mesh_is_deform_only_update(TransInfo *t, TransDataContainer *tc) +{ + if (tc->custom.type.data && + ((struct TransCustomDataMesh *)tc->custom.type.data)->cd_layer_correct) { + return false; + } + + Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(t->depsgraph, (ID *)tc->obedit->data); + Mesh *mesh_eval_cage = me_eval->edit_mesh->mesh_eval_cage; + Mesh *mesh_eval_final = me_eval->edit_mesh->mesh_eval_final; + if (mesh_eval_cage && !mesh_eval_cage->runtime.is_original) { + return false; + } + if (mesh_eval_final && mesh_eval_final != mesh_eval_cage && + !mesh_eval_final->runtime.is_original) { + return false; + } + + return me_eval->runtime.deformed_only; +} + void recalcData_mesh(TransInfo *t) { bool is_canceling = t->state == TRANS_CANCEL; @@ -2094,12 +2115,10 @@ void recalcData_mesh(TransInfo *t) tc_mesh_partial_types_calc(t, &partial_state); FOREACH_TRANS_DATA_CONTAINER (t, tc) { - const bool use_cd_layer_correct = - tc->custom.type.data && - ((struct TransCustomDataMesh *)tc->custom.type.data)->cd_layer_correct; + const bool is_deform_only = tc_mesh_is_deform_only_update(t, tc); DEG_id_tag_update(tc->obedit->data, - use_cd_layer_correct ? ID_RECALC_GEOMETRY : ID_RECALC_GEOMETRY_DEFORM); + is_deform_only ? ID_RECALC_GEOMETRY_DEFORM : ID_RECALC_GEOMETRY); tc_mesh_partial_update(t, tc, &partial_state); } -- cgit v1.2.3 From 3e125d12af625920d677ede0d24214b33f81b6ba Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 14 Jul 2021 11:09:25 +0200 Subject: Fix T89849: Time offset not working with Bake Object transform to Grease pencil The bake animation was not using the remap of time done by grease pencil time modifier. --- source/blender/editors/gpencil/gpencil_bake_animation.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c index 1a5e2950e09..2d299230124 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.c +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -38,6 +38,7 @@ #include "BKE_duplilist.h" #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" +#include "BKE_gpencil_modifier.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_material.h" @@ -303,8 +304,12 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op float matrix[4][4]; BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); + /* Apply time modifier. */ + int remap_cfra = BKE_gpencil_time_modifier_cfra( + depsgraph, scene, elem->ob, gpl_src, CFRA, false); /* Duplicate frame. */ - bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf_src = BKE_gpencil_layer_frame_get( + gpl_src, remap_cfra, GP_GETFRAME_USE_PREV); if (gpf_src == NULL) { continue; } -- cgit v1.2.3 From 271f34f77e056611e5e227b60823dd94f76340df Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 14 Jul 2021 11:16:43 +0200 Subject: Geometry Nodes: initial socket inspection Socket inspection helps with debugging a geometry node group. Now, when hovering over a socket, a tooltip will appear that provides information about the data in the socket. Note, socket inspection only works for sockets that have been computed already. Nodes that are not connected to an output are not computed. Future improvements can include ui changes to make the tooltip look more like in the original design (T85251). Furthermore, additional information could be shown if necessary. Differential Revision: https://developer.blender.org/D11842 --- source/blender/editors/space_node/node_draw.cc | 193 +++++++++++++++++++++++++ 1 file changed, 193 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index d4f178603b8..7eca5c0c933 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -45,6 +45,7 @@ #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" @@ -826,6 +827,149 @@ void node_socket_color_get( } } +struct SocketTooltipData { + bNodeTree *ntree; + bNode *node; + bNodeSocket *socket; +}; + +static void create_inspection_string_for_generic_value(const geo_log::GenericValueLog &value_log, + std::stringstream &ss) +{ + auto id_to_inspection_string = [&](ID *id) { + ss << (id ? id->name + 2 : TIP_("None")) << " (" << BKE_idtype_idcode_to_name(GS(id->name)) + << ")"; + }; + + const GPointer value = value_log.value(); + if (value.is_type()) { + ss << *value.get() << TIP_(" (Integer)"); + } + else if (value.is_type()) { + ss << *value.get() << TIP_(" (Float)"); + } + else if (value.is_type()) { + ss << *value.get() << TIP_(" (Vector)"); + } + else if (value.is_type()) { + ss << (*value.get() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (value.is_type()) { + ss << *value.get() << TIP_(" (String)"); + } + else if (value.is_type()) { + id_to_inspection_string((ID *)*value.get()); + } + else if (value.is_type()) { + id_to_inspection_string((ID *)*value.get()); + } + else if (value.is_type()) { + id_to_inspection_string((ID *)*value.get()); + } + else if (value.is_type()) { + id_to_inspection_string((ID *)*value.get()); + } +} + +static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log, + std::stringstream &ss) +{ + Span component_types = value_log.component_types(); + if (component_types.is_empty()) { + ss << TIP_("Empty Geometry"); + return; + } + + auto to_string = [](int value) { + char str[16]; + BLI_str_format_int_grouped(str, value); + return std::string(str); + }; + + ss << TIP_("Geometry:\n"); + for (GeometryComponentType type : component_types) { + const char *line_end = (type == component_types.last()) ? "" : ".\n"; + switch (type) { + case GEO_COMPONENT_TYPE_MESH: { + const geo_log::GeometryValueLog::MeshInfo &mesh_info = *value_log.mesh_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"), + to_string(mesh_info.tot_verts).c_str(), + to_string(mesh_info.tot_edges).c_str(), + to_string(mesh_info.tot_faces).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const geo_log::GeometryValueLog::PointCloudInfo &pointcloud_info = + *value_log.pointcloud_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Point Cloud: %s points"), + to_string(pointcloud_info.tot_points).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + const geo_log::GeometryValueLog::CurveInfo &curve_info = *value_log.curve_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Curve: %s splines"), + to_string(curve_info.tot_splines).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + const geo_log::GeometryValueLog::InstancesInfo &instances_info = *value_log.instances_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Instances: %s"), + to_string(instances_info.tot_instances).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + ss << TIP_("\u2022 Volume") << line_end; + break; + } + } + } +} + +static std::optional create_socket_inspection_string(bContext *C, + bNodeTree &UNUSED(ntree), + bNode &node, + bNodeSocket &socket) +{ + SpaceNode *snode = CTX_wm_space_node(C); + const geo_log::SocketLog *socket_log = geo_log::ModifierLog::find_socket_by_node_editor_context( + *snode, node, socket); + if (socket_log == nullptr) { + return {}; + } + const geo_log::ValueLog *value_log = socket_log->value(); + if (value_log == nullptr) { + return {}; + } + + std::stringstream ss; + if (const geo_log::GenericValueLog *generic_value_log = + dynamic_cast(value_log)) { + create_inspection_string_for_generic_value(*generic_value_log, ss); + } + else if (const geo_log::GeometryValueLog *geo_value_log = + dynamic_cast(value_log)) { + create_inspection_string_for_geometry(*geo_value_log, ss); + } + + return ss.str(); +} + static void node_socket_draw_nested(const bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, @@ -855,6 +999,55 @@ static void node_socket_draw_nested(const bContext *C, shape_id, size_id, outline_col_id); + + if (ntree->type != NTREE_GEOMETRY) { + /* Only geometry nodes has socket value tooltips currently. */ + return; + } + + bNode *node = (bNode *)node_ptr->data; + uiBlock *block = node->block; + + /* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible + * button on top of them for the tooltip. */ + const eUIEmbossType old_emboss = UI_block_emboss_get(block); + UI_block_emboss_set(block, UI_EMBOSS_NONE); + uiBut *but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_NONE, + sock->locx - size / 2, + sock->locy - size / 2, + size, + size, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + + SocketTooltipData *data = (SocketTooltipData *)MEM_mallocN(sizeof(SocketTooltipData), __func__); + data->ntree = ntree; + data->node = (bNode *)node_ptr->data; + data->socket = sock; + + UI_but_func_tooltip_set( + but, + [](bContext *C, void *argN, const char *UNUSED(tip)) { + SocketTooltipData *data = (SocketTooltipData *)argN; + std::optional str = create_socket_inspection_string( + C, *data->ntree, *data->node, *data->socket); + if (str.has_value()) { + return BLI_strdup(str->c_str()); + } + return BLI_strdup(TIP_("The socket value has not been computed yet")); + }, + data, + MEM_freeN); + /* Disable the button so that clicks on it are ignored the the link operator still works. */ + UI_but_flag_enable(but, UI_BUT_DISABLED); + UI_block_emboss_set(block, old_emboss); } /** -- cgit v1.2.3 From 2761679180c37ffddfeef213dd5b5b0d6290395c Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 14 Jul 2021 13:22:40 +0200 Subject: Fix T89826: VSE snapping with constrained axis Constraining to X axis caused snapping to not work at all. Constraining to Y axis caused snapping indicator to be drawn, when snapping doesn't occur. Reviewed By: mano-wii Differential Revision: https://developer.blender.org/D11898 --- source/blender/editors/transform/transform_mode_edge_seq_slide.c | 2 +- source/blender/editors/transform/transform_snap_sequencer.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c index fe853440c96..a8f7fc43b5e 100644 --- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c @@ -129,7 +129,7 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2])) transform_convert_sequencer_channel_clamp(t, values_final); if (t->con.mode & CON_APPLY) { - t->con.applyVec(t, NULL, NULL, t->values, values_final); + t->con.applyVec(t, NULL, NULL, values_final, values_final); } } diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index d9395e5e960..d0b730383d5 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -236,6 +236,11 @@ void transform_snap_sequencer_data_free(TransSeqSnapData *data) bool transform_snap_sequencer_calc(TransInfo *t) { + /* Prevent snapping when constrained to Y axis. */ + if (t->con.mode & CON_APPLY && t->con.mode & CON_AXIS1) { + return false; + } + const TransSeqSnapData *snap_data = t->tsnap.seq_context; int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0; -- cgit v1.2.3 From 7cd91a06ebc15729867c9bb2b56fb36f21ecb5e1 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 14 Jul 2021 13:45:19 +0200 Subject: Fix T88908: Incorrect path handling in adding strips When image strip is added from python using `image_strip_add` operator and directory path is not terminated with slash, last part of directory was ignored. Use `BLI_join_dirfile` instead of simple string concatenation. --- source/blender/editors/space_sequencer/sequencer_add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index ac31e0e7c37..1239286d4da 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -260,7 +260,7 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm RNA_PROP_BEGIN (op->ptr, itemptr, prop) { char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); BLI_strncpy(load_data->name, filename, sizeof(load_data->name)); - BLI_snprintf(load_data->path, sizeof(load_data->path), "%s%s", directory, filename); + BLI_join_dirfile(load_data->path, sizeof(load_data->path), directory, filename); MEM_freeN(filename); break; } -- cgit v1.2.3 From 2acebcae24a6cb7cc5dcef6ddc437396e129da46 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 14 Jul 2021 10:11:41 -0400 Subject: Cleanup: Avoid duplication in line art stroke generation The BKE_gpencil_stroke_add_points API function worked well for creating the primitives in the add object menu, but it expected a specific data format that doesn't make sense in a dynamic context. As evidence of that we can see the way source data was duplicated in the line art file just to use this API function. This commit solves that problem in two ways: - Clean up the line art function (this should make it faster too). - Move/rename the function so its intended use is more clear. Differential Revision: https://developer.blender.org/D11909 --- .../blender/editors/gpencil/gpencil_add_monkey.c | 82 ++++++++++++++-------- .../blender/editors/gpencil/gpencil_add_stroke.c | 2 +- source/blender/editors/include/ED_gpencil.h | 7 ++ 3 files changed, 62 insertions(+), 29 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index d8734c4ae6b..8d60ef3ed12 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -39,6 +39,32 @@ #include "ED_gpencil.h" +/** + * Populate stroke with point data from data buffers. + * \param gps: Grease pencil stroke + * \param array: Flat array of point data values. Each entry has #GP_PRIM_DATABUF_SIZE values. + * \param totpoints: Total of points + * \param mat: 4x4 transform matrix to transform points into the right coordinate space. + */ +void ED_gpencil_stroke_init_data(bGPDstroke *gps, + const float *array, + const int totpoints, + const float mat[4][4]) +{ + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + const int x = GP_PRIM_DATABUF_SIZE * i; + + pt->x = array[x]; + pt->y = array[x + 1]; + pt->z = array[x + 2]; + mul_m4_v3(mat, &pt->x); + + pt->pressure = array[x + 3]; + pt->strength = array[x + 4]; + } +} + /* Definition of the most important info from a color */ typedef struct ColorTemplate { const char *name; @@ -847,115 +873,115 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) /* generate strokes */ gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false); - BKE_gpencil_stroke_add_points(gps, data0, 270, mat); + ED_gpencil_stroke_init_data(gps, data0, 270, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data1, 33, mat); + ED_gpencil_stroke_init_data(gps, data1, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 60, false); - BKE_gpencil_stroke_add_points(gps, data2, 18, mat); + ED_gpencil_stroke_init_data(gps, data2, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false); - BKE_gpencil_stroke_add_points(gps, data3, 64, mat); + ED_gpencil_stroke_init_data(gps, data3, 64, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data4, 33, mat); + ED_gpencil_stroke_init_data(gps, data4, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false); - BKE_gpencil_stroke_add_points(gps, data5, 64, mat); + ED_gpencil_stroke_init_data(gps, data5, 64, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data6, 33, mat); + ED_gpencil_stroke_init_data(gps, data6, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data7, 18, mat); + ED_gpencil_stroke_init_data(gps, data7, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false); - BKE_gpencil_stroke_add_points(gps, data8, 49, mat); + ED_gpencil_stroke_init_data(gps, data8, 49, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data9, 33, mat); + ED_gpencil_stroke_init_data(gps, data9, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false); - BKE_gpencil_stroke_add_points(gps, data10, 49, mat); + ED_gpencil_stroke_init_data(gps, data10, 49, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data11, 18, mat); + ED_gpencil_stroke_init_data(gps, data11, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data12, 18, mat); + ED_gpencil_stroke_init_data(gps, data12, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data13, 33, mat); + ED_gpencil_stroke_init_data(gps, data13, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data14, 33, mat); + ED_gpencil_stroke_init_data(gps, data14, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 65, 60, false); - BKE_gpencil_stroke_add_points(gps, data15, 65, mat); + ED_gpencil_stroke_init_data(gps, data15, 65, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 60, false); - BKE_gpencil_stroke_add_points(gps, data16, 34, mat); + ED_gpencil_stroke_init_data(gps, data16, 34, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data17, 33, mat); + ED_gpencil_stroke_init_data(gps, data17, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 40, false); - BKE_gpencil_stroke_add_points(gps, data18, 33, mat); + ED_gpencil_stroke_init_data(gps, data18, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 40, false); - BKE_gpencil_stroke_add_points(gps, data19, 34, mat); + ED_gpencil_stroke_init_data(gps, data19, 34, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data20, 33, mat); + ED_gpencil_stroke_init_data(gps, data20, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 64, 60, false); - BKE_gpencil_stroke_add_points(gps, data21, 64, mat); + ED_gpencil_stroke_init_data(gps, data21, 64, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false); - BKE_gpencil_stroke_add_points(gps, data22, 26, mat); + ED_gpencil_stroke_init_data(gps, data22, 26, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false); - BKE_gpencil_stroke_add_points(gps, data23, 26, mat); + ED_gpencil_stroke_init_data(gps, data23, 26, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data24, 33, mat); + ED_gpencil_stroke_init_data(gps, data24, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data25, 18, mat); + ED_gpencil_stroke_init_data(gps, data25, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data26, 18, mat); + ED_gpencil_stroke_init_data(gps, data26, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data27, 33, mat); + ED_gpencil_stroke_init_data(gps, data27, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); /* update depsgraph */ diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index e95496b51ee..73c4e64dd9a 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -235,7 +235,7 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) /* generate stroke */ gps = BKE_gpencil_stroke_add(frame_lines, color_black, 175, 75, false); - BKE_gpencil_stroke_add_points(gps, data0, 175, mat); + ED_gpencil_stroke_init_data(gps, data0, 175, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); /* update depsgraph */ diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 59b5a1abaa6..8a8d91a570c 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -251,6 +251,13 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y); /* ----------- Add Primitive Utilities -------------- */ +/* Number of values defining each point in the built-in data buffers for primitives. */ +#define GP_PRIM_DATABUF_SIZE 5 +void ED_gpencil_stroke_init_data(struct bGPDstroke *gps, + const float *array, + const int totpoints, + const float mat[4][4]); + void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]); -- cgit v1.2.3 From 8e8a6b80cf2749d176d14eaa3bbfd0eccc9ec75e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 15 Jul 2021 18:23:28 +1000 Subject: Cleanup: replace BLI_assert(!"text") with BLI_assert_msg(0, "text") This shows the text as part of the assertion message. --- source/blender/editors/armature/pose_slide.c | 2 +- source/blender/editors/curve/editcurve_add.c | 2 +- source/blender/editors/gpencil/gpencil_convert.c | 2 +- source/blender/editors/gpencil/gpencil_select.c | 2 +- source/blender/editors/interface/interface_handlers.c | 2 +- source/blender/editors/interface/interface_icons.c | 2 +- source/blender/editors/interface/interface_widgets.c | 4 ++-- source/blender/editors/interface/view2d_edge_pan.c | 2 +- source/blender/editors/mask/mask_ops.c | 2 +- source/blender/editors/object/object_relations.c | 2 +- source/blender/editors/render/render_preview.c | 6 +++--- source/blender/editors/render/render_shading.c | 2 +- source/blender/editors/screen/area.c | 2 +- source/blender/editors/screen/area_query.c | 2 +- source/blender/editors/screen/glutil.c | 4 ++-- source/blender/editors/sculpt_paint/sculpt.c | 2 +- source/blender/editors/sculpt_paint/sculpt_automasking.c | 6 +++--- source/blender/editors/sculpt_paint/sculpt_smooth.c | 2 +- source/blender/editors/sculpt_paint/sculpt_undo.c | 6 +++--- source/blender/editors/space_clip/clip_editor.c | 2 +- source/blender/editors/space_file/filesel.c | 4 ++-- source/blender/editors/space_outliner/outliner_tree.c | 7 ++++--- source/blender/editors/space_outliner/tree/tree_element_id.cc | 2 +- source/blender/editors/space_sequencer/sequencer_draw.c | 2 +- source/blender/editors/space_view3d/view3d_select.c | 4 ++-- source/blender/editors/transform/transform_generics.c | 2 +- source/blender/editors/transform/transform_mode_edge_slide.c | 2 +- source/blender/editors/undo/ed_undo.c | 4 ++-- source/blender/editors/util/select_utils.c | 4 ++-- 29 files changed, 44 insertions(+), 43 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index f01afcfc578..1a1685e4a01 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -833,7 +833,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) float prevFrameF, nextFrameF; if (!pose_frame_range_from_object_get(pso, pfl->ob, &prevFrameF, &nextFrameF)) { - BLI_assert(!"Invalid pfl data"); + BLI_assert_msg(0, "Invalid pfl data"); return; } diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 065763764c1..2be55accd3a 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -485,7 +485,7 @@ Nurb *ED_curve_add_nurbs_primitive( break; default: /* should never happen */ - BLI_assert(!"invalid nurbs type"); + BLI_assert_msg(0, "invalid nurbs type"); return NULL; } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 2cb1e09d9a6..ee3536c2f3f 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1409,7 +1409,7 @@ static void gpencil_layer_to_curve(bContext *C, gtd); break; default: - BLI_assert(!"invalid mode"); + BLI_assert_msg(0, "invalid mode"); break; } prev_gps = gps; diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index c33b43247fd..69734fa1ba8 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -689,7 +689,7 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) break; default: - BLI_assert(!"unhandled select grouped gpencil mode"); + BLI_assert_msg(0, "unhandled select grouped gpencil mode"); break; } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 0e45970a165..3136ca64e0f 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -6513,7 +6513,7 @@ static void ui_ndofedit_but_HSVCUBE(uiButHSVCube *hsv_but, CLAMP(hsv[2], hsv_but->but.softmin, hsv_but->but.softmax); break; default: - BLI_assert(!"invalid hsv type"); + BLI_assert_msg(0, "invalid hsv type"); break; } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index aa957afbf8f..2a505abe257 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1500,7 +1500,7 @@ static void icon_draw_rect(float x, /* sanity check */ if (w <= 0 || h <= 0 || w > 2000 || h > 2000) { printf("%s: icons are %i x %i pixels?\n", __func__, w, h); - BLI_assert(!"invalid icon size"); + BLI_assert_msg(0, "invalid icon size"); return; } /* modulate color */ diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 72847fb2476..8abff4ad0d8 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -3115,7 +3115,7 @@ void ui_draw_gradient(const rcti *rect, copy_v3_v3(col1[3], col1[2]); break; default: - BLI_assert(!"invalid 'type' argument"); + BLI_assert_msg(0, "invalid 'type' argument"); hsv_to_rgb(1.0, 1.0, 1.0, &col1[2][0], &col1[2][1], &col1[2][2]); copy_v3_v3(col1[0], col1[2]); copy_v3_v3(col1[1], col1[2]); @@ -5374,7 +5374,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, } } else { - BLI_assert(!"Unknwon menu item separator type"); + BLI_assert_msg(0, "Unknwon menu item separator type"); } if (fstyle->kerning == 1) { diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c index ca32a754f1d..1d300c7b275 100644 --- a/source/blender/editors/interface/view2d_edge_pan.c +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -160,7 +160,7 @@ static float edge_pan_speed(View2DEdgePanData *vpd, distance = min - event_loc; } else { - BLI_assert(!"Calculating speed outside of pan zones"); + BLI_assert_msg(0, "Calculating speed outside of pan zones"); return 0.0f; } float distance_factor = distance / (vpd->speed_ramp * U.widget_unit); diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 3476f1ca735..6fa7457ce14 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -390,7 +390,7 @@ static void select_sliding_point(Mask *mask, point->bezt.f3 |= SELECT; break; default: - BLI_assert(!"Unexpected situation in select_sliding_point()"); + BLI_assert_msg(0, "Unexpected situation in select_sliding_point()"); } mask_layer->act_spline = spline; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index d7177eabcba..c61965b3e23 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2022,7 +2022,7 @@ static void single_obdata_users( break; default: printf("ERROR %s: can't copy %s\n", __func__, id->name); - BLI_assert(!"This should never happen."); + BLI_assert_msg(0, "This should never happen."); /* We need to end the FOREACH_OBJECT_FLAG_BEGIN iterator to prevent memory leak. */ BKE_scene_objects_iterator_end(&iter_macro); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index e8f69aded5b..7f7ee45a803 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -253,7 +253,7 @@ static const char *preview_collection_name(const char pr_type) case MA_ATMOS: return "Atmosphere"; default: - BLI_assert(!"Unknown preview type"); + BLI_assert_msg(0, "Unknown preview type"); return ""; } } @@ -335,7 +335,7 @@ static ID *duplicate_ids(ID *id, const bool allow_failure) return NULL; default: if (!allow_failure) { - BLI_assert(!"ID type preview not supported."); + BLI_assert_msg(0, "ID type preview not supported."); } return NULL; } @@ -1469,7 +1469,7 @@ static int icon_previewimg_size_index_get(const IconPreviewSize *icon_size, } } - BLI_assert(!"The searched icon size does not match any in the preview image"); + BLI_assert_msg(0, "The searched icon size does not match any in the preview image"); return -1; } diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 9595a66173b..d2b1ebdad78 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -2418,7 +2418,7 @@ static void paste_mtex_copybuf(ID *id) mtex = &(((FreestyleLineStyle *)id)->mtex[(int)((FreestyleLineStyle *)id)->texact]); break; default: - BLI_assert(!"invalid id type"); + BLI_assert_msg(0, "invalid id type"); return; } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 39de63c22a9..f4109981aae 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -3672,7 +3672,7 @@ static void region_visible_rect_calc(ARegion *region, rcti *rect) /* Skip floating. */ } else { - BLI_assert(!"Region overlap with unknown alignment"); + BLI_assert_msg(0, "Region overlap with unknown alignment"); } } } diff --git a/source/blender/editors/screen/area_query.c b/source/blender/editors/screen/area_query.c index d569e56e11b..fd4f3964398 100644 --- a/source/blender/editors/screen/area_query.c +++ b/source/blender/editors/screen/area_query.c @@ -88,7 +88,7 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *region, rcti *r_re r_region_gutter->xmin = r_region_gutter->xmax - category_tabs_width; } else { - BLI_assert(!"Unsupported alignment"); + BLI_assert_msg(0, "Unsupported alignment"); } return true; } diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index e366760a55d..f651fd4fb61 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -126,7 +126,7 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, components = 1; } else { - BLI_assert(!"Incompatible format passed to immDrawPixels"); + BLI_assert_msg(0, "Incompatible format passed to immDrawPixels"); return; } @@ -426,7 +426,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, format = GPU_RGBA16F; } else { - BLI_assert(!"Incompatible number of channels for GLSL display"); + BLI_assert_msg(0, "Incompatible number of channels for GLSL display"); } if (format != 0) { diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 54b6f3d3590..83388c1aef2 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2394,7 +2394,7 @@ static float brush_strength(const Sculpt *sd, case BRUSH_MASK_SMOOTH: return alpha * pressure * feather; } - BLI_assert(!"Not supposed to happen"); + BLI_assert_msg(0, "Not supposed to happen"); return 0.0f; case SCULPT_TOOL_CREASE: diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index 5f5fb51d75f..35f48400fe2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c @@ -209,7 +209,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au Brush *brush = BKE_paint_brush(&sd->paint); if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { - BLI_assert(!"Topology masking: pmap missing"); + BLI_assert_msg(0, "Topology masking: pmap missing"); return NULL; } @@ -248,7 +248,7 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { - BLI_assert(!"Face Sets automasking: pmap missing"); + BLI_assert_msg(0, "Face Sets automasking: pmap missing"); return NULL; } @@ -273,7 +273,7 @@ float *SCULPT_boundary_automasking_init(Object *ob, SculptSession *ss = ob->sculpt; if (!ss->pmap) { - BLI_assert(!"Boundary Edges masking: pmap missing"); + BLI_assert_msg(0, "Boundary Edges masking: pmap missing"); return NULL; } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 61984610a5a..eabbfe43e03 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -367,7 +367,7 @@ void SCULPT_smooth(Sculpt *sd, last = max_iterations * (bstrength - count * fract); if (type == PBVH_FACES && !ss->pmap) { - BLI_assert(!"sculpt smooth: pmap missing"); + BLI_assert_msg(0, "sculpt smooth: pmap missing"); return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index f21c900941b..501a1e53276 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -766,7 +766,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - BLI_assert(!"Dynamic topology should've already been handled"); + BLI_assert_msg(0, "Dynamic topology should've already been handled"); break; } } @@ -1065,7 +1065,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - BLI_assert(!"Dynamic topology should've already been handled"); + BLI_assert_msg(0, "Dynamic topology should've already been handled"); case SCULPT_UNDO_GEOMETRY: case SCULPT_UNDO_FACE_SETS: break; @@ -1355,7 +1355,7 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - BLI_assert(!"Dynamic topology should've already been handled"); + BLI_assert_msg(0, "Dynamic topology should've already been handled"); case SCULPT_UNDO_GEOMETRY: case SCULPT_UNDO_FACE_SETS: break; diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 2da13646a8b..67b4fd61d38 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -1025,7 +1025,7 @@ static void prefetch_startjob(void *pjv, short *stop, short *do_update, float *p progress); } else { - BLI_assert(!"Unknown movie clip source when prefetching frames"); + BLI_assert_msg(0, "Unknown movie clip source when prefetching frames"); } } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index b7accbf71e5..7ab93df81d2 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -378,7 +378,7 @@ FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile) return &sfile->asset_params->base_params; } - BLI_assert(!"Invalid browse mode set in file space."); + BLI_assert_msg(0, "Invalid browse mode set in file space."); return NULL; } @@ -399,7 +399,7 @@ FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) return (FileSelectParams *)sfile->asset_params; } - BLI_assert(!"Invalid browse mode set in file space."); + BLI_assert_msg(0, "Invalid browse mode set in file space."); return NULL; } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index c08707b7a6a..c5ec656080a 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -592,7 +592,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, switch (GS(id->name)) { case ID_LI: case ID_SCE: - BLI_assert(!"ID type expected to be expanded through new tree-element design"); + BLI_assert_msg(0, "ID type expected to be expanded through new tree-element design"); break; case ID_OB: { outliner_add_object_contents(space_outliner, te, tselem, (Object *)id); @@ -905,12 +905,13 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, } else if (type == TSE_SOME_ID) { if (!te->type) { - BLI_assert(!"Expected this ID type to be ported to new Outliner tree-element design"); + BLI_assert_msg(0, "Expected this ID type to be ported to new Outliner tree-element design"); } } else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) { if (!te->type) { - BLI_assert(!"Expected override types to be ported to new Outliner tree-element design"); + BLI_assert_msg(0, + "Expected override types to be ported to new Outliner tree-element design"); } } else { diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index ce99b954204..7ff5a3285f1 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -85,7 +85,7 @@ TreeElementID *TreeElementID::createFromID(TreeElement &legacy_te, ID &id) return new TreeElementID(legacy_te, id); /* Deprecated */ case ID_IP: - BLI_assert(!"Outliner trying to build tree-element for deprecated ID type"); + BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type"); return nullptr; } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 8371a634a78..cdbe5bc63ce 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1556,7 +1556,7 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C, *r_format = GPU_RGB16F; } else { - BLI_assert(!"Incompatible number of channels for float buffer in sequencer"); + BLI_assert_msg(0, "Incompatible number of channels for float buffer in sequencer"); *r_format = GPU_RGBA16F; display_buffer = NULL; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 210dd7311a4..ecf43c734e2 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1378,7 +1378,7 @@ static bool view3d_lasso_select(bContext *C, changed = do_lasso_select_meta(vc, mcoords, mcoords_len, sel_op); break; default: - BLI_assert(!"lasso select on incorrect object type"); + BLI_assert_msg(0, "lasso select on incorrect object type"); break; } @@ -3604,7 +3604,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } break; default: - BLI_assert(!"box select on incorrect object type"); + BLI_assert_msg(0, "box select on incorrect object type"); break; } changed_multi |= changed; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 575d9c0828e..aaac8e21cb9 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -910,7 +910,7 @@ void calculateCenterCursor2D(TransInfo *t, float r_center[2]) BKE_mask_coord_from_movieclip(space_clip->clip, &space_clip->user, co, cursor); } else { - BLI_assert(!"Shall not happen"); + BLI_assert_msg(0, "Shall not happen"); } r_center[0] = co[0] * t->aspect[0]; diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index bdd074666a2..066a2853dc7 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -107,7 +107,7 @@ static TransDataContainer *edge_slide_container_first_ok(TransInfo *t) return tc; } } - BLI_assert(!"Should never happen, at least one EdgeSlideData should be valid"); + BLI_assert_msg(0, "Should never happen, at least one EdgeSlideData should be valid"); return NULL; } diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 00efdb87fea..23161028e03 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -321,7 +321,7 @@ static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList * /* FIXME: See comments in `ed_undo_step_direction`. */ if (ED_gpencil_session_active()) { - BLI_assert(!"Not implemented currently."); + BLI_assert_msg(0, "Not implemented currently."); } wmWindowManager *wm = CTX_wm_manager(C); @@ -369,7 +369,7 @@ static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList * /* FIXME: See comments in `ed_undo_step_direction`. */ if (ED_gpencil_session_active()) { - BLI_assert(!"Not implemented currently."); + BLI_assert_msg(0, "Not implemented currently."); } wmWindowManager *wm = CTX_wm_manager(C); diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index 4e8cf1e92e6..5681edd2f5c 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -41,7 +41,7 @@ int ED_select_op_action(const eSelectOp sel_op, const bool is_select, const bool case SEL_OP_XOR: return (is_select && is_inside) ? 0 : ((!is_select && is_inside) ? 1 : -1); } - BLI_assert(!"invalid sel_op"); + BLI_assert_msg(0, "invalid sel_op"); return -1; } /** @@ -67,7 +67,7 @@ int ED_select_op_action_deselected(const eSelectOp sel_op, case SEL_OP_XOR: return (is_select && is_inside) ? 0 : ((!is_select && is_inside) ? 1 : -1); } - BLI_assert(!"invalid sel_op"); + BLI_assert_msg(0, "invalid sel_op"); return -1; } -- cgit v1.2.3 From c27ef1e9e8e663e02173e518c1e669e9845b3d1f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 15 Jul 2021 11:00:23 +0200 Subject: Geometry Nodes: dim links whose start and end sockets are not visible This makes node trees with long links that cross other nodes easier to work with. Dimmed links will be ignored by various modal operators like cut and reroute insertion. Differential Revision: https://developer.blender.org/D11813 --- source/blender/editors/space_node/drawnode.cc | 26 +++++++++++++++++++--- source/blender/editors/space_node/node_add.cc | 2 +- source/blender/editors/space_node/node_edit.cc | 24 ++++++++++++++++++++ source/blender/editors/space_node/node_intern.h | 2 ++ .../editors/space_node/node_relationships.cc | 17 +++++++++----- 5 files changed, 61 insertions(+), 10 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 243652da608..878a4e7d2c6 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3911,8 +3911,10 @@ static struct { GPUVertBuf *inst_vbo; uint p0_id, p1_id, p2_id, p3_id; uint colid_id, muted_id; + uint dim_factor_id; GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; GPUVertBufRaw colid_step, muted_step; + GPUVertBufRaw dim_factor_step; uint count; bool enabled; } g_batch_link; @@ -3927,6 +3929,8 @@ static void nodelink_batch_reset() g_batch_link.inst_vbo, g_batch_link.colid_id, &g_batch_link.colid_step); GPU_vertbuf_attr_get_raw_data( g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.dim_factor_id, &g_batch_link.dim_factor_step); g_batch_link.count = 0; } @@ -4044,6 +4048,8 @@ static void nodelink_batch_init() &format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT); g_batch_link.muted_id = GPU_vertformat_attr_add( &format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT); + g_batch_link.dim_factor_id = GPU_vertformat_attr_add( + &format_inst, "dim_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); /* Alloc max count but only draw the range we need. */ GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); @@ -4119,7 +4125,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode, int th_col2, int th_col3, bool drawarrow, - bool drawmuted) + bool drawmuted, + float dim_factor) { /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); @@ -4138,6 +4145,7 @@ static void nodelink_batch_add_link(const SpaceNode *snode, colid[3] = drawarrow; char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step); muted[0] = drawmuted; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor; if (g_batch_link.count == NODELINK_GROUP_SIZE) { nodelink_batch_draw(snode); @@ -4152,6 +4160,8 @@ void node_draw_link_bezier(const View2D *v2d, int th_col2, int th_col3) { + const float dim_factor = node_link_dim_factor(v2d, link); + float vec[4][2]; const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT; if (node_link_bezier_handles(v2d, snode, link, vec)) { @@ -4164,8 +4174,17 @@ void node_draw_link_bezier(const View2D *v2d, if (g_batch_link.enabled && !highlighted) { /* Add link to batch. */ - nodelink_batch_add_link( - snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow, drawmuted); + nodelink_batch_add_link(snode, + vec[0], + vec[1], + vec[2], + vec[3], + th_col1, + th_col2, + th_col3, + drawarrow, + drawmuted, + dim_factor); } else { /* Draw single link. */ @@ -4190,6 +4209,7 @@ void node_draw_link_bezier(const View2D *v2d, GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE); GPU_batch_uniform_1i(batch, "doArrow", drawarrow); GPU_batch_uniform_1i(batch, "doMuted", drawmuted); + GPU_batch_uniform_1f(batch, "dim_factor", dim_factor); GPU_batch_draw(batch); } } diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index c167744de01..9264c9d3572 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -260,7 +260,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) BLI_listbase_clear(&input_links); for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } if (add_reroute_intersect_check(link, mcoords, i, insert_point)) { diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 5dd935bdd76..b1afd6549bc 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -21,6 +21,8 @@ * \ingroup spnode */ +#include + #include "MEM_guardedalloc.h" #include "DNA_light_types.h" @@ -1226,6 +1228,28 @@ int node_find_indicated_socket( return 0; } +/* ****************** Link Dimming *********************** */ + +float node_link_dim_factor(const View2D *v2d, const bNodeLink *link) +{ + const float min_endpoint_distance = std::min( + std::max(BLI_rctf_length_x(&v2d->cur, link->fromsock->locx), + BLI_rctf_length_y(&v2d->cur, link->fromsock->locy)), + std::max(BLI_rctf_length_x(&v2d->cur, link->tosock->locx), + BLI_rctf_length_y(&v2d->cur, link->tosock->locy))); + + if (min_endpoint_distance == 0.0f) { + return 1.0f; + } + const float viewport_width = BLI_rctf_size_x(&v2d->cur); + return std::clamp(1.0f - min_endpoint_distance / viewport_width * 10.0f, 0.05f, 1.0f); +} + +bool node_link_is_hidden_or_dimmed(const View2D *v2d, const bNodeLink *link) +{ + return nodeLinkIsHidden(link) || node_link_dim_factor(v2d, link) < 0.5f; +} + /* ****************** Duplicate *********************** */ static void node_duplicate_reparent_recursive(bNode *node) diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 09e5a110a45..df20420e472 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -265,6 +265,8 @@ int node_find_indicated_socket(struct SpaceNode *snode, struct bNodeSocket **sockp, const float cursor[2], int in_out); +float node_link_dim_factor(const struct View2D *v2d, const struct bNodeLink *link); +bool node_link_is_hidden_or_dimmed(const struct View2D *v2d, const struct bNodeLink *link); void NODE_OT_duplicate(struct wmOperatorType *ot); void NODE_OT_delete(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index aadf93961e9..725c872e98f 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -36,6 +36,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_screen.h" #include "ED_node.h" /* own include */ #include "ED_render.h" @@ -1332,7 +1333,7 @@ static int cut_links_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), bmain); LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } @@ -1429,7 +1430,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) /* Count intersected links and clear test flag. */ int tot = 0; LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } link->flag &= ~NODE_LINK_TEST; @@ -1443,7 +1444,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) /* Mute links. */ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link) || (link->flag & NODE_LINK_TEST)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link) || (link->flag & NODE_LINK_TEST)) { continue; } @@ -1458,7 +1459,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) /* Clear remaining test flags. */ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } link->flag &= ~NODE_LINK_TEST; @@ -1894,9 +1895,11 @@ static bool ed_node_link_conditions(ScrArea *area, return false; } + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + /* test node for links */ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } @@ -1927,13 +1930,15 @@ void ED_node_link_intersect_test(ScrArea *area, int test) return; } + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + /* find link to select/highlight */ bNodeLink *selink = nullptr; float dist_best = FLT_MAX; LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { float coord_array[NODE_LINK_RESOL + 1][2]; - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } -- cgit v1.2.3 From 2e8641e45e6bcb98fdb714cdae416b3570a2f47d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 15 Jul 2021 12:43:03 +0200 Subject: Fix: crash when creating new node links This was a regression in rBc27ef1e9e8e663e02173e518c1e669e9845b3d1f. --- source/blender/editors/space_node/node_edit.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index b1afd6549bc..af9c888cbf7 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1232,6 +1232,10 @@ int node_find_indicated_socket( float node_link_dim_factor(const View2D *v2d, const bNodeLink *link) { + if (link->fromsock == nullptr || link->tosock == nullptr) { + return 1.0f; + } + const float min_endpoint_distance = std::min( std::max(BLI_rctf_length_x(&v2d->cur, link->fromsock->locx), BLI_rctf_length_y(&v2d->cur, link->fromsock->locy)), -- cgit v1.2.3 From 3df40cc343f09926c044dd9eeb7f81f5360ff330 Mon Sep 17 00:00:00 2001 From: Paul Golter Date: Thu, 15 Jul 2021 15:49:25 +0200 Subject: Fix: Subtitles: Order of channels in the .blend file will be kept in the .rst file. If text strips have the same start frame but are stacked on top of each other in different channels the order in which they are written in the .rst file was random before. Reviewed By: Richard Antalik Differential Revision: https://developer.blender.org/D11903 --- .../blender/editors/space_sequencer/sequencer_edit.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 75cf8542f67..4b26469aad3 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2934,6 +2934,23 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot) /** \name Export Subtitles Operator * \{ */ +/** Comparison function suitable to be used with BLI_listbase_sort(). */ +static int seq_cmp_time_startdisp_channel(const void *a, const void *b) +{ + Sequence *seq_a = (Sequence *)a; + Sequence *seq_b = (Sequence *)b; + + int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a); + int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b); + + /** If strips have the same start frame favor the one with a higher channel. **/ + if (seq_a_start == seq_b_start) { + return seq_a->machine > seq_b->machine; + } + + return (seq_a_start > seq_b_start); +} + static int sequencer_export_subtitles_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) @@ -3003,7 +3020,7 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - BLI_listbase_sort(&text_seq, SEQ_time_cmp_time_startdisp); + BLI_listbase_sort(&text_seq, seq_cmp_time_startdisp_channel); /* Open and write file. */ file = BLI_fopen(filepath, "w"); -- cgit v1.2.3 From 7898089de3f20b08a08516bd2fd2be7dd1c565fe Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 8 Jul 2021 22:16:50 +0200 Subject: Assets: Add an active asset library per workspace, for the UI to use This per-workspace active asset library will be used by the asset views later. Note that Asset Browsers have their own active asset library, overriding the one from the workspace. As part of this the `FileSelectAssetLibraryUID` type gets replaced by `AssetLibraryReference` which is on the asset level now, not the File/Asset Browser level. But some more work is needed to complete that, which is better done in a separate commit. This also moves the asset library from/to enum-value logic from RNA to the editor asset level, which will later be used by the asset view. --- source/blender/editors/asset/CMakeLists.txt | 2 + source/blender/editors/asset/asset_edit.cc | 53 +++++++++++++++++++++++++++ source/blender/editors/include/ED_asset.h | 6 +++ source/blender/editors/space_file/file_draw.c | 2 +- source/blender/editors/space_file/filelist.c | 2 +- source/blender/editors/space_file/filesel.c | 12 +++--- 6 files changed, 69 insertions(+), 8 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 8c5f91561b7..794e64fa420 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -19,6 +19,7 @@ set(INC ../include ../../blenkernel ../../blenlib + ../../blenloader ../../makesdna ../../makesrna ../../windowmanager @@ -34,6 +35,7 @@ set(SRC ) set(LIB + bf_blenloader ) blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc index d20de4141cb..5ae07a54b11 100644 --- a/source/blender/editors/asset/asset_edit.cc +++ b/source/blender/editors/asset/asset_edit.cc @@ -65,3 +65,56 @@ bool ED_asset_can_make_single_from_context(const bContext *C) /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; } + +/* TODO better place? */ +/* TODO What about the setter and the itemf? */ +#include "BKE_preferences.h" +#include "DNA_asset_types.h" +#include "DNA_userdef_types.h" +int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library) +{ + /* Simple case: Predefined repo, just set the value. */ + if (library->type < ASSET_LIBRARY_CUSTOM) { + return library->type; + } + + /* Note that the path isn't checked for validity here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, library->custom_library_index); + if (user_library) { + return ASSET_LIBRARY_CUSTOM + library->custom_library_index; + } + + BLI_assert(0); + return ASSET_LIBRARY_LOCAL; +} + +AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) +{ + AssetLibraryReference library; + + /* Simple case: Predefined repo, just set the value. */ + if (value < ASSET_LIBRARY_CUSTOM) { + library.type = value; + library.custom_library_index = -1; + BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL)); + return library; + } + + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, value - ASSET_LIBRARY_CUSTOM); + + /* Note that the path isn't checked for validity here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (!user_library) { + library.type = ASSET_LIBRARY_LOCAL; + library.custom_library_index = -1; + } + else if (user_library && is_valid) { + library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; + library.type = ASSET_LIBRARY_CUSTOM; + } + return library; +} diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index dd505167fe5..a2ef0755fbd 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -24,11 +24,17 @@ extern "C" { #endif +struct AssetLibraryReference; +struct bContext; + bool ED_asset_mark_id(const struct bContext *C, struct ID *id); bool ED_asset_clear_id(struct ID *id); bool ED_asset_can_make_single_from_context(const struct bContext *C); +int ED_asset_library_reference_to_enum_value(const struct AssetLibraryReference *library); +struct AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); + void ED_operatortypes_asset(void); #ifdef __cplusplus diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index faa4b3cc9cc..a314a85491d 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -1105,7 +1105,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) return false; } /* Check if the library exists. */ - if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) || + if ((asset_params->asset_library.type == ASSET_LIBRARY_LOCAL) || filelist_is_dir(sfile->files, asset_params->base_params.dir)) { return false; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 0e15538e03b..c4b895dc54d 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1050,7 +1050,7 @@ static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *li if (library_a->type != library_b->type) { return false; } - if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) { + if (library_a->type == ASSET_LIBRARY_CUSTOM) { /* Don't only check the index, also check that it's valid. */ bUserAssetLibrary *library_ptr_a = BKE_preferences_asset_library_find_from_index( &U, library_a->custom_library_index); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 7ab93df81d2..7bc83e8fc79 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -118,7 +118,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params), "FileAssetSelectParams"); asset_params->base_params.details_flags = U_default.file_space_data.details_flags; - asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL; + asset_params->asset_library.type = ASSET_LIBRARY_LOCAL; asset_params->asset_library.custom_library_index = -1; asset_params->import_type = FILE_ASSET_IMPORT_APPEND; } @@ -420,26 +420,26 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) bUserAssetLibrary *user_library = NULL; /* Ensure valid repository, or fall-back to local one. */ - if (library->type == FILE_ASSET_LIBRARY_CUSTOM) { + if (library->type == ASSET_LIBRARY_CUSTOM) { BLI_assert(library->custom_library_index >= 0); user_library = BKE_preferences_asset_library_find_from_index(&U, library->custom_library_index); if (!user_library) { - library->type = FILE_ASSET_LIBRARY_LOCAL; + library->type = ASSET_LIBRARY_LOCAL; } } switch (library->type) { - case FILE_ASSET_LIBRARY_LOCAL: + case ASSET_LIBRARY_LOCAL: base_params->dir[0] = '\0'; break; - case FILE_ASSET_LIBRARY_CUSTOM: + case ASSET_LIBRARY_CUSTOM: BLI_assert(user_library); BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir)); break; } - base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; + base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; } void fileselect_refresh_params(SpaceFile *sfile) -- cgit v1.2.3 From 3feb3ce32d7d1fddf745bf30592cf68d906c295f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 8 Jul 2021 22:33:02 +0200 Subject: Assets: Expose active asset library in context For the Asset Browser, this returns the active asset library of the Asset Browser, otherwise it returns the one active in the workspace. This gives simple access to the active asset library from UI code and Python scripts. For example the upcoming Pose Library add-on uses this, as well as the upcoming asset view template. --- source/blender/editors/screen/screen_context.c | 11 +++++++++++ source/blender/editors/space_file/space_file.c | 24 +++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index d50962a56a9..ece6ba986f3 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -36,6 +36,7 @@ #include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "BLI_ghash.h" #include "BLI_listbase.h" @@ -111,6 +112,7 @@ const char *screen_context_dir[] = { "selected_editable_fcurves", "active_editable_fcurve", "selected_editable_keyframes", + "asset_library", NULL, }; @@ -1024,6 +1026,14 @@ static eContextResult screen_ctx_selected_editable_keyframes(const bContext *C, return CTX_RESULT_NO_DATA; } +static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataResult *result) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + CTX_data_pointer_set( + result, &workspace->id, &RNA_AssetLibraryReference, &workspace->active_asset_library); + return CTX_RESULT_OK; +} + /* Registry of context callback functions. */ typedef eContextResult (*context_callback)(const bContext *C, bContextDataResult *result); @@ -1098,6 +1108,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves); register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve); register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes); + register_context_function("asset_library", screen_ctx_asset_library); } /* Entry point for the screen context. */ diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 05d484d8e2e..e71c00e2312 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -868,7 +868,12 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C), } } -static const char *file_context_dir[] = {"active_file", "id", NULL}; +static const char *file_context_dir[] = { + "active_file", + "asset_library", + "id", + NULL, +}; static int /*eContextResult*/ file_context(const bContext *C, const char *member, @@ -899,6 +904,23 @@ static int /*eContextResult*/ file_context(const bContext *C, CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); return CTX_RESULT_OK; } + if (CTX_data_equals(member, "asset_library")) { + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + if (!asset_params) { + return CTX_RESULT_NO_DATA; + } + + BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, type) == + offsetof(AssetLibraryReference, type), + "Expected FileSelectAssetLibraryUID to match AssetLibraryReference"); + BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, custom_library_index) == + offsetof(AssetLibraryReference, custom_library_index), + "Expected FileSelectAssetLibraryUID to match AssetLibraryReference"); + + CTX_data_pointer_set( + result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library); + return CTX_RESULT_OK; + } if (CTX_data_equals(member, "id")) { const FileDirEntry *file = filelist_file(sfile->files, params->active_file); if (file == NULL) { -- cgit v1.2.3 From 35affaa971cfb2d1829031f49a6ef9fb19ca576f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 8 Jul 2021 23:20:26 +0200 Subject: Assets: AssetHandle type as temporary design to reference assets With temporary I mean that this is not intended to be part of the eventual asset system design. For that we are planning to have an `AssetRepresentation` instead, see T87235. Once the `AssetList` is implemented (see T88184), that would be the owner of the asset representations. However for the upcoming asset system, asset browser, asset view and pose library commits we need some kind of asset handle to pass around. That is what this commit introduces. Idea is a handle to wrap the `FileDirEntry` representing the asset, and an API to access its data (currently very small, will be extended in further commits). So the fact that an asset is currently a file internally is abstracted away. However: We have to expose it as file in the Python API, because we can't return the asset-handle directly there, for reasons explained in the code. So the active asset file is exposed as `bpy.context.asset_file_handle`. --- source/blender/editors/asset/asset_edit.cc | 7 +++++++ source/blender/editors/include/ED_asset.h | 2 ++ 2 files changed, 9 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc index 5ae07a54b11..949dc1890ad 100644 --- a/source/blender/editors/asset/asset_edit.cc +++ b/source/blender/editors/asset/asset_edit.cc @@ -23,6 +23,8 @@ #include "BKE_lib_id.h" #include "DNA_ID.h" +#include "DNA_asset_types.h" +#include "DNA_space_types.h" #include "UI_interface_icons.h" @@ -118,3 +120,8 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) } return library; } + +const char *ED_asset_handle_get_name(const AssetHandle *asset) +{ + return asset->file_data->name; +} diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index a2ef0755fbd..da0809f2e33 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -35,6 +35,8 @@ bool ED_asset_can_make_single_from_context(const struct bContext *C); int ED_asset_library_reference_to_enum_value(const struct AssetLibraryReference *library); struct AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); +const char *ED_asset_handle_get_name(const AssetHandle *asset); + void ED_operatortypes_asset(void); #ifdef __cplusplus -- cgit v1.2.3 From a26a059244f247a85c48a28dedeb4a551110c793 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 9 Jul 2021 12:56:26 +0200 Subject: Assets: Initial Asset List as part of the Asset System design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements a basic, WIP version of the asset list. This is needed to give the asset view UI template asset reading and displaying functionality. See: * Asset System: Data Storage, Reading & UI Access - https://developer.blender.org/T88184 Especially the asset list internals should change. It uses the File/Asset Browser's `FileList` API, which isn't really meant for access from outside the File Browser. But as explained in T88184, it does a lot of the stuff we currently need, so we (Sybren Stüvel and I) decided to go this route for now. Work on a file-list rewrite which integrates well with the asset system started in the `asset-system-filelist` branch. Further includes: * Operator to reload the asset list. * New `bpy.types.AssetHandle.get_full_library_path()` function, which gets the full path of the asset via the asset-list. * Changes to preview loading to prevent the preview loading job to run eternally for asset views. File Browsers have this issue too, but should be fixed separately. --- source/blender/editors/asset/CMakeLists.txt | 2 + source/blender/editors/asset/asset_edit.cc | 28 ++ source/blender/editors/asset/asset_list.cc | 637 +++++++++++++++++++++++++ source/blender/editors/asset/asset_ops.cc | 33 ++ source/blender/editors/include/ED_asset.h | 44 ++ source/blender/editors/space_file/filelist.c | 32 +- source/blender/editors/space_file/filelist.h | 6 +- source/blender/editors/space_file/space_file.c | 2 +- source/blender/editors/undo/ed_undo.c | 3 + source/blender/editors/util/ed_util.c | 3 + source/blender/editors/util/ed_util_ops.cc | 3 + 11 files changed, 783 insertions(+), 10 deletions(-) create mode 100644 source/blender/editors/asset/asset_list.cc (limited to 'source/blender/editors') diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 794e64fa420..d87879bea2a 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -31,11 +31,13 @@ set(INC_SYS set(SRC asset_edit.cc + asset_list.cc asset_ops.cc ) set(LIB bf_blenloader + bf_blenkernel ) blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc index 949dc1890ad..0937af0dbf1 100644 --- a/source/blender/editors/asset/asset_edit.cc +++ b/source/blender/editors/asset/asset_edit.cc @@ -18,10 +18,15 @@ * \ingroup edasset */ +#include +#include + #include "BKE_asset.h" #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BLO_readfile.h" + #include "DNA_ID.h" #include "DNA_asset_types.h" #include "DNA_space_types.h" @@ -32,6 +37,8 @@ #include "ED_asset.h" +using namespace blender; + bool ED_asset_mark_id(const bContext *C, ID *id) { if (id->asset_data) { @@ -47,6 +54,9 @@ bool ED_asset_mark_id(const bContext *C, ID *id) UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + return true; } @@ -59,6 +69,9 @@ bool ED_asset_clear_id(ID *id) /* Don't clear fake user here, there's no guarantee that it was actually set by * #ED_asset_mark_id(), it might have been something/someone else. */ + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + return true; } @@ -125,3 +138,18 @@ const char *ED_asset_handle_get_name(const AssetHandle *asset) { return asset->file_data->name; } + +void ED_asset_handle_get_full_library_path(const bContext *C, + const AssetLibraryReference *asset_library, + const AssetHandle *asset, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset); + if (asset_path.empty()) { + return; + } + + BLO_library_path_explode(asset_path.c_str(), r_full_lib_path, nullptr, nullptr); +} diff --git a/source/blender/editors/asset/asset_list.cc b/source/blender/editors/asset/asset_list.cc new file mode 100644 index 00000000000..1ea948d97d4 --- /dev/null +++ b/source/blender/editors/asset/asset_list.cc @@ -0,0 +1,637 @@ +/* + * 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 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * Abstractions to manage runtime asset lists with a global cache for multiple UI elements to + * access. + * Internally this uses the #FileList API and structures from `filelist.c`. This is just because it + * contains most necessary logic already and there's not much time for a more long-term solution. + */ + +#include +#include + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_screen.h" + +#include "BLI_function_ref.hh" +#include "BLI_hash.hh" +#include "BLI_map.hh" +#include "BLI_path_util.h" +#include "BLI_utility_mixins.hh" + +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "BKE_preferences.h" + +#include "ED_asset.h" +#include "ED_fileselect.h" +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* XXX uses private header of file-space. */ +#include "../space_file/filelist.h" + +using namespace blender; + +/** + * Wrapper to add logic to the AssetLibraryReference DNA struct. + */ +class AssetLibraryReferenceWrapper { + const AssetLibraryReference reference_; + + public: + /* Intentionally not `explicit`, allow implicit conversion for convienience. Might have to be + * NOLINT */ + AssetLibraryReferenceWrapper(const AssetLibraryReference &reference); + ~AssetLibraryReferenceWrapper() = default; + + friend bool operator==(const AssetLibraryReferenceWrapper &a, + const AssetLibraryReferenceWrapper &b); + uint64_t hash() const; +}; + +AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryReference &reference) + : reference_(reference) +{ +} + +bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b) +{ + return (a.reference_.type == b.reference_.type) && (a.reference_.type == ASSET_LIBRARY_CUSTOM) ? + (a.reference_.custom_library_index == b.reference_.custom_library_index) : + true; +} + +uint64_t AssetLibraryReferenceWrapper::hash() const +{ + uint64_t hash1 = DefaultHash{}(reference_.type); + if (reference_.type != ASSET_LIBRARY_CUSTOM) { + return hash1; + } + + uint64_t hash2 = DefaultHash{}( + reference_.custom_library_index); + return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */ +} + +/* -------------------------------------------------------------------- */ +/** \name Asset list API + * + * Internally re-uses #FileList from the File Browser. It does all the heavy lifting already. + * \{ */ + +/** + * RAII wrapper for `FileList` + */ +class FileListWrapper { + static void filelist_free_fn(FileList *list) + { + filelist_free(list); + MEM_freeN(list); + } + + std::unique_ptr file_list_; + + public: + explicit FileListWrapper(eFileSelectType filesel_type) + : file_list_(filelist_new(filesel_type), filelist_free_fn) + { + } + FileListWrapper(FileListWrapper &&other) = default; + FileListWrapper &operator=(FileListWrapper &&other) = default; + ~FileListWrapper() + { + /* Destructs the owned pointer. */ + file_list_ = nullptr; + } + + operator FileList *() const + { + return file_list_.get(); + } +}; + +class PreviewTimer { + /* Non-owning! The Window-Manager registers and owns this. */ + wmTimer *timer_ = nullptr; + + public: + void ensureRunning(const bContext *C) + { + if (!timer_) { + timer_ = WM_event_add_timer_notifier( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_LIST_PREVIEW, 0.01); + } + } + + void stop(const bContext *C) + { + if (timer_) { + WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), timer_); + timer_ = nullptr; + } + } +}; + +class AssetList : NonCopyable { + FileListWrapper filelist_; + AssetLibraryReference library_ref_; + PreviewTimer previews_timer_; + + public: + AssetList() = delete; + AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref); + AssetList(AssetList &&other) = default; + ~AssetList() = default; + + void setup(const AssetFilterSettings *filter_settings = nullptr); + void fetch(const bContext &C); + void ensurePreviewsJob(bContext *C); + void clear(bContext *C); + + bool needsRefetch() const; + void iterate(AssetListIterFn fn) const; + bool listen(const wmNotifier ¬ifier) const; + int size() const; + void tagMainDataDirty() const; + void remapID(ID *id_old, ID *id_new) const; + StringRef filepath() const; +}; + +AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref) + : filelist_(filesel_type), library_ref_(asset_library_ref) +{ +} + +void AssetList::setup(const AssetFilterSettings *filter_settings) +{ + FileList *files = filelist_; + + /* TODO there should only be one (FileSelectAssetLibraryUID vs. AssetLibraryReference). */ + FileSelectAssetLibraryUID file_asset_lib_ref; + file_asset_lib_ref.type = library_ref_.type; + file_asset_lib_ref.custom_library_index = library_ref_.custom_library_index; + + bUserAssetLibrary *user_library = nullptr; + + /* Ensure valid repository, or fall-back to local one. */ + if (library_ref_.type == ASSET_LIBRARY_CUSTOM) { + BLI_assert(library_ref_.custom_library_index >= 0); + + user_library = BKE_preferences_asset_library_find_from_index( + &U, library_ref_.custom_library_index); + } + + /* Relevant bits from file_refresh(). */ + /* TODO pass options properly. */ + filelist_setrecursion(files, 1); + filelist_setsorting(files, FILE_SORT_ALPHA, false); + filelist_setlibrary(files, &file_asset_lib_ref); + /* TODO different filtering settings require the list to be reread. That's a no-go for when we + * want to allow showing the same asset library with different filter settings (as in, + * different ID types). The filelist needs to be made smarter somehow, maybe goes together with + * the plan to separate the view (preview caching, filtering, etc. ) from the data. */ + filelist_setfilter_options( + files, + filter_settings != nullptr, + true, + true, /* Just always hide parent, prefer to not add an extra user option for this. */ + FILE_TYPE_BLENDERLIB, + filter_settings ? filter_settings->id_types : FILTER_ID_ALL, + true, + "", + ""); + + char path[FILE_MAXDIR] = ""; + if (user_library) { + BLI_strncpy(path, user_library->path, sizeof(path)); + filelist_setdir(files, path); + } + else { + filelist_setdir(files, path); + } +} + +void AssetList::fetch(const bContext &C) +{ + FileList *files = filelist_; + + if (filelist_needs_force_reset(files)) { + filelist_readjob_stop(files, CTX_wm_manager(&C)); + filelist_clear(files); + } + + if (filelist_needs_reading(files)) { + if (!filelist_pending(files)) { + filelist_readjob_start(files, NC_ASSET | ND_ASSET_LIST_READING, &C); + } + } + filelist_sort(files); + filelist_filter(files); +} + +bool AssetList::needsRefetch() const +{ + return filelist_needs_force_reset(filelist_) || filelist_needs_reading(filelist_); +} + +void AssetList::iterate(AssetListIterFn fn) const +{ + FileList *files = filelist_; + int numfiles = filelist_files_ensure(files); + + for (int i = 0; i < numfiles; i++) { + FileDirEntry *file = filelist_file(files, i); + if (!fn(*file)) { + break; + } + } +} + +void AssetList::ensurePreviewsJob(bContext *C) +{ + FileList *files = filelist_; + int numfiles = filelist_files_ensure(files); + + filelist_cache_previews_set(files, true); + filelist_file_cache_slidingwindow_set(files, 256); + /* TODO fetch all previews for now. */ + filelist_file_cache_block(files, numfiles / 2); + filelist_cache_previews_update(files); + + { + const bool previews_running = filelist_cache_previews_running(files) && + !filelist_cache_previews_done(files); + if (previews_running) { + previews_timer_.ensureRunning(C); + } + else { + /* Preview is not running, no need to keep generating update events! */ + previews_timer_.stop(C); + } + } +} + +void AssetList::clear(bContext *C) +{ + /* Based on #ED_fileselect_clear() */ + + FileList *files = filelist_; + filelist_readjob_stop(files, CTX_wm_manager(C)); + filelist_freelib(files); + filelist_clear(files); + + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, NULL); +} + +/** + * \return True if the asset-list needs a UI redraw. + */ +bool AssetList::listen(const wmNotifier ¬ifier) const +{ + switch (notifier.category) { + case NC_ID: { + if (ELEM(notifier.action, NA_RENAME)) { + return true; + } + break; + } + case NC_ASSET: + if (ELEM(notifier.data, ND_ASSET_LIST, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) { + return true; + } + if (ELEM(notifier.action, NA_ADDED, NA_REMOVED, NA_EDITED)) { + return true; + } + break; + } + + return false; +} + +/** + * \return The number of assets in the list. + */ +int AssetList::size() const +{ + return filelist_files_ensure(filelist_); +} + +void AssetList::tagMainDataDirty() const +{ + if (filelist_needs_reset_on_main_changes(filelist_)) { + /* Full refresh of the file list if local asset data was changed. Refreshing this view + * is cheap and users expect this to be updated immediately. */ + filelist_tag_force_reset(filelist_); + } +} + +void AssetList::remapID(ID * /*id_old*/, ID * /*id_new*/) const +{ + /* Trigger full refetch of the file list if main data was changed, don't even attempt remap + * pointers. We could give file list types a id-remap callback, but it's probably not worth it. + * Refreshing local file lists is relatively cheap. */ + tagMainDataDirty(); +} + +StringRef AssetList::filepath() const +{ + return filelist_dir(filelist_); +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Runtime asset list cache + * \{ */ + +/** + * Class managing a global asset list map, each entry being a list for a specific asset library. + */ +class AssetListStorage { + using AssetListMap = Map; + + public: + /* Purely static class, can't instantiate this. */ + AssetListStorage() = delete; + + static void fetch_library(const AssetLibraryReference &library_reference, + const bContext &C, + const AssetFilterSettings *filter_settings = nullptr); + static void destruct(); + static AssetList *lookup_list(const AssetLibraryReference &library_ref); + static void tagMainDataDirty(); + static void remapID(ID *id_new, ID *id_old); + + private: + static std::optional asset_library_reference_to_fileselect_type( + const AssetLibraryReference &library_reference); + + using is_new_t = bool; + static std::tuple ensure_list_storage( + const AssetLibraryReference &library_reference, eFileSelectType filesel_type); + + static AssetListMap &global_storage(); +}; + +void AssetListStorage::fetch_library(const AssetLibraryReference &library_reference, + const bContext &C, + const AssetFilterSettings *filter_settings) +{ + std::optional filesel_type = asset_library_reference_to_fileselect_type(library_reference); + if (!filesel_type) { + return; + } + + auto [list, is_new] = ensure_list_storage(library_reference, *filesel_type); + if (is_new || list.needsRefetch()) { + list.setup(filter_settings); + list.fetch(C); + } +} + +void AssetListStorage::destruct() +{ + global_storage().~AssetListMap(); +} + +AssetList *AssetListStorage::lookup_list(const AssetLibraryReference &library_ref) +{ + return global_storage().lookup_ptr(library_ref); +} + +void AssetListStorage::tagMainDataDirty() +{ + for (AssetList &list : global_storage().values()) { + list.tagMainDataDirty(); + } +} + +void AssetListStorage::remapID(ID *id_new, ID *id_old) +{ + for (AssetList &list : global_storage().values()) { + list.remapID(id_new, id_old); + } +} + +std::optional AssetListStorage::asset_library_reference_to_fileselect_type( + const AssetLibraryReference &library_reference) +{ + switch (library_reference.type) { + case ASSET_LIBRARY_CUSTOM: + return FILE_LOADLIB; + case ASSET_LIBRARY_LOCAL: + return FILE_MAIN_ASSET; + } + + return std::nullopt; +} + +std::tuple AssetListStorage::ensure_list_storage( + const AssetLibraryReference &library_reference, eFileSelectType filesel_type) +{ + AssetListMap &storage = global_storage(); + + if (AssetList *list = storage.lookup_ptr(library_reference)) { + return {*list, false}; + } + storage.add(library_reference, AssetList(filesel_type, library_reference)); + return {storage.lookup(library_reference), true}; +} + +/** + * Wrapper for Construct on First Use idiom, to avoid the Static Initialization Fiasco. + */ +AssetListStorage::AssetListMap &AssetListStorage::global_storage() +{ + static AssetListMap global_storage_; + return global_storage_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +/** + * Invoke asset list reading, potentially in a parallel job. Won't wait until the job is done, + * and may return earlier. + */ +void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, + const AssetFilterSettings *filter_settings, + const bContext *C) +{ + AssetListStorage::fetch_library(*library_reference, *C, filter_settings); +} + +void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C) +{ + + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + list->ensurePreviewsJob(C); + } +} + +void ED_assetlist_clear(const AssetLibraryReference *library_reference, bContext *C) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + list->clear(C); + } +} + +bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference) +{ + return AssetListStorage::lookup_list(*library_reference) != nullptr; +} + +/* TODO expose AssetList with an iterator? */ +void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + list->iterate(fn); + } +} + +/* TODO hack to use the File Browser path, so we can keep all the import logic handled by the asset + * API. Get rid of this once the File Browser is integrated better with the asset list. */ +static const char *assetlist_library_path_from_sfile_get_hack(const bContext *C) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + if (!sfile || !ED_fileselect_is_asset_browser(sfile)) { + return nullptr; + } + + FileAssetSelectParams *asset_select_params = ED_fileselect_get_asset_params(sfile); + if (!asset_select_params) { + return nullptr; + } + + return filelist_dir(sfile->files); +} + +std::string ED_assetlist_asset_filepath_get(const bContext *C, + const AssetLibraryReference &library_reference, + const AssetHandle &asset_handle) +{ + if (asset_handle.file_data->id || !asset_handle.file_data->asset_data) { + return {}; + } + const char *library_path = ED_assetlist_library_path(&library_reference); + if (!library_path) { + library_path = assetlist_library_path_from_sfile_get_hack(C); + } + if (!library_path) { + return {}; + } + const char *asset_relpath = asset_handle.file_data->relpath; + + char path[FILE_MAX_LIBEXTRA]; + BLI_join_dirfile(path, sizeof(path), library_path, asset_relpath); + + return path; +} + +ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle) +{ + return asset_handle->file_data->asset_data ? asset_handle->file_data->id : nullptr; +} + +ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle) +{ + ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data); + if (imbuf) { + return imbuf; + } + + return filelist_geticon_image_ex(asset_handle->file_data); +} + +const char *ED_assetlist_library_path(const AssetLibraryReference *library_reference) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + return list->filepath().data(); + } + return nullptr; +} + +/** + * \return True if the region needs a UI redraw. + */ +bool ED_assetlist_listen(const AssetLibraryReference *library_reference, + const wmNotifier *notifier) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + return list->listen(*notifier); + } + return false; +} + +/** + * \return The number of assets stored in the asset list for \a library_reference, or -1 if there + * is no list fetched for it. + */ +int ED_assetlist_size(const AssetLibraryReference *library_reference) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + return list->size(); + } + return -1; +} + +/** + * Tag all asset lists in the storage that show main data as needing an update (refetch). + * + * This only tags the data. If the asset list is visible on screen, the space is still responsible + * for ensuring the necessary redraw. It can use #ED_assetlist_listen() to check if the asset-list + * needs a redraw for a given notifier. + */ +void ED_assetlist_storage_tag_main_data_dirty() +{ + AssetListStorage::tagMainDataDirty(); +} + +/** + * Remapping of ID pointers within the asset lists. Typically called when an ID is deleted to clear + * all references to it (\a id_new is null then). + */ +void ED_assetlist_storage_id_remap(ID *id_old, ID *id_new) +{ + AssetListStorage::remapID(id_old, id_new); +} + +/** + * Can't wait for static deallocation to run. There's nested data allocated with our guarded + * allocator, it will complain about unfreed memory on exit. + */ +void ED_assetlist_storage_exit() +{ + AssetListStorage::destruct(); +} + +/** \} */ diff --git a/source/blender/editors/asset/asset_ops.cc b/source/blender/editors/asset/asset_ops.cc index 8ca1b488a1d..79edd1f8a6a 100644 --- a/source/blender/editors/asset/asset_ops.cc +++ b/source/blender/editors/asset/asset_ops.cc @@ -252,8 +252,41 @@ static void ASSET_OT_clear(wmOperatorType *ot) /* -------------------------------------------------------------------- */ +static bool asset_list_refresh_poll(bContext *C) +{ + const AssetLibraryReference *library = CTX_wm_asset_library(C); + if (!library) { + return false; + } + + return ED_assetlist_storage_has_list_for_library(library); +} + +static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) +{ + const AssetLibraryReference *library = CTX_wm_asset_library(C); + ED_assetlist_clear(library, C); + return OPERATOR_FINISHED; +} + +static void ASSET_OT_list_refresh(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refresh Asset List"; + ot->description = "Trigger a reread of the assets"; + ot->idname = "ASSET_OT_list_refresh"; + + /* api callbacks */ + ot->exec = asset_list_refresh_exec; + ot->poll = asset_list_refresh_poll; +} + +/* -------------------------------------------------------------------- */ + void ED_operatortypes_asset(void) { WM_operatortype_append(ASSET_OT_mark); WM_operatortype_append(ASSET_OT_clear); + + WM_operatortype_append(ASSET_OT_list_refresh); } diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index da0809f2e33..381071a5c6f 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -20,12 +20,18 @@ #pragma once +#include "DNA_ID_enums.h" + #ifdef __cplusplus extern "C" { #endif +struct AssetFilterSettings; struct AssetLibraryReference; struct bContext; +struct Main; +struct ReportList; +struct wmNotifier; bool ED_asset_mark_id(const struct bContext *C, struct ID *id); bool ED_asset_clear_id(struct ID *id); @@ -36,9 +42,47 @@ int ED_asset_library_reference_to_enum_value(const struct AssetLibraryReference struct AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); const char *ED_asset_handle_get_name(const AssetHandle *asset); +void ED_asset_handle_get_full_library_path(const struct bContext *C, + const AssetLibraryReference *asset_library, + const AssetHandle *asset, + char r_full_lib_path[]); + +void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, + const struct AssetFilterSettings *filter_settings, + const struct bContext *C); +void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, + struct bContext *C); +void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); +bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); +void ED_assetlist_storage_tag_main_data_dirty(void); +void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new); +void ED_assetlist_storage_exit(void); + +ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle); +struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle); +const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference); + +bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference, + const struct wmNotifier *notifier); +int ED_assetlist_size(const struct AssetLibraryReference *library_reference); void ED_operatortypes_asset(void); #ifdef __cplusplus } #endif + +/* TODO move to C++ asset-list header? */ +#ifdef __cplusplus + +# include + +std::string ED_assetlist_asset_filepath_get(const bContext *C, + const AssetLibraryReference &library_reference, + const AssetHandle &asset_handle); + +# include "BLI_function_ref.hh" +/* Can return false to stop iterating. */ +using AssetListIterFn = blender::FunctionRef; +void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn); +#endif diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index c4b895dc54d..492a189fc81 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -334,6 +334,7 @@ typedef struct FileListEntryCache { /* Previews handling. */ TaskPool *previews_pool; ThreadQueue *previews_done; + size_t previews_todo_count; } FileListEntryCache; /* FileListCache.flags */ @@ -1153,7 +1154,7 @@ ImBuf *filelist_file_getimage(const FileDirEntry *file) return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL; } -static ImBuf *filelist_geticon_image_ex(FileDirEntry *file) +ImBuf *filelist_geticon_image_ex(const FileDirEntry *file) { ImBuf *ibuf = NULL; @@ -1494,6 +1495,7 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat /* That way task freeing function won't free th preview, since it does not own it anymore. */ atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL); BLI_thread_queue_push(cache->previews_done, preview); + atomic_fetch_and_sub_z(&cache->previews_todo_count, 1); } // printf("%s: End (%d)...\n", __func__, threadid); @@ -1520,6 +1522,7 @@ static void filelist_cache_preview_ensure_running(FileListEntryCache *cache) if (!cache->previews_pool) { cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW); cache->previews_done = BLI_thread_queue_init(); + cache->previews_todo_count = 0; IMB_thumb_locks_acquire(); } @@ -1553,6 +1556,7 @@ static void filelist_cache_previews_free(FileListEntryCache *cache) BLI_task_pool_free(cache->previews_pool); cache->previews_pool = NULL; cache->previews_done = NULL; + cache->previews_todo_count = 0; IMB_thumb_locks_release(); } @@ -1633,6 +1637,8 @@ static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size) cache->size = cache_size; cache->flags = FLC_IS_INIT; + cache->previews_todo_count = 0; + /* We cannot translate from non-main thread, so init translated strings once from here. */ IMB_thumb_ensure_translations(); } @@ -2384,7 +2390,8 @@ void filelist_cache_previews_set(FileList *filelist, const bool use_previews) if (use_previews && (filelist->flags & FL_IS_READY)) { cache->flags |= FLC_PREVIEWS_ACTIVE; - BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL)); + BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL) && + (cache->previews_todo_count == 0)); // printf("%s: Init Previews...\n", __func__); @@ -2457,6 +2464,18 @@ bool filelist_cache_previews_running(FileList *filelist) return (cache->previews_pool != NULL); } +bool filelist_cache_previews_done(FileList *filelist) +{ + FileListEntryCache *cache = &filelist->filelist_cache; + if ((cache->flags & FLC_PREVIEWS_ACTIVE) == 0) { + /* There are no previews. */ + return false; + } + + return (cache->previews_pool == NULL) || (cache->previews_done == NULL) || + (cache->previews_todo_count == (size_t)BLI_thread_queue_len(cache->previews_done)); +} + /* would recognize .blend as well */ static bool file_is_blend_backup(const char *str) { @@ -3432,7 +3451,7 @@ static void filelist_readjob_free(void *flrjv) MEM_freeN(flrj); } -void filelist_readjob_start(FileList *filelist, const bContext *C) +void filelist_readjob_start(FileList *filelist, const int space_notifier, const bContext *C) { Main *bmain = CTX_data_main(C); wmJob *wm_job; @@ -3464,7 +3483,7 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) filelist_readjob_endjob(flrj); filelist_readjob_free(flrj); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED, NULL); + WM_event_add_notifier(C, space_notifier | NA_JOB_FINISHED, NULL); return; } @@ -3476,10 +3495,7 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) WM_JOB_PROGRESS, WM_JOB_TYPE_FILESEL_READDIR); WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free); - WM_jobs_timer(wm_job, - 0.01, - NC_SPACE | ND_SPACE_FILE_LIST, - NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED); + WM_jobs_timer(wm_job, 0.01, space_notifier, space_notifier | NA_JOB_FINISHED); WM_jobs_callbacks( wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob); diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 0aace74e621..cb98cf6e74a 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -79,6 +79,7 @@ void filelist_init_icons(void); void filelist_free_icons(void); struct ImBuf *filelist_getimage(struct FileList *filelist, const int index); struct ImBuf *filelist_file_getimage(const FileDirEntry *file); +struct ImBuf *filelist_geticon_image_ex(const FileDirEntry *file); struct ImBuf *filelist_geticon_image(struct FileList *filelist, const int index); int filelist_geticon(struct FileList *filelist, const int index, const bool is_main); @@ -144,13 +145,16 @@ struct BlendHandle *filelist_lib(struct FileList *filelist); bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group); void filelist_freelib(struct FileList *filelist); -void filelist_readjob_start(struct FileList *filelist, const struct bContext *C); +void filelist_readjob_start(struct FileList *filelist, + int space_notifier, + const struct bContext *C); void filelist_readjob_stop(struct FileList *filelist, struct wmWindowManager *wm); int filelist_readjob_running(struct FileList *filelist, struct wmWindowManager *wm); bool filelist_cache_previews_update(struct FileList *filelist); void filelist_cache_previews_set(struct FileList *filelist, const bool use_previews); bool filelist_cache_previews_running(struct FileList *filelist); +bool filelist_cache_previews_done(struct FileList *filelist); #ifdef __cplusplus } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index e71c00e2312..31c7dee294b 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -367,7 +367,7 @@ static void file_refresh(const bContext *C, ScrArea *area) if (filelist_needs_reading(sfile->files)) { if (!filelist_pending(sfile->files)) { - filelist_readjob_start(sfile->files, C); + filelist_readjob_start(sfile->files, NC_SPACE | ND_SPACE_FILE_LIST, C); } } diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 23161028e03..0368252f54b 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -50,6 +50,7 @@ #include "BLO_blend_validate.h" +#include "ED_asset.h" #include "ED_gpencil.h" #include "ED_object.h" #include "ED_outliner.h" @@ -268,6 +269,8 @@ static void ed_undo_step_post(bContext *C, WM_toolsystem_refresh_active(C); WM_toolsystem_refresh_screen_all(bmain); + ED_assetlist_storage_tag_main_data_dirty(); + if (CLOG_CHECK(&LOG, 1)) { BKE_undosys_print(wm->undo_stack); } diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 7bbdc58474f..73f328f85d7 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -46,6 +46,7 @@ #include "DEG_depsgraph.h" #include "ED_armature.h" +#include "ED_asset.h" #include "ED_image.h" #include "ED_mesh.h" #include "ED_object.h" @@ -169,6 +170,8 @@ void ED_editors_init(bContext *C) ED_space_image_paint_update(bmain, wm, scene); } + ED_assetlist_storage_tag_main_data_dirty(); + SWAP(int, reports->flag, reports_flag_prev); wm->op_undo_depth--; } diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index 462f7768f81..7d32d252718 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -36,6 +36,7 @@ #include "BLT_translation.h" +#include "ED_asset.h" #include "ED_render.h" #include "ED_undo.h" #include "ED_util.h" @@ -131,9 +132,11 @@ static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op)) if (preview) { BKE_previewimg_clear(preview); } + UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); + ED_assetlist_storage_tag_main_data_dirty(); return OPERATOR_FINISHED; } -- cgit v1.2.3 From 637731dc610370782b1041ece4425bf3e6086ba0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 8 Jul 2021 16:27:18 +0200 Subject: Assets: Abstraction for temporary loading of asset data-blocks This is an editor-level abstraction for the `BLO_library_temp_xxx()` API for temporary loading of data-blocks from another Blend file. It abstracts away the asset specific code, like asset file-path handling and local asset data-block handling. Main use-case for this is applying assets as presets that are based on data-blocks, like poses. Such preset assets are an important part of the asset system design, so such an abstraction will likely find more usage in the future. --- source/blender/editors/asset/CMakeLists.txt | 1 + .../editors/asset/asset_temp_id_consumer.cc | 113 +++++++++++++++++++++ source/blender/editors/include/ED_asset.h | 11 ++ 3 files changed, 125 insertions(+) create mode 100644 source/blender/editors/asset/asset_temp_id_consumer.cc (limited to 'source/blender/editors') diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index d87879bea2a..a27975bc37b 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -33,6 +33,7 @@ set(SRC asset_edit.cc asset_list.cc asset_ops.cc + asset_temp_id_consumer.cc ) set(LIB diff --git a/source/blender/editors/asset/asset_temp_id_consumer.cc b/source/blender/editors/asset/asset_temp_id_consumer.cc new file mode 100644 index 00000000000..24e1fc86fef --- /dev/null +++ b/source/blender/editors/asset/asset_temp_id_consumer.cc @@ -0,0 +1,113 @@ +/* + * 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 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * API for temporary loading of asset IDs. + * Uses the `BLO_library_temp_xxx()` API internally. + */ + +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "BKE_report.h" + +#include "BLI_utility_mixins.hh" + +#include "BLO_readfile.h" + +#include "MEM_guardedalloc.h" + +#include "ED_asset.h" + +using namespace blender; + +class AssetTemporaryIDConsumer : NonCopyable, NonMovable { + const AssetHandle &handle_; + TempLibraryContext *temp_lib_context_ = nullptr; + + public: + AssetTemporaryIDConsumer(const AssetHandle &handle) : handle_(handle) + { + } + ~AssetTemporaryIDConsumer() + { + if (temp_lib_context_) { + BLO_library_temp_free(temp_lib_context_); + } + } + + ID *get_local_id() + { + return ED_assetlist_asset_local_id_get(&handle_); + } + + ID *import_id(const bContext *C, + const AssetLibraryReference &asset_library, + ID_Type id_type, + Main &bmain, + ReportList &reports) + { + const char *asset_name = ED_asset_handle_get_name(&handle_); + char blend_file_path[FILE_MAX_LIBEXTRA]; + ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path); + + temp_lib_context_ = BLO_library_temp_load_id( + &bmain, blend_file_path, id_type, asset_name, &reports); + + if (temp_lib_context_ == nullptr || temp_lib_context_->temp_id == nullptr) { + BKE_reportf(&reports, RPT_ERROR, "Unable to load %s from %s", asset_name, blend_file_path); + return nullptr; + } + + BLI_assert(GS(temp_lib_context_->temp_id->name) == id_type); + return temp_lib_context_->temp_id; + } +}; + +AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle) +{ + if (!handle) { + return nullptr; + } + BLI_assert(handle->file_data->asset_data != nullptr); + return reinterpret_cast( + OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle)); +} + +void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer) +{ + OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer); +} + +ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, + const bContext *C, + const AssetLibraryReference *asset_library, + ID_Type id_type, + Main *bmain, + ReportList *reports) +{ + if (!(consumer_ && asset_library && bmain && reports)) { + return nullptr; + } + AssetTemporaryIDConsumer *consumer = reinterpret_cast(consumer_); + + if (ID *local_id = consumer->get_local_id()) { + return local_id; + } + return consumer->import_id(C, *asset_library, id_type, *bmain, *reports); +} diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index 381071a5c6f..d33085f1cc4 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -33,6 +33,8 @@ struct Main; struct ReportList; struct wmNotifier; +typedef struct AssetTempIDConsumer AssetTempIDConsumer; + bool ED_asset_mark_id(const struct bContext *C, struct ID *id); bool ED_asset_clear_id(struct ID *id); @@ -47,6 +49,15 @@ void ED_asset_handle_get_full_library_path(const struct bContext *C, const AssetHandle *asset, char r_full_lib_path[]); +AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle); +void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); +struct ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer, + const struct bContext *C, + const AssetLibraryReference *asset_library, + ID_Type id_type, + struct Main *bmain, + struct ReportList *reports); + void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, const struct AssetFilterSettings *filter_settings, const struct bContext *C); -- cgit v1.2.3 From f3610a23d12bc9e490bb8d58120336de9a6cb196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 12 Jul 2021 13:18:09 +0200 Subject: Animation: new pose library based on Asset Browser Introduce new pose library, based on the Asset Browser. Contrary to the old pose library (in `editors/armature/pose_lib.c`), which stored an entire library of poses in an `Action`, in the new library each pose is its own `Action` datablock. This is done for compatibility with the asset browser, and also to make it easier to attach preview images, share datablocks, etc. Furthermore, it opens the door to having animation snippets in the pose library as well. This commit contains the C code for the pose library; in order to fully use it, an addon is required as well (which will be committed shortly). --- source/blender/editors/armature/CMakeLists.txt | 3 + source/blender/editors/armature/armature_intern.h | 4 + source/blender/editors/armature/armature_ops.c | 2 + source/blender/editors/armature/pose_backup.c | 139 +++++ source/blender/editors/armature/pose_lib_2.c | 638 ++++++++++++++++++++++ source/blender/editors/include/ED_armature.h | 16 + 6 files changed, 802 insertions(+) create mode 100644 source/blender/editors/armature/pose_backup.c create mode 100644 source/blender/editors/armature/pose_lib_2.c (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index 0030e78002b..e942bcf2902 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../blenloader ../../blentranslation ../../depsgraph ../../gpu @@ -43,9 +44,11 @@ set(SRC armature_utils.c editarmature_undo.c meshlaplacian.c + pose_backup.c pose_edit.c pose_group.c pose_lib.c + pose_lib_2.c pose_select.c pose_slide.c pose_transform.c diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index d429e51061b..f9950d27e97 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -203,6 +203,10 @@ void POSELIB_OT_pose_move(struct wmOperatorType *ot); void POSELIB_OT_browse_interactive(struct wmOperatorType *ot); void POSELIB_OT_apply_pose(struct wmOperatorType *ot); +/* pose_lib_2.c */ +void POSELIB_OT_apply_pose_asset(struct wmOperatorType *ot); +void POSELIB_OT_blend_pose_asset(struct wmOperatorType *ot); + /* ******************************************************* */ /* Pose Sliding Tools */ /* pose_slide.c */ diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index a0face26bae..fbd89106de5 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -131,6 +131,8 @@ void ED_operatortypes_armature(void) /* POSELIB */ WM_operatortype_append(POSELIB_OT_browse_interactive); WM_operatortype_append(POSELIB_OT_apply_pose); + WM_operatortype_append(POSELIB_OT_apply_pose_asset); + WM_operatortype_append(POSELIB_OT_blend_pose_asset); WM_operatortype_append(POSELIB_OT_pose_add); WM_operatortype_append(POSELIB_OT_pose_remove); diff --git a/source/blender/editors/armature/pose_backup.c b/source/blender/editors/armature/pose_backup.c new file mode 100644 index 00000000000..dffcd9bdc5a --- /dev/null +++ b/source/blender/editors/armature/pose_backup.c @@ -0,0 +1,139 @@ +/* + * 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 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edarmature + */ + +#include "ED_armature.h" + +#include + +#include "BLI_listbase.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_object_types.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_idprop.h" + +/* simple struct for storing backup info for one pose channel */ +typedef struct PoseChannelBackup { + struct PoseChannelBackup *next, *prev; + + struct bPoseChannel *pchan; /* Pose channel this backup is for. */ + + struct bPoseChannel olddata; /* Backup of pose channel. */ + struct IDProperty *oldprops; /* Backup copy (needs freeing) of pose channel's ID properties. */ +} PoseChannelBackup; + +typedef struct PoseBackup { + bool is_bone_selection_relevant; + ListBase /* PoseChannelBackup* */ backups; +} PoseBackup; + +static PoseBackup *pose_backup_create(const Object *ob, + const bAction *action, + const bool is_bone_selection_relevant) +{ + ListBase backups = {NULL, NULL}; + const bArmature *armature = ob->data; + + /* TODO(Sybren): reuse same approach as in `armature_pose.cc` in this function, as that doesn't + * have the assumption that action group names are bone names. */ + LISTBASE_FOREACH (bActionGroup *, agrp, &action->groups) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name); + if (pchan == NULL) { + continue; + } + + if (is_bone_selection_relevant && !PBONE_SELECTED(armature, pchan->bone)) { + continue; + } + + PoseChannelBackup *chan_bak = MEM_callocN(sizeof(*chan_bak), "PoseChannelBackup"); + chan_bak->pchan = pchan; + memcpy(&chan_bak->olddata, chan_bak->pchan, sizeof(chan_bak->olddata)); + + if (pchan->prop) { + chan_bak->oldprops = IDP_CopyProperty(pchan->prop); + } + + BLI_addtail(&backups, chan_bak); + } + + /* PoseBackup is constructed late, so that the above loop can use stack variables. */ + PoseBackup *pose_backup = MEM_callocN(sizeof(*pose_backup), __func__); + pose_backup->is_bone_selection_relevant = is_bone_selection_relevant; + pose_backup->backups = backups; + return pose_backup; +} + +PoseBackup *ED_pose_backup_create_all_bones(const Object *ob, const bAction *action) +{ + return pose_backup_create(ob, action, false); +} + +PoseBackup *ED_pose_backup_create_selected_bones(const Object *ob, const bAction *action) +{ + /* See if bone selection is relevant. */ + bool all_bones_selected = true; + bool no_bones_selected = true; + const bArmature *armature = ob->data; + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + const bool is_selected = PBONE_SELECTED(armature, pchan->bone); + all_bones_selected &= is_selected; + no_bones_selected &= !is_selected; + } + + /* If no bones are selected, act as if all are. */ + const bool is_bone_selection_relevant = !all_bones_selected && !no_bones_selected; + return pose_backup_create(ob, action, is_bone_selection_relevant); +} + +bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup) +{ + return pose_backup->is_bone_selection_relevant; +} + +void ED_pose_backup_restore(const PoseBackup *pbd) +{ + LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) { + memcpy(chan_bak->pchan, &chan_bak->olddata, sizeof(chan_bak->olddata)); + + if (chan_bak->oldprops) { + IDP_SyncGroupValues(chan_bak->pchan->prop, chan_bak->oldprops); + } + + /* TODO: constraints settings aren't restored yet, + * even though these could change (though not that likely) */ + } +} + +void ED_pose_backup_free(PoseBackup *pbd) +{ + LISTBASE_FOREACH_MUTABLE (PoseChannelBackup *, chan_bak, &pbd->backups) { + if (chan_bak->oldprops) { + IDP_FreeProperty(chan_bak->oldprops); + } + BLI_freelinkN(&pbd->backups, chan_bak); + } + MEM_freeN(pbd); +} diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c new file mode 100644 index 00000000000..eb091296282 --- /dev/null +++ b/source/blender/editors/armature/pose_lib_2.c @@ -0,0 +1,638 @@ +/* + * 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 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021, Blender Foundation + */ + +/** \file + * \ingroup edarmature + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_armature_types.h" + +#include "BKE_action.h" +#include "BKE_anim_data.h" +#include "BKE_animsys.h" +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_lib_id.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" + +#include "ED_armature.h" +#include "ED_asset.h" +#include "ED_keyframing.h" +#include "ED_screen.h" + +#include "armature_intern.h" + +typedef enum ePoseBlendState { + POSE_BLEND_INIT, + POSE_BLEND_BLENDING, + POSE_BLEND_ORIGINAL, + POSE_BLEND_CONFIRM, + POSE_BLEND_CANCEL, +} ePoseBlendState; + +typedef struct PoseBlendData { + ePoseBlendState state; + bool needs_redraw; + + struct { + bool use_release_confirm; + int drag_start_xy[2]; + int init_event_type; + + bool cursor_wrap_enabled; + } release_confirm_info; + + /* For temp-loading the Action from the pose library. */ + AssetTempIDConsumer *temp_id_consumer; + + /* Blend factor, interval [0, 1] for interpolating between current and given pose. */ + float blend_factor; + struct PoseBackup *pose_backup; + + Object *ob; /* Object to work on. */ + bAction *act; /* Pose to blend into the current pose. */ + bool free_action; + + Scene *scene; /* For auto-keying. */ + ScrArea *area; /* For drawing status text. */ + + /** Info-text to print in header. */ + char headerstr[UI_MAX_DRAW_STR]; +} PoseBlendData; + +/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */ +static void poselib_backup_posecopy(PoseBlendData *pbd) +{ + pbd->pose_backup = ED_pose_backup_create_selected_bones(pbd->ob, pbd->act); + + if (pbd->state == POSE_BLEND_INIT) { + /* Ready for blending now. */ + pbd->state = POSE_BLEND_BLENDING; + } +} + +/* ---------------------------- */ + +/* Auto-key/tag bones affected by the pose Action. */ +static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd) +{ + if (!autokeyframe_cfra_can_key(scene, &pbd->ob->id)) { + return; + } + + AnimData *adt = BKE_animdata_from_id(&pbd->ob->id); + if (adt != NULL && adt->action != NULL && ID_IS_LINKED(&adt->action->id)) { + /* Changes to linked-in Actions are not allowed. */ + return; + } + + bPose *pose = pbd->ob->pose; + bAction *act = pbd->act; + + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); + ListBase dsources = {NULL, NULL}; + + /* start tagging/keying */ + const bArmature *armature = pbd->ob->data; + LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) { + /* only for selected bones unless there aren't any selected, in which case all are included */ + bPoseChannel *pchan = BKE_pose_channel_find_name(pose, agrp->name); + if (pchan == NULL) { + continue; + } + + if (ED_pose_backup_is_selection_relevant(pbd->pose_backup) && + !PBONE_SELECTED(armature, pchan->bone)) { + continue; + } + + /* Add data-source override for the PoseChannel, to be used later. */ + ANIM_relative_keyingset_add_source(&dsources, &pbd->ob->id, &RNA_PoseBone, pchan); + } + + /* Perform actual auto-keying. */ + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + BLI_freelistN(&dsources); + + /* send notifiers for this */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); +} + +/* Apply the relevant changes to the pose */ +static void poselib_blend_apply(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = (PoseBlendData *)op->customdata; + + if (pbd->state == POSE_BLEND_BLENDING) { + BLI_snprintf(pbd->headerstr, + sizeof(pbd->headerstr), + TIP_("PoseLib blending: \"%s\" at %3.0f%%"), + pbd->act->id.name + 2, + pbd->blend_factor * 100); + ED_area_status_text(pbd->area, pbd->headerstr); + + ED_workspace_status_text( + C, TIP_("Tab: show original pose; Horizontal mouse movement: change blend percentage")); + } + else { + ED_area_status_text(pbd->area, TIP_("PoseLib showing original pose")); + ED_workspace_status_text(C, TIP_("Tab: show blended pose")); + } + + if (!pbd->needs_redraw) { + return; + } + pbd->needs_redraw = false; + + ED_pose_backup_restore(pbd->pose_backup); + + /* The pose needs updating, whether it's for restoring the original pose or for showing the + * result of the blend. */ + DEG_id_tag_update(&pbd->ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pbd->ob); + + if (pbd->state != POSE_BLEND_BLENDING) { + return; + } + + /* Perform the actual blending. */ + struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f); + BKE_pose_apply_action_blend(pbd->ob, pbd->act, &anim_eval_context, pbd->blend_factor); +} + +/* ---------------------------- */ + +static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor) +{ + pbd->blend_factor = CLAMPIS(new_factor, 0.0f, 1.0f); + pbd->needs_redraw = true; +} + +static void poselib_slide_mouse_update_blendfactor(PoseBlendData *pbd, const wmEvent *event) +{ + if (pbd->release_confirm_info.use_release_confirm) { + /* Release confirm calculates factor based on where the dragging was started from. */ + const float range = 300 * U.pixelsize; + const float new_factor = (event->x - pbd->release_confirm_info.drag_start_xy[0]) / range; + poselib_blend_set_factor(pbd, new_factor); + } + else { + const float new_factor = (event->x - pbd->area->v1->vec.x) / ((float)pbd->area->winx); + poselib_blend_set_factor(pbd, new_factor); + } +} + +/* Return operator return value. */ +static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event) +{ + PoseBlendData *pbd = op->customdata; + + if (event->type == MOUSEMOVE) { + poselib_slide_mouse_update_blendfactor(pbd, event); + return OPERATOR_RUNNING_MODAL; + } + + /* Handle the release confirm event directly, it has priority over others. */ + if (pbd->release_confirm_info.use_release_confirm && + (event->type == pbd->release_confirm_info.init_event_type) && (event->val == KM_RELEASE)) { + pbd->state = POSE_BLEND_CONFIRM; + return OPERATOR_RUNNING_MODAL; + } + + /* only accept 'press' event, and ignore 'release', so that we don't get double actions */ + if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) { + return OPERATOR_RUNNING_MODAL; + } + + /* NORMAL EVENT HANDLING... */ + /* searching takes priority over normal activity */ + switch (event->type) { + /* Exit - cancel. */ + case EVT_ESCKEY: + case RIGHTMOUSE: + pbd->state = POSE_BLEND_CANCEL; + break; + + /* Exit - confirm. */ + case LEFTMOUSE: + case EVT_RETKEY: + case EVT_PADENTER: + case EVT_SPACEKEY: + pbd->state = POSE_BLEND_CONFIRM; + break; + + /* TODO(Sybren): toggle between original pose and poselib pose. */ + case EVT_TABKEY: + pbd->state = pbd->state == POSE_BLEND_BLENDING ? POSE_BLEND_ORIGINAL : POSE_BLEND_BLENDING; + pbd->needs_redraw = true; + break; + + /* TODO(Sybren): use better UI for slider. */ + } + + return OPERATOR_RUNNING_MODAL; +} + +static void poselib_blend_cursor_update(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + + /* Ensure cursor-grab (continuous grabbing) is enabled when using release-confirm. */ + if (pbd->release_confirm_info.use_release_confirm && + !pbd->release_confirm_info.cursor_wrap_enabled) { + WM_cursor_grab_enable(CTX_wm_window(C), WM_CURSOR_WRAP_XY, true, NULL); + pbd->release_confirm_info.cursor_wrap_enabled = true; + } +} + +/* ---------------------------- */ + +static Object *get_poselib_object(bContext *C) +{ + if (C == NULL) { + return NULL; + } + return BKE_object_pose_armature_get(CTX_data_active_object(C)); +} + +static void poselib_tempload_exit(PoseBlendData *pbd) +{ + ED_asset_temp_id_consumer_free(&pbd->temp_id_consumer); +} + +static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op) +{ + bool asset_handle_valid; + const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); + /* Poll callback should check. */ + BLI_assert((asset_library != NULL) && asset_handle_valid); + + PoseBlendData *pbd = op->customdata; + + pbd->temp_id_consumer = ED_asset_temp_id_consumer_create(&asset_handle); + return (bAction *)ED_asset_temp_id_consumer_ensure_local_id( + pbd->temp_id_consumer, C, asset_library, ID_AC, CTX_data_main(C), op->reports); +} + +static bAction *flip_pose(bContext *C, Object *ob, bAction *action) +{ + bAction *action_copy = (bAction *)BKE_id_copy_ex(NULL, &action->id, NULL, LIB_ID_COPY_LOCALIZE); + + /* Lock the window manager while flipping the pose. Flipping requires temporarily modifying the + * pose, which can cause unwanted visual glitches. */ + wmWindowManager *wm = CTX_wm_manager(C); + const bool interface_was_locked = CTX_wm_interface_locked(C); + WM_set_locked_interface(wm, true); + + BKE_action_flip_with_pose(action_copy, ob); + + WM_set_locked_interface(wm, interface_was_locked); + return action_copy; +} + +/* Return true on success, false if the context isn't suitable. */ +static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *event) +{ + op->customdata = NULL; + + /* check if valid poselib */ + Object *ob = get_poselib_object(C); + if (ELEM(NULL, ob, ob->pose, ob->data)) { + BKE_report(op->reports, RPT_ERROR, TIP_("Pose lib is only for armatures in pose mode")); + return false; + } + + /* Set up blend state info. */ + PoseBlendData *pbd; + op->customdata = pbd = MEM_callocN(sizeof(PoseBlendData), "PoseLib Preview Data"); + + bAction *action = poselib_blend_init_get_action(C, op); + if (action == NULL) { + return false; + } + + /* Maybe flip the Action. */ + const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped"); + if (apply_flipped) { + action = flip_pose(C, ob, action); + pbd->free_action = true; + } + pbd->act = action; + + /* Get the basic data. */ + pbd->ob = ob; + pbd->ob->pose = ob->pose; + + pbd->scene = CTX_data_scene(C); + pbd->area = CTX_wm_area(C); + + pbd->state = POSE_BLEND_INIT; + pbd->needs_redraw = true; + pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor"); + /* Just to avoid a clang-analyzer warning (false positive), it's set properly below. */ + pbd->release_confirm_info.use_release_confirm = false; + + /* Release confirm data. Only available if there's an event to work with. */ + if (event != NULL) { + PropertyRNA *release_confirm_prop = RNA_struct_find_property(op->ptr, "release_confirm"); + pbd->release_confirm_info.use_release_confirm = (release_confirm_prop != NULL) && + RNA_property_boolean_get(op->ptr, + release_confirm_prop); + } + + if (pbd->release_confirm_info.use_release_confirm) { + BLI_assert(event != NULL); + pbd->release_confirm_info.drag_start_xy[0] = event->x; + pbd->release_confirm_info.drag_start_xy[1] = event->y; + pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type( + event->type); + } + + /* Make backups for blending and restoring the pose. */ + poselib_backup_posecopy(pbd); + + /* Set pose flags to ensure the depsgraph evaluation doesn't overwrite it. */ + pbd->ob->pose->flag &= ~POSE_DO_UNLOCK; + pbd->ob->pose->flag |= POSE_LOCKED; + + return true; +} + +static void poselib_blend_cleanup(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + wmWindow *win = CTX_wm_window(C); + + /* Redraw the header so that it doesn't show any of our stuff anymore. */ + ED_area_status_text(pbd->area, NULL); + ED_workspace_status_text(C, NULL); + + /* This signals the depsgraph to unlock and reevaluate the pose on the next evaluation. */ + bPose *pose = pbd->ob->pose; + pose->flag |= POSE_DO_UNLOCK; + + switch (pbd->state) { + case POSE_BLEND_CONFIRM: { + Scene *scene = pbd->scene; + poselib_keytag_pose(C, scene, pbd); + + /* Ensure the redo panel has the actually-used value, instead of the initial value. */ + RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor); + break; + } + + case POSE_BLEND_INIT: + case POSE_BLEND_BLENDING: + case POSE_BLEND_ORIGINAL: + /* Cleanup should not be called directly from these states. */ + BLI_assert(!"poselib_blend_cleanup: unexpected pose blend state"); + BKE_report(op->reports, RPT_ERROR, "Internal pose library error, cancelling operator"); + ATTR_FALLTHROUGH; + case POSE_BLEND_CANCEL: + ED_pose_backup_restore(pbd->pose_backup); + break; + } + + if (pbd->release_confirm_info.cursor_wrap_enabled) { + WM_cursor_grab_disable(win, pbd->release_confirm_info.drag_start_xy); + pbd->release_confirm_info.cursor_wrap_enabled = false; + } + + DEG_id_tag_update(&pbd->ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pbd->ob); + /* Update mouse-hover highlights. */ + WM_event_add_mousemove(win); +} + +static void poselib_blend_free(wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + if (pbd == NULL) { + return; + } + + if (pbd->free_action) { + /* Run before #poselib_tempload_exit to avoid any problems from indirectly + * referenced ID pointers. */ + BKE_id_free(NULL, pbd->act); + } + poselib_tempload_exit(pbd); + + /* Must have been dealt with before! */ + BLI_assert(pbd->release_confirm_info.cursor_wrap_enabled == false); + + /* Free temp data for operator */ + ED_pose_backup_free(pbd->pose_backup); + pbd->pose_backup = NULL; + + MEM_SAFE_FREE(op->customdata); +} + +static int poselib_blend_exit(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + const ePoseBlendState exit_state = pbd->state; + + poselib_blend_cleanup(C, op); + poselib_blend_free(op); + + if (exit_state == POSE_BLEND_CANCEL) { + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; +} + +/* Cancel previewing operation (called when exiting Blender) */ +static void poselib_blend_cancel(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + pbd->state = POSE_BLEND_CANCEL; + poselib_blend_exit(C, op); +} + +/* Main modal status check. */ +static int poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + const int operator_result = poselib_blend_handle_event(C, op, event); + + poselib_blend_cursor_update(C, op); + + const PoseBlendData *pbd = op->customdata; + if (ELEM(pbd->state, POSE_BLEND_CONFIRM, POSE_BLEND_CANCEL)) { + return poselib_blend_exit(C, op); + } + + if (pbd->needs_redraw) { + poselib_blend_apply(C, op); + } + + return operator_result; +} + +/* Modal Operator init. */ +static int poselib_blend_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!poselib_blend_init_data(C, op, event)) { + poselib_blend_free(op); + return OPERATOR_CANCELLED; + } + + /* Do initial apply to have something to look at. */ + poselib_blend_apply(C, op); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +/* Single-shot apply. */ +static int poselib_blend_exec(bContext *C, wmOperator *op) +{ + if (!poselib_blend_init_data(C, op, NULL)) { + poselib_blend_free(op); + return OPERATOR_CANCELLED; + } + + poselib_blend_apply(C, op); + + PoseBlendData *pbd = op->customdata; + pbd->state = POSE_BLEND_CONFIRM; + return poselib_blend_exit(C, op); +} + +static bool poselib_asset_in_context(bContext *C) +{ + bool asset_handle_valid; + /* Check whether the context provides the asset data needed to add a pose. */ + const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); + + return (asset_library != NULL) && asset_handle_valid && + (asset_handle.file_data->blentype == ID_AC); +} + +/* Poll callback for operators that require existing PoseLib data (with poses) to work. */ +static bool poselib_blend_poll(bContext *C) +{ + Object *ob = get_poselib_object(C); + if (ELEM(NULL, ob, ob->pose, ob->data)) { + /* Pose lib is only for armatures in pose mode. */ + return false; + } + + return poselib_asset_in_context(C); +} + +void POSELIB_OT_apply_pose_asset(wmOperatorType *ot) +{ + /* Identifiers: */ + ot->name = "Apply Pose Library Pose"; + ot->idname = "POSELIB_OT_apply_pose_asset"; + ot->description = "Apply the given Pose Action to the rig"; + + /* Callbacks: */ + ot->exec = poselib_blend_exec; + ot->poll = poselib_blend_poll; + + /* Flags: */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties: */ + RNA_def_float_factor(ot->srna, + "blend_factor", + 1.0f, + 0.0f, + 1.0f, + "Blend Factor", + "Amount that the pose is applied on top of the existing poses", + 0.0f, + 1.0f); + RNA_def_boolean(ot->srna, + "flipped", + false, + "Apply Flipped", + "When enabled, applies the pose flipped over the X-axis"); +} + +void POSELIB_OT_blend_pose_asset(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* Identifiers: */ + ot->name = "Blend Pose Library Pose"; + ot->idname = "POSELIB_OT_blend_pose_asset"; + ot->description = "Blend the given Pose Action to the rig"; + + /* Callbacks: */ + ot->invoke = poselib_blend_invoke; + ot->modal = poselib_blend_modal; + ot->cancel = poselib_blend_cancel; + ot->exec = poselib_blend_exec; + ot->poll = poselib_blend_poll; + + /* Flags: */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* Properties: */ + prop = RNA_def_float_factor(ot->srna, + "blend_factor", + 0.0f, + 0.0f, + 1.0f, + "Blend Factor", + "Amount that the pose is applied on top of the existing poses", + 0.0f, + 1.0f); + /* Blending should always start at 0%, and not at whatever percentage was last used. This RNA + * property just exists for symmetry with the Apply operator (and thus simplicity of the rest of + * the code, which can assume this property exists). */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + RNA_def_boolean(ot->srna, + "flipped", + false, + "Apply Flipped", + "When enabled, applies the pose flipped over the X-axis"); + prop = RNA_def_boolean(ot->srna, + "release_confirm", + false, + "Confirm on Release", + "Always confirm operation when releasing button"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 85563b76f38..eaa54f66928 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -23,10 +23,15 @@ #pragma once +#include + +#include "BLI_listbase.h" + #ifdef __cplusplus extern "C" { #endif +struct bAction; struct Base; struct Bone; struct Depsgraph; @@ -242,6 +247,17 @@ void ED_mesh_deform_bind_callback(struct MeshDeformModifierData *mmd, int totvert, float cagemat[4][4]); +/* Pose backups, pose_backup.c */ +struct PoseBackup; +/* Create a backup of those bones that are animated in the given action. */ +struct PoseBackup *ED_pose_backup_create_selected_bones( + const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT; +struct PoseBackup *ED_pose_backup_create_all_bones( + const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT; +bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup); +void ED_pose_backup_restore(const struct PoseBackup *pose_backup); +void ED_pose_backup_free(struct PoseBackup *pose_backup); + #ifdef __cplusplus } #endif -- cgit v1.2.3 From 6e01b52100f3f9674c49127cbcc445e7e5c987d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 12 Jul 2021 13:23:59 +0200 Subject: Assets: temporarily apply pose when generating preview image When generating a preview image for a pose, temporarily apply it to the armature. Contrary to the usual pose application, this ignores the selected bones and always applies the entire pose. --- source/blender/editors/render/render_preview.c | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 7f7ee45a803..9abf15d2198 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -54,7 +54,9 @@ #include "DNA_space_types.h" #include "DNA_world_types.h" +#include "BKE_animsys.h" #include "BKE_appdir.h" +#include "BKE_armature.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -93,6 +95,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_armature.h" #include "ED_datafiles.h" #include "ED_render.h" #include "ED_screen.h" @@ -151,6 +154,10 @@ typedef struct IconPreview { void *owner; ID *id, *id_copy; /* May be NULL! (see ICON_TYPE_PREVIEW case in #ui_icon_ensure_deferred()) */ ListBase sizes; + + /* May be NULL, is used for rendering IDs that require some other object for it to be applied on + * before the ID can be represented as an image, for example when rendering an Action. */ + struct Object *active_object; } IconPreview; /** \} */ @@ -813,6 +820,47 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview /** \name Action Preview * \{ */ +static struct PoseBackup *action_preview_render_prepare(IconPreview *preview) +{ + Object *object = preview->active_object; + if (object == NULL) { + WM_report(RPT_WARNING, "No active object, unable to apply the Action before rendering"); + return NULL; + } + if (object->pose == NULL) { + WM_reportf(RPT_WARNING, + "Object %s has no pose, unable to apply the Action before rendering", + object->id.name + 2); + return NULL; + } + + /* Create a backup of the current pose. */ + struct bAction *action = (struct bAction *)preview->id; + struct PoseBackup *pose_backup = ED_pose_backup_create_all_bones(object, action); + + /* Apply the Action as pose, so that it can be rendered. This assumes the Action represents a + * single pose, and that thus the evaluation time doesn't matter. */ + AnimationEvalContext anim_eval_context = {preview->depsgraph, 0.0f}; + BKE_pose_apply_action_all_bones(object, action, &anim_eval_context); + + /* Force evaluation of the new pose, before the preview is rendered. */ + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + DEG_evaluate_on_refresh(preview->depsgraph); + + return pose_backup; +} + +static void action_preview_render_cleanup(IconPreview *preview, struct PoseBackup *pose_backup) +{ + if (pose_backup == NULL) { + return; + } + ED_pose_backup_restore(pose_backup); + ED_pose_backup_free(pose_backup); + + DEG_id_tag_update(&preview->active_object->id, ID_RECALC_GEOMETRY); +} + /* Render a pose. It is assumed that the pose has already been applied and that the scene camera is * capturing the pose. In other words, this function just renders from the scene camera without * evaluating the Action stored in preview->id. */ @@ -827,6 +875,9 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview BLI_assert(depsgraph != NULL); BLI_assert(preview->scene == DEG_get_input_scene(depsgraph)); + /* Apply the pose before getting the evaluated scene, so that the new pose is evaluated. */ + struct PoseBackup *pose_backup = action_preview_render_prepare(preview); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *camera_eval = scene_eval->camera; if (camera_eval == NULL) { @@ -850,6 +901,8 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview NULL, err_out); + action_preview_render_cleanup(preview, pose_backup); + if (err_out[0] != '\0') { printf("Error rendering Action %s preview: %s\n", preview->id->name + 2, err_out); } @@ -1625,6 +1678,7 @@ void ED_preview_icon_render( /* Control isn't given back to the caller until the preview is done. So we don't need to copy * the ID to avoid thread races. */ ip.id_copy = duplicate_ids(id, true); + ip.active_object = CTX_data_active_object(C); icon_preview_add_size(&ip, rect, sizex, sizey); @@ -1666,6 +1720,7 @@ void ED_preview_icon_job( ip->bmain = CTX_data_main(C); ip->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ip->scene = DEG_get_input_scene(ip->depsgraph); + ip->active_object = CTX_data_active_object(C); ip->owner = owner; ip->id = id; ip->id_copy = duplicate_ids(id, false); -- cgit v1.2.3 From 788d38046032736531cb77507e397cbea7993549 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 14 Jul 2021 16:01:53 +0200 Subject: UI: UI list refactor & preparations for asset view template This is more of a first-pass refactor for the UI list template. More improvements could be done, but that's better done separately. Main purpose of this is to make the UI list code more manageable and ready for the asset view template. No functional changes for users. * Split the huge template function into more manageable functions, with clear names and a few structs with high coherency. * Move runtime data management to the template code, with a free callback called from BKE. This is UI data and should be managed at that level. * Replace boolean arguments with bit-flags (easily extendable and more readable from the caller). * Allow passing custom-data to the UI list for callbacks to access. * Make list grip button for resizing optional. * Put logic for generating the internal UI list identifier (stored in .blends) into function. This is a quite important bit and a later commit adds a related function. Good to have a clear API for this. * Improve naming, comments, etc. As part of further cleanups I'd like to move this to an own file. --- source/blender/editors/include/UI_interface.h | 31 +- .../editors/interface/interface_templates.c | 843 +++++++++++++-------- source/blender/editors/space_node/drawnode.cc | 6 +- 3 files changed, 568 insertions(+), 312 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 802c175492f..55d0c5c4342 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -25,6 +25,7 @@ #include "BLI_compiler_attrs.h" #include "BLI_sys_types.h" /* size_t */ +#include "BLI_utildefines.h" #include "UI_interface_icons.h" #ifdef __cplusplus @@ -2194,6 +2195,17 @@ void uiTemplateCacheFile(uiLayout *layout, /* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */ #define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list" +enum uiTemplateListFlags { + UI_TEMPLATE_LIST_FLAG_NONE = 0, + UI_TEMPLATE_LIST_SORT_REVERSE = (1 << 0), + UI_TEMPLATE_LIST_SORT_LOCK = (1 << 1), + /* Don't allow resizing the list, i.e. don't add the grip button. */ + UI_TEMPLATE_LIST_NO_GRIP = (1 << 2), + + UI_TEMPLATE_LIST_FLAGS_LAST +}; +ENUM_OPERATORS(enum uiTemplateListFlags, UI_TEMPLATE_LIST_FLAGS_LAST); + void uiTemplateList(uiLayout *layout, struct bContext *C, const char *listtype_name, @@ -2207,8 +2219,23 @@ void uiTemplateList(uiLayout *layout, int maxrows, int layout_type, int columns, - bool sort_reverse, - bool sort_lock); + enum uiTemplateListFlags flags); +struct uiList *uiTemplateList_ex(uiLayout *layout, + struct bContext *C, + const char *listtype_name, + const char *list_id, + struct PointerRNA *dataptr, + const char *propname, + struct PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags, + void *customdata); + void uiTemplateNodeLink(uiLayout *layout, struct bContext *C, struct bNodeTree *ntree, diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 9c17486aea4..30f72e75979 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -5828,36 +5828,283 @@ static void uilist_filter_items_default(struct uiList *ui_list, } } +static void uilist_free_dyn_data(uiList *ui_list) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + if (!dyn_data) { + return; + } + + MEM_SAFE_FREE(dyn_data->items_filter_flags); + MEM_SAFE_FREE(dyn_data->items_filter_neworder); + MEM_SAFE_FREE(dyn_data->customdata); +} + +/** + * The validated data that was passed to #uiTemplateList (typically through Python). + * Populated through #ui_template_list_data_retrieve(). + */ +typedef struct { + PointerRNA dataptr; + PropertyRNA *prop; + PointerRNA active_dataptr; + PropertyRNA *activeprop; + const char *item_dyntip_propname; + + /* Index as stored in the input property. I.e. the index before sorting. */ + int active_item_idx; +} TemplateListInputData; + +/** + * Internal wrapper for a single item in the list (well, actually stored as a vector). + */ typedef struct { PointerRNA item; int org_idx; int flt_flag; } _uilist_item; +/** + * Container for the item vector and additional info. + */ +typedef struct { + _uilist_item *item_vec; + /* Index of the active item following visual order. I.e. unlike + * TemplateListInputData.active_item_idx, this is the index after sorting. */ + int active_item_idx; + int tot_items; +} TemplateListItems; + +typedef struct { + uiListDrawItemFunc draw_item; + uiListDrawFilterFunc draw_filter; + + int rows; + int maxrows; + int columns; +} TemplateListLayoutDrawData; + typedef struct { int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ int start_idx; /* Index of first item to display. */ int end_idx; /* Index of last item to display + 1. */ -} uiListLayoutdata; +} TemplateListVisualInfo; + +/** + * Validate input parameters and initialize \a r_data from that. Plus find the list-type and return + * it in \a r_list_type. + * + * \return false if the input data isn't valid. Will also raise an RNA warning in that case. + */ +static bool ui_template_list_data_retrieve(const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + TemplateListInputData *r_input_data, + uiListType **r_list_type) +{ + memset(r_input_data, 0, sizeof(*r_input_data)); + + /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ + if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { + RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", + UI_UL_DEFAULT_CLASS_NAME); + return false; + } + + if (!active_dataptr->data) { + RNA_warning("No active data"); + return false; + } + + r_input_data->dataptr = *dataptr; + if (dataptr->data) { + r_input_data->prop = RNA_struct_find_property(dataptr, propname); + if (!r_input_data->prop) { + RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname); + return false; + } + } + + r_input_data->active_dataptr = *active_dataptr; + r_input_data->activeprop = RNA_struct_find_property(active_dataptr, active_propname); + if (!r_input_data->activeprop) { + RNA_warning( + "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname); + return false; + } + + if (r_input_data->prop) { + const PropertyType type = RNA_property_type(r_input_data->prop); + if (type != PROP_COLLECTION) { + RNA_warning("Expected a collection data property"); + return false; + } + } + + const PropertyType activetype = RNA_property_type(r_input_data->activeprop); + if (activetype != PROP_INT) { + RNA_warning("Expected an integer active data property"); + return false; + } + + /* Find the uiList type. */ + if (!(*r_list_type = WM_uilisttype_find(listtype_name, false))) { + RNA_warning("List type %s not found", listtype_name); + return false; + } + + r_input_data->active_item_idx = RNA_property_int_get(&r_input_data->active_dataptr, + r_input_data->activeprop); + r_input_data->item_dyntip_propname = item_dyntip_propname; + + return true; +} + +static void ui_template_list_collect_items(PointerRNA *list_ptr, + PropertyRNA *list_prop, + uiListDyn *dyn_data, + int filter_exclude, + bool order_reverse, + int activei, + TemplateListItems *r_items) +{ + int i = 0; + int reorder_i = 0; + bool activei_mapping_pending = true; + + RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) { + if (!dyn_data->items_filter_flags || + ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { + int new_order_idx; + if (dyn_data->items_filter_neworder) { + new_order_idx = dyn_data->items_filter_neworder[reorder_i++]; + new_order_idx = order_reverse ? dyn_data->items_shown - new_order_idx - 1 : new_order_idx; + } + else { + new_order_idx = order_reverse ? dyn_data->items_shown - ++reorder_i : reorder_i++; + } + // printf("%s: ii: %d\n", __func__, ii); + r_items->item_vec[new_order_idx].item = itemptr; + r_items->item_vec[new_order_idx].org_idx = i; + r_items->item_vec[new_order_idx].flt_flag = dyn_data->items_filter_flags ? + dyn_data->items_filter_flags[i] : + 0; + + if (activei_mapping_pending && activei == i) { + activei = new_order_idx; + /* So that we do not map again activei! */ + activei_mapping_pending = false; + } +#if 0 /* For now, do not alter active element, even if it will be hidden... */ + else if (activei < i) { + /* We do not want an active but invisible item! + * Only exception is when all items are filtered out... + */ + if (prev_order_idx >= 0) { + activei = prev_order_idx; + RNA_property_int_set(active_dataptr, activeprop, prev_i); + } + else { + activei = new_order_idx; + RNA_property_int_set(active_dataptr, activeprop, i); + } + } + prev_i = i; + prev_ii = new_order_idx; +#endif + } + i++; + } + RNA_PROP_END; + + /* If mapping is still pending, no active item was found. Mark as invalid (-1) */ + r_items->active_item_idx = activei_mapping_pending ? -1 : activei; +} + +/** + * Create the UI-list representation of the list items, sorted and filtered if needed. + */ +static void ui_template_list_collect_display_items(bContext *C, + uiList *ui_list, + TemplateListInputData *input_data, + const uiListFilterItemsFunc filter_items_fn, + TemplateListItems *r_items) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + memset(r_items, 0, sizeof(*r_items)); + + /* Filter list items! (not for compact layout, though) */ + if (input_data->dataptr.data && input_data->prop) { + const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; + int items_shown; +#if 0 + int prev_ii = -1, prev_i; +#endif + + if (ui_list->layout_type == UILST_LAYOUT_COMPACT) { + dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length( + &input_data->dataptr, input_data->prop); + } + else { + // printf("%s: filtering...\n", __func__); + filter_items_fn(ui_list, C, &input_data->dataptr, RNA_property_identifier(input_data->prop)); + // printf("%s: filtering done.\n", __func__); + } + + items_shown = dyn_data->items_shown; + if (items_shown >= 0) { + r_items->item_vec = MEM_mallocN(sizeof(*r_items->item_vec) * items_shown, __func__); + // printf("%s: items shown: %d.\n", __func__, items_shown); + + ui_template_list_collect_items(&input_data->dataptr, + input_data->prop, + dyn_data, + filter_exclude, + order_reverse, + input_data->active_item_idx, + r_items); + } + if (dyn_data->items_shown >= 0) { + r_items->tot_items = dyn_data->items_shown; + } + else { + r_items->tot_items = dyn_data->items_len; + } + } +} + +static void ui_template_list_free_items(TemplateListItems *items) +{ + if (items->item_vec) { + MEM_freeN(items->item_vec); + } +} static void uilist_prepare(uiList *ui_list, - int len, - int activei, - int rows, - int maxrows, - int columns, - uiListLayoutdata *layoutdata) + const TemplateListItems *items, + const TemplateListLayoutDrawData *layout_data, + TemplateListVisualInfo *r_visual_info) { uiListDyn *dyn_data = ui_list->dyn_data; - const bool use_auto_size = (ui_list->list_grip < (rows - UI_LIST_AUTO_SIZE_THRESHOLD)); + const bool use_auto_size = (ui_list->list_grip < + (layout_data->rows - UI_LIST_AUTO_SIZE_THRESHOLD)); + + int actual_rows = layout_data->rows; + int actual_maxrows = layout_data->maxrows; + int columns = layout_data->columns; /* default rows */ - if (rows <= 0) { - rows = 5; + if (actual_rows <= 0) { + actual_rows = 5; } - dyn_data->visual_height_min = rows; - if (maxrows < rows) { - maxrows = max_ii(rows, 5); + dyn_data->visual_height_min = actual_rows; + if (actual_maxrows < actual_rows) { + actual_maxrows = max_ii(actual_rows, 5); } if (columns <= 0) { columns = 9; @@ -5865,42 +6112,46 @@ static void uilist_prepare(uiList *ui_list, int activei_row; if (columns > 1) { - dyn_data->height = (int)ceil((double)len / (double)columns); - activei_row = (int)floor((double)activei / (double)columns); + dyn_data->height = (int)ceil((double)items->tot_items / (double)columns); + activei_row = (int)floor((double)items->active_item_idx / (double)columns); } else { - dyn_data->height = len; - activei_row = activei; + dyn_data->height = items->tot_items; + activei_row = items->active_item_idx; } + dyn_data->columns = columns; + if (!use_auto_size) { /* No auto-size, yet we clamp at min size! */ - maxrows = rows = max_ii(ui_list->list_grip, rows); + actual_rows = max_ii(ui_list->list_grip, actual_rows); } - else if ((rows != maxrows) && (dyn_data->height > rows)) { + else if ((actual_rows != actual_maxrows) && (dyn_data->height > actual_rows)) { /* Expand size if needed and possible. */ - rows = min_ii(dyn_data->height, maxrows); + actual_rows = min_ii(dyn_data->height, actual_maxrows); } /* If list length changes or list is tagged to check this, * and active is out of view, scroll to it. */ - if (ui_list->list_last_len != len || ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM) { + if ((ui_list->list_last_len != items->tot_items) || + (ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM)) { if (activei_row < ui_list->list_scroll) { ui_list->list_scroll = activei_row; } - else if (activei_row >= ui_list->list_scroll + rows) { - ui_list->list_scroll = activei_row - rows + 1; + else if (activei_row >= ui_list->list_scroll + actual_rows) { + ui_list->list_scroll = activei_row - actual_rows + 1; } ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM; } - const int max_scroll = max_ii(0, dyn_data->height - rows); + const int max_scroll = max_ii(0, dyn_data->height - actual_rows); CLAMP(ui_list->list_scroll, 0, max_scroll); - ui_list->list_last_len = len; - dyn_data->visual_height = rows; - layoutdata->visual_items = rows * columns; - layoutdata->start_idx = ui_list->list_scroll * columns; - layoutdata->end_idx = min_ii(layoutdata->start_idx + rows * columns, len); + ui_list->list_last_len = items->tot_items; + dyn_data->visual_height = actual_rows; + r_visual_info->visual_items = actual_rows * columns; + r_visual_info->start_idx = ui_list->list_scroll * columns; + r_visual_info->end_idx = min_ii(r_visual_info->start_idx + actual_rows * columns, + items->tot_items); } static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2)) @@ -5940,117 +6191,32 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha return BLI_sprintfN("%s - %s", tip, dyn_tooltip); } -void uiTemplateList(uiLayout *layout, - bContext *C, - const char *listtype_name, - const char *list_id, - PointerRNA *dataptr, - const char *propname, - PointerRNA *active_dataptr, - const char *active_propname, - const char *item_dyntip_propname, - int rows, - int maxrows, - int layout_type, - int columns, - bool sort_reverse, - bool sort_lock) +/** + * \note Note that \a layout_type may be NULL. + */ +static uiList *ui_list_ensure(bContext *C, + uiListType *ui_list_type, + const char *list_id, + int layout_type, + bool sort_reverse, + bool sort_lock) { - PropertyRNA *prop = NULL, *activeprop; - _uilist_item *items_ptr = NULL; - uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; - uiBut *but; - - uiListLayoutdata layoutdata; - char ui_list_id[UI_MAX_NAME_STR]; - char numstr[32]; - int rnaicon = ICON_NONE, icon = ICON_NONE; - int i = 0, activei = 0; - int len = 0; - - /* validate arguments */ - /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ - if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { - RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", - UI_UL_DEFAULT_CLASS_NAME); - return; - } - - uiBlock *block = uiLayoutGetBlock(layout); - - if (!active_dataptr->data) { - RNA_warning("No active data"); - return; - } - - if (dataptr->data) { - prop = RNA_struct_find_property(dataptr, propname); - if (!prop) { - RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname); - return; - } - } - - activeprop = RNA_struct_find_property(active_dataptr, active_propname); - if (!activeprop) { - RNA_warning( - "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname); - return; - } - - if (prop) { - const PropertyType type = RNA_property_type(prop); - if (type != PROP_COLLECTION) { - RNA_warning("Expected a collection data property"); - return; - } - } - - const PropertyType activetype = RNA_property_type(activeprop); - if (activetype != PROP_INT) { - RNA_warning("Expected an integer active data property"); - return; - } - - /* get icon */ - if (dataptr->data && prop) { - StructRNA *ptype = RNA_property_pointer_type(dataptr, prop); - rnaicon = RNA_struct_ui_icon(ptype); - } - - /* get active data */ - activei = RNA_property_int_get(active_dataptr, activeprop); - - /* Find the uiList type. */ - uiListType *ui_list_type = WM_uilisttype_find(listtype_name, false); - - if (ui_list_type == NULL) { - RNA_warning("List type %s not found", listtype_name); - return; - } - - uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : - uilist_draw_item_default; - uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : - uilist_draw_filter_default; - uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : - uilist_filter_items_default; - - /* Find or add the uiList to the current Region. */ - /* We tag the list id with the list type... */ - BLI_snprintf( - ui_list_id, sizeof(ui_list_id), "%s_%s", ui_list_type->idname, list_id ? list_id : ""); - /* Allows to work in popups. */ ARegion *region = CTX_wm_menu(C); if (region == NULL) { region = CTX_wm_region(C); } - uiList *ui_list = BLI_findstring(®ion->ui_lists, ui_list_id, offsetof(uiList, list_id)); + + /* Find or add the uiList to the current Region. */ + + char full_list_id[UI_MAX_NAME_STR]; + WM_uilisttype_to_full_list_id(ui_list_type, list_id, full_list_id); + + uiList *ui_list = BLI_findstring(®ion->ui_lists, full_list_id, offsetof(uiList, list_id)); if (!ui_list) { ui_list = MEM_callocN(sizeof(uiList), "uiList"); - BLI_strncpy(ui_list->list_id, ui_list_id, sizeof(ui_list->list_id)); + BLI_strncpy(ui_list->list_id, full_list_id, sizeof(ui_list->list_id)); BLI_addtail(®ion->ui_lists, ui_list); ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ if (sort_reverse) { @@ -6065,6 +6231,9 @@ void uiTemplateList(uiLayout *layout, ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data"); } uiListDyn *dyn_data = ui_list->dyn_data; + /* Note that this isn't a `uiListType` callback, it's stored in the runtime list data. Otherwise + * the runtime data could leak when the type is unregistered (e.g. on "Reload Scripts"). */ + dyn_data->free_runtime_data_fn = uilist_free_dyn_data; /* Because we can't actually pass type across save&load... */ ui_list->type = ui_list_type; @@ -6075,110 +6244,55 @@ void uiTemplateList(uiLayout *layout, MEM_SAFE_FREE(dyn_data->items_filter_neworder); dyn_data->items_len = dyn_data->items_shown = -1; - /* When active item changed since last draw, scroll to it. */ - if (activei != ui_list->list_last_activei) { - ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; - ui_list->list_last_activei = activei; - } + return ui_list; +} - /* Filter list items! (not for compact layout, though) */ - if (dataptr->data && prop) { - const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; - const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; - int items_shown, idx = 0; -#if 0 - int prev_ii = -1, prev_i; -#endif +static void ui_template_list_layout_draw(bContext *C, + uiList *ui_list, + uiLayout *layout, + TemplateListInputData *input_data, + TemplateListItems *items, + const TemplateListLayoutDrawData *layout_data, + const enum uiTemplateListFlags flags) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + const char *active_propname = RNA_property_identifier(input_data->activeprop); - if (layout_type == UILST_LAYOUT_COMPACT) { - dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop); - } - else { - // printf("%s: filtering...\n", __func__); - filter_items(ui_list, C, dataptr, propname); - // printf("%s: filtering done.\n", __func__); - } + uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; + char numstr[32]; + int rnaicon = ICON_NONE, icon = ICON_NONE; + uiBut *but; - items_shown = dyn_data->items_shown; - if (items_shown >= 0) { - bool activei_mapping_pending = true; - items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, __func__); - // printf("%s: items shown: %d.\n", __func__, items_shown); - RNA_PROP_BEGIN (dataptr, itemptr, prop) { - if (!dyn_data->items_filter_flags || - ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { - int ii; - if (dyn_data->items_filter_neworder) { - ii = dyn_data->items_filter_neworder[idx++]; - ii = order_reverse ? items_shown - ii - 1 : ii; - } - else { - ii = order_reverse ? items_shown - ++idx : idx++; - } - // printf("%s: ii: %d\n", __func__, ii); - items_ptr[ii].item = itemptr; - items_ptr[ii].org_idx = i; - items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] : - 0; - - if (activei_mapping_pending && activei == i) { - activei = ii; - /* So that we do not map again activei! */ - activei_mapping_pending = false; - } -#if 0 /* For now, do not alter active element, even if it will be hidden... */ - else if (activei < i) { - /* We do not want an active but invisible item! - * Only exception is when all items are filtered out... - */ - if (prev_ii >= 0) { - activei = prev_ii; - RNA_property_int_set(active_dataptr, activeprop, prev_i); - } - else { - activei = ii; - RNA_property_int_set(active_dataptr, activeprop, i); - } - } - prev_i = i; - prev_ii = ii; -#endif - } - i++; - } - RNA_PROP_END; + uiBlock *block = uiLayoutGetBlock(layout); - if (activei_mapping_pending) { - /* No active item found, set to 'invalid' -1 value... */ - activei = -1; - } - } - if (dyn_data->items_shown >= 0) { - len = dyn_data->items_shown; - } - else { - len = dyn_data->items_len; - } + /* get icon */ + if (input_data->dataptr.data && input_data->prop) { + StructRNA *ptype = RNA_property_pointer_type(&input_data->dataptr, input_data->prop); + rnaicon = RNA_struct_ui_icon(ptype); } - switch (layout_type) { - case UILST_LAYOUT_DEFAULT: + TemplateListVisualInfo visual_info; + switch (ui_list->layout_type) { + case UILST_LAYOUT_DEFAULT: { /* layout */ - box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop); + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); glob = uiLayoutColumn(box, true); row = uiLayoutRow(glob, false); col = uiLayoutColumn(row, true); + TemplateListLayoutDrawData adjusted_layout_data = *layout_data; + adjusted_layout_data.columns = 1; /* init numbers */ - uilist_prepare(ui_list, len, activei, rows, maxrows, 1, &layoutdata); + uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); - if (dataptr->data && prop) { + int i = 0; + if (input_data->dataptr.data && input_data->prop) { /* create list items */ - for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { - PointerRNA *itemptr = &items_ptr[i].item; + for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; void *dyntip_data; - const int org_i = items_ptr[i].org_idx; - const int flt_flag = items_ptr[i].flt_flag; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; uiBlock *subblock = uiLayoutGetBlock(col); overlap = uiLayoutOverlap(col); @@ -6186,7 +6300,7 @@ void uiTemplateList(uiLayout *layout, UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); /* list item behind label & other buttons */ - sub = uiLayoutRow(overlap, false); + uiLayoutRow(overlap, false); but = uiDefButR_prop(subblock, UI_BTYPE_LISTROW, @@ -6196,15 +6310,16 @@ void uiTemplateList(uiLayout *layout, 0, UI_UNIT_X * 10, UI_UNIT_Y, - active_dataptr, - activeprop, + &input_data->active_dataptr, + input_data->activeprop, 0, 0, org_i, 0, 0, TIP_("Double click to rename")); - if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, item_dyntip_propname))) { + if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, + input_data->item_dyntip_propname))) { UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN); } @@ -6214,23 +6329,23 @@ void uiTemplateList(uiLayout *layout, if (icon == ICON_DOT) { icon = ICON_NONE; } - draw_item(ui_list, - C, - sub, - dataptr, - itemptr, - icon, - active_dataptr, - active_propname, - org_i, - flt_flag); + layout_data->draw_item(ui_list, + C, + sub, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); /* Items should be able to set context pointers for the layout. But the list-row button * swallows events, so it needs the context storage too for handlers to see it. */ but->context = uiLayoutGetContextStore(sub); /* If we are "drawing" active item, set all labels as active. */ - if (i == activei) { + if (i == items->active_item_idx) { ui_layout_list_set_labels_active(sub); } @@ -6239,13 +6354,13 @@ void uiTemplateList(uiLayout *layout, } /* add dummy buttons to fill space */ - for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) { + for (; i < visual_info.start_idx + visual_info.visual_items; i++) { uiItemL(col, "", ICON_NONE); } /* add scrollbar */ - if (len > layoutdata.visual_items) { - col = uiLayoutColumn(row, false); + if (items->tot_items > visual_info.visual_items) { + uiLayoutColumn(row, false); uiDefButI(block, UI_BTYPE_SCROLL, 0, @@ -6261,21 +6376,29 @@ void uiTemplateList(uiLayout *layout, 0, ""); } - break; + } break; case UILST_LAYOUT_COMPACT: row = uiLayoutRow(layout, true); - if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) && - (activei < dyn_data->items_shown)) { - PointerRNA *itemptr = &items_ptr[activei].item; - const int org_i = items_ptr[activei].org_idx; + if ((input_data->dataptr.data && input_data->prop) && (dyn_data->items_shown > 0) && + (items->active_item_idx >= 0) && (items->active_item_idx < dyn_data->items_shown)) { + PointerRNA *itemptr = &items->item_vec[items->active_item_idx].item; + const int org_i = items->item_vec[items->active_item_idx].org_idx; icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); if (icon == ICON_DOT) { icon = ICON_NONE; } - draw_item( - ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0); + layout_data->draw_item(ui_list, + C, + row, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + 0); } /* if list is empty, add in dummy button */ else { @@ -6293,8 +6416,8 @@ void uiTemplateList(uiLayout *layout, 0, UI_UNIT_X * 5, UI_UNIT_Y, - active_dataptr, - activeprop, + &input_data->active_dataptr, + input_data->activeprop, 0, 0, 0, @@ -6305,24 +6428,25 @@ void uiTemplateList(uiLayout *layout, UI_but_flag_enable(but, UI_BUT_DISABLED); } break; - case UILST_LAYOUT_GRID: - box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop); + case UILST_LAYOUT_GRID: { + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); glob = uiLayoutColumn(box, true); row = uiLayoutRow(glob, false); col = uiLayoutColumn(row, true); subrow = NULL; /* Quite gcc warning! */ - uilist_prepare(ui_list, len, activei, rows, maxrows, columns, &layoutdata); + uilist_prepare(ui_list, items, layout_data, &visual_info); - if (dataptr->data && prop) { + int i = 0; + if (input_data->dataptr.data && input_data->prop) { /* create list items */ - for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { - PointerRNA *itemptr = &items_ptr[i].item; - const int org_i = items_ptr[i].org_idx; - const int flt_flag = items_ptr[i].flt_flag; + for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; /* create button */ - if (!(i % columns)) { + if (!(i % layout_data->columns)) { subrow = uiLayoutRow(col, false); } @@ -6332,7 +6456,7 @@ void uiTemplateList(uiLayout *layout, UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); /* list item behind label & other buttons */ - sub = uiLayoutRow(overlap, false); + uiLayoutRow(overlap, false); but = uiDefButR_prop(subblock, UI_BTYPE_LISTROW, @@ -6342,8 +6466,8 @@ void uiTemplateList(uiLayout *layout, 0, UI_UNIT_X * 10, UI_UNIT_Y, - active_dataptr, - activeprop, + &input_data->active_dataptr, + input_data->activeprop, 0, 0, org_i, @@ -6355,19 +6479,19 @@ void uiTemplateList(uiLayout *layout, sub = uiLayoutRow(overlap, false); icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - draw_item(ui_list, - C, - sub, - dataptr, - itemptr, - icon, - active_dataptr, - active_propname, - org_i, - flt_flag); + layout_data->draw_item(ui_list, + C, + sub, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); /* If we are "drawing" active item, set all labels as active. */ - if (i == activei) { + if (i == items->active_item_idx) { ui_layout_list_set_labels_active(sub); } @@ -6376,15 +6500,15 @@ void uiTemplateList(uiLayout *layout, } /* add dummy buttons to fill space */ - for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) { - if (!(i % columns)) { + for (; i < visual_info.start_idx + visual_info.visual_items; i++) { + if (!(i % layout_data->columns)) { subrow = uiLayoutRow(col, false); } uiItemL(subrow, "", ICON_NONE); } /* add scrollbar */ - if (len > layoutdata.visual_items) { + if (items->tot_items > visual_info.visual_items) { /* col = */ uiLayoutColumn(row, false); uiDefButI(block, UI_BTYPE_SCROLL, @@ -6402,9 +6526,12 @@ void uiTemplateList(uiLayout *layout, ""); } break; + } } if (glob) { + const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0; + /* About #UI_BTYPE_GRIP drag-resize: * We can't directly use results from a grip button, since we have a * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature). @@ -6443,21 +6570,23 @@ void uiTemplateList(uiLayout *layout, TIP_("Hide filtering options")); UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ - but = uiDefIconButI(subblock, - UI_BTYPE_GRIP, - 0, - ICON_GRIP, - 0, - 0, - UI_UNIT_X * 10.0f, - UI_UNIT_Y * 0.5f, - &dyn_data->resize, - 0.0, - 0.0, - 0, - 0, - ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + if (add_grip_but) { + but = uiDefIconButI(subblock, + UI_BTYPE_GRIP, + 0, + ICON_GRIP, + 0, + 0, + UI_UNIT_X * 10.0f, + UI_UNIT_Y * 0.5f, + &dyn_data->resize, + 0.0, + 0.0, + 0, + 0, + ""); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + } UI_block_emboss_set(subblock, UI_EMBOSS); @@ -6478,7 +6607,7 @@ void uiTemplateList(uiLayout *layout, 0, ""); - draw_filter(ui_list, C, col); + layout_data->draw_filter(ui_list, C, col); } else { but = uiDefIconButBitI(subblock, @@ -6498,29 +6627,131 @@ void uiTemplateList(uiLayout *layout, TIP_("Show filtering options")); UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ - but = uiDefIconButI(subblock, - UI_BTYPE_GRIP, - 0, - ICON_GRIP, - 0, - 0, - UI_UNIT_X * 10.0f, - UI_UNIT_Y * 0.5f, - &dyn_data->resize, - 0.0, - 0.0, - 0, - 0, - ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + if (add_grip_but) { + but = uiDefIconButI(subblock, + UI_BTYPE_GRIP, + 0, + ICON_GRIP, + 0, + 0, + UI_UNIT_X * 10.0f, + UI_UNIT_Y * 0.5f, + &dyn_data->resize, + 0.0, + 0.0, + 0, + 0, + ""); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + } UI_block_emboss_set(subblock, UI_EMBOSS); } } +} + +uiList *uiTemplateList_ex(uiLayout *layout, + bContext *C, + const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags, + void *customdata) +{ + TemplateListInputData input_data = {0}; + uiListType *ui_list_type; + if (!ui_template_list_data_retrieve(listtype_name, + list_id, + dataptr, + propname, + active_dataptr, + active_propname, + item_dyntip_propname, + &input_data, + &ui_list_type)) { + return NULL; + } + + uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : + uilist_draw_item_default; + uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : + uilist_draw_filter_default; + uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : + uilist_filter_items_default; + + uiList *ui_list = ui_list_ensure(C, + ui_list_type, + list_id, + layout_type, + flags & UI_TEMPLATE_LIST_SORT_REVERSE, + flags & UI_TEMPLATE_LIST_SORT_LOCK); + uiListDyn *dyn_data = ui_list->dyn_data; + + MEM_SAFE_FREE(dyn_data->customdata); + dyn_data->customdata = customdata; - if (items_ptr) { - MEM_freeN(items_ptr); + /* When active item changed since last draw, scroll to it. */ + if (input_data.active_item_idx != ui_list->list_last_activei) { + ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; + ui_list->list_last_activei = input_data.active_item_idx; } + + TemplateListItems items; + ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items); + + TemplateListLayoutDrawData layout_data = { + .draw_item = draw_item, + .draw_filter = draw_filter, + .rows = rows, + .maxrows = maxrows, + .columns = columns, + }; + + ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data, flags); + + ui_template_list_free_items(&items); + + return ui_list; +} + +void uiTemplateList(uiLayout *layout, + bContext *C, + const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags) +{ + uiTemplateList_ex(layout, + C, + listtype_name, + list_id, + dataptr, + propname, + active_dataptr, + active_propname, + item_dyntip_propname, + rows, + maxrows, + layout_type, + columns, + flags, + NULL); } /** \} */ diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 878a4e7d2c6..df3afb42ab2 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1953,8 +1953,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi 0, 0, 0, - false, - false); + UI_TEMPLATE_LIST_FLAG_NONE); RNA_property_collection_lookup_int( ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr); } @@ -1972,8 +1971,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi 0, 0, 0, - false, - false); + UI_TEMPLATE_LIST_FLAG_NONE); RNA_property_collection_lookup_int( ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr); } -- cgit v1.2.3 From 00c7ea68a88e3da0b920f436114f7160d354f6c6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 14 Jul 2021 16:23:02 +0200 Subject: UI: New UI list layout type for big preview tiles This new layout type is meant for the upcoming asset view UI template. With it it is possible to show big asset previews with their names in a responsive grid layout. Notes: * The layout is only available for C defined UI lists. We could expose it to Python, but I think there are still some scrolling issues to be fixed first. (The asset view template doesn't use scrolling for the UI list.) * I'd consider this a more usable version of the existing `GRID` layout type. We may remove that in favor of the new one in future. --- .../editors/interface/interface_templates.c | 94 ++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 30f72e75979..9fbaa62b77f 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6527,6 +6527,100 @@ static void ui_template_list_layout_draw(bContext *C, } break; } + case UILST_LAYOUT_BIG_PREVIEW_GRID: + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); + /* For grip button. */ + glob = uiLayoutColumn(box, true); + /* For scrollbar. */ + row = uiLayoutRow(glob, false); + + /* TODO ED_fileselect_init_layout(). Share somehow? */ + float size_x = (96.0f / 20.0f) * UI_UNIT_X; + float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + + const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); + uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); + + TemplateListLayoutDrawData adjusted_layout_data = *layout_data; + adjusted_layout_data.columns = cols_per_row; + uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); + + if (input_data->dataptr.data && input_data->prop) { + /* create list items */ + for (int i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; + + overlap = uiLayoutOverlap(grid); + col = uiLayoutColumn(overlap, false); + + uiBlock *subblock = uiLayoutGetBlock(col); + UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); + + but = uiDefButR_prop(subblock, + UI_BTYPE_LISTROW, + 0, + "", + 0, + 0, + size_x, + size_y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + org_i, + 0, + 0, + NULL); + UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); + + col = uiLayoutColumn(overlap, false); + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + layout_data->draw_item(ui_list, + C, + col, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); + + /* Items should be able to set context pointers for the layout. But the list-row button + * swallows events, so it needs the context storage too for handlers to see it. */ + but->context = uiLayoutGetContextStore(col); + + /* If we are "drawing" active item, set all labels as active. */ + if (i == items->active_item_idx) { + ui_layout_list_set_labels_active(col); + } + + UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); + } + } + + if (items->tot_items > visual_info.visual_items) { + /* col = */ uiLayoutColumn(row, false); + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + size_y * dyn_data->visual_height, + &ui_list->list_scroll, + 0, + dyn_data->height - dyn_data->visual_height, + dyn_data->visual_height, + 0, + ""); + } + break; } if (glob) { -- cgit v1.2.3 From ae1dc8f5f98d045b11312401d6f93d0eae15ddc5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 9 Jul 2021 21:46:55 +0200 Subject: UI: Internal support for custom UI list item drag & activate operators For pose libraries, we need to be able to apply a pose whenever activating (clicking) an item in the Pose Library asset view and blend it by dragging (press & move). And since we want to allow Python scripts to define what happens at least when activating an asset (so they can define for example a custom "Apply" operator for preset assets), it makes sense to just let them pass an operator name to the asset view template. The template will be introduced in a following commit. --- source/blender/editors/include/UI_interface.h | 9 + .../blender/editors/interface/interface_handlers.c | 214 +++++++++++++++++++-- .../blender/editors/interface/interface_intern.h | 26 ++- source/blender/editors/interface/interface_panel.c | 2 +- source/blender/editors/interface/interface_query.c | 95 ++++++++- .../editors/interface/interface_templates.c | 49 +++++ 6 files changed, 365 insertions(+), 30 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 55d0c5c4342..58eaba480e1 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -588,6 +588,8 @@ bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title); bool UI_block_is_empty(const uiBlock *block); bool UI_block_can_add_separator(const uiBlock *block); +struct uiList *UI_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event); + /* interface_region_menu_popup.c */ /** * Popup Menus @@ -2282,6 +2284,13 @@ void uiTemplateFileSelectPath(uiLayout *layout, struct bContext *C, struct FileSelectParams *params); +struct PointerRNA *UI_list_custom_activate_operator_set(struct uiList *ui_list, + const char *opname, + bool create_properties); +struct PointerRNA *UI_list_custom_drag_operator_set(struct uiList *ui_list, + const char *opname, + bool create_properties); + /* items */ void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname); void uiItemEnumO_ptr(uiLayout *layout, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 3136ca64e0f..14fd9025c8f 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -767,23 +767,34 @@ static uiAfterFunc *ui_afterfunc_new(void) * For executing operators after the button is pressed. * (some non operator buttons need to trigger operators), see: T37795. * + * \param context_but: A button from which to get the context from (`uiBut.context`) for the + * operator execution. + * + * \note Ownership over \a properties is moved here. The after-func owns it now. * \note Can only call while handling buttons. */ -PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props) +static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot, + PointerRNA **properties, + int opcontext, + const uiBut *context_but) { - PointerRNA *ptr = NULL; uiAfterFunc *after = ui_afterfunc_new(); after->optype = ot; after->opcontext = opcontext; + if (properties) { + after->opptr = *properties; + *properties = NULL; + } - if (create_props) { - ptr = MEM_callocN(sizeof(PointerRNA), __func__); - WM_operator_properties_create_ptr(ptr, ot); - after->opptr = ptr; + if (context_but && context_but->context) { + after->context = CTX_store_copy(context_but->context); } +} - return ptr; +void ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext) +{ + ui_handle_afterfunc_add_operator_ex(ot, NULL, opcontext, NULL); } static void popup_check(bContext *C, wmOperator *op) @@ -1145,6 +1156,42 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu data->applied = true; } +/** + * \note Ownership of \a properties is moved here. The after-func owns it now. + * + * \param context_but: The button to use context from when calling or polling the operator. + * + * \returns true if the operator was executed, otherwise false. + */ +static bool ui_list_invoke_item_operator(bContext *C, + const uiBut *context_but, + wmOperatorType *ot, + PointerRNA **properties) +{ + if (!ui_but_context_poll_operator(C, ot, context_but)) { + return false; + } + + /* Allow the context to be set from the hovered button, so the list item draw callback can set + * context for the operators. */ + ui_handle_afterfunc_add_operator_ex(ot, properties, WM_OP_INVOKE_DEFAULT, context_but); + return true; +} + +static void ui_apply_but_LISTROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) +{ + uiBut *listbox = ui_list_find_from_row(data->region, but); + if (listbox) { + uiList *list = listbox->custom_data; + if (list && list->dyn_data->custom_activate_optype) { + ui_list_invoke_item_operator( + C, but, list->dyn_data->custom_activate_optype, &list->dyn_data->custom_activate_opptr); + } + } + + ui_apply_but_ROW(C, block, but, data); +} + static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data) { if (!data->str) { @@ -1617,7 +1664,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const */ if (drag_info->is_xy_lock_init == false) { /* first store the buttons original coords */ - uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true); + uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true, NULL, NULL); if (but) { if (but->flag & UI_BUT_DRAG_LOCK) { @@ -1688,7 +1735,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); uiBut *but = ui_but_find_mouse_over_ex( - region, drag_info->xy_init[0], drag_info->xy_init[1], true); + region, drag_info->xy_init[0], drag_info->xy_init[1], true, NULL, NULL); if (but) { ui_apply_but_undo(but); @@ -2250,9 +2297,11 @@ static void ui_apply_but( ui_apply_but_TOG(C, but, data); break; case UI_BTYPE_ROW: - case UI_BTYPE_LISTROW: ui_apply_but_ROW(C, block, but, data); break; + case UI_BTYPE_LISTROW: + ui_apply_but_LISTROW(C, block, but, data); + break; case UI_BTYPE_DATASETROW: ui_apply_but_ROW(C, block, but, data); break; @@ -4284,7 +4333,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, uiButtonActivateType activate_type) { ARegion *region = CTX_wm_region(C); - uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true); + uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true, NULL, NULL); if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) { /* exit listrow */ @@ -4778,6 +4827,15 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) { ret = WM_UI_HANDLER_CONTINUE; } + /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event + * will be sent for the list to work with. */ + const uiBut *listbox = ui_list_find_mouse_over(data->region, event); + if (listbox) { + const uiList *ui_list = listbox->custom_data; + if (ui_list && ui_list->dyn_data->custom_drag_optype) { + ret = WM_UI_HANDLER_CONTINUE; + } + } button_activate_state(C, but, BUTTON_STATE_EXIT); return ret; } @@ -9289,6 +9347,128 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) return retval; } +/** + * Activate the underlying list-row button, so the row is highlighted. + * Early exits if \a activate_dragging is true, but the custom drag operator fails to execute. + * Gives the wanted behavior where the item is activated on a tweak event when the custom drag + * operator is executed. + */ +static int ui_list_activate_hovered_row(bContext *C, + ARegion *region, + const uiList *ui_list, + const wmEvent *event, + bool activate_dragging) +{ + const bool do_drag = activate_dragging && ui_list->dyn_data->custom_drag_optype; + + if (do_drag) { + const uiBut *hovered_but = ui_but_find_mouse_over(region, event); + if (!ui_list_invoke_item_operator(C, + hovered_but, + ui_list->dyn_data->custom_drag_optype, + &ui_list->dyn_data->custom_drag_opptr)) { + return WM_UI_HANDLER_CONTINUE; + } + } + + const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; + uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy[0], mouse_xy[1]); + if (listrow) { + wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; + + /* Hacky: Ensure the custom activate operator is not called when the custom drag operator + * was. Only one should run! */ + if (activate_dragging && do_drag) { + ((uiList *)ui_list)->dyn_data->custom_activate_optype = NULL; + } + + /* Simulate click on listrow button itself (which may be overlapped by another button). Also + * calls the custom activate operator (ui_list->custom_activate_opname). */ + UI_but_execute(C, region, listrow); + + ((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype; + } + + return WM_UI_HANDLER_BREAK; +} + +static bool ui_list_is_hovering_draggable_but(bContext *C, + const uiList *list, + const ARegion *region, + const wmEvent *event) +{ + /* On a tweak event, uses the coordinates from where tweaking was started. */ + const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; + const uiBut *hovered_but = ui_but_find_mouse_over_ex( + region, mouse_xy[0], mouse_xy[1], false, NULL, NULL); + + if (list->dyn_data->custom_drag_optype) { + if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) { + return true; + } + } + + return (hovered_but && hovered_but->dragpoin); +} + +static int ui_list_handle_click_drag(bContext *C, + const uiList *ui_list, + ARegion *region, + const wmEvent *event) +{ + if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + return WM_HANDLER_CONTINUE; + } + + int retval = WM_HANDLER_CONTINUE; + + const bool is_draggable = ui_list_is_hovering_draggable_but(C, ui_list, region, event); + bool activate = false; + bool activate_dragging = false; + + if (event->type == EVT_TWEAK_L) { + if (is_draggable) { + activate_dragging = true; + activate = true; + } + } + /* KM_CLICK is only sent after an uncaught release event, so the forground button gets all + * regular events (including mouse presses to start dragging) and this part only kicks in if it + * hasn't handled the release event. Note that if there's no overlaid button, the row selects + * on the press event already via regular UI_BTYPE_LISTROW handling. */ + else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) { + activate = true; + } + + if (activate) { + retval = ui_list_activate_hovered_row(C, region, ui_list, event, activate_dragging); + } + + return retval; +} + +static void ui_list_activate_row_from_index( + bContext *C, ARegion *region, uiBut *listbox, uiList *ui_list, int index) +{ + uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox); + if (new_active_row) { + /* Preferred way to update the active item, also calls the custom activate operator + * (#uiList.custom_activate_opname). */ + UI_but_execute(C, region, new_active_row); + } + else { + /* A bit ugly, set the active index in RNA directly. That's because a button that's + * scrolled away in the list box isn't created at all. + * The custom activate operator (#uiList.custom_activate_opname) is not called in this case + * (which may need the row button context).*/ + RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index); + RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); + ui_apply_but_undo(listbox); + } + + ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; +} + static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox) { int retval = WM_UI_HANDLER_CONTINUE; @@ -9322,7 +9502,10 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } } - if (val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + retval = ui_list_handle_click_drag(C, ui_list, region, event); + } + else if (val == KM_PRESS) { if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) || ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl && @@ -9384,12 +9567,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi CLAMP(value, min, max); if (value != value_orig) { - RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, value); - RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); - - ui_apply_but_undo(listbox); - - ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; + ui_list_activate_row_from_index(C, region, listbox, ui_list, value); redraw = true; } retval = WM_UI_HANDLER_BREAK; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 40a9c67c630..ab76de8bdba 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -931,9 +931,7 @@ const char *ui_textedit_undo(struct uiUndoStack_Text *undo_stack, int *r_cursor_index); /* interface_handlers.c */ -PointerRNA *ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, - int opcontext, - bool create_props); +extern void ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, int opcontext); extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val); extern void ui_but_activate_event(struct bContext *C, struct ARegion *region, uiBut *but); extern void ui_but_activate_over(struct bContext *C, struct ARegion *region, uiBut *but); @@ -1128,19 +1126,32 @@ bool ui_but_contains_point_px_icon(const uiBut *but, bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, int x, int y) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_find_mouse_over(struct ARegion *region, +uiBut *ui_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; - +uiBut *ui_list_find_from_row(const struct ARegion *region, + const uiBut *row_but) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, + int x, + int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_row_find_from_index(const struct ARegion *region, + const int index, + uiBut *listbox) ATTR_WARN_UNUSED_RESULT; + +typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, const int x, const int y, - const bool labeledit) ATTR_WARN_UNUSED_RESULT; + const bool labeledit, + const uiButFindPollFn find_poll, + const void *find_custom_data) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_find_mouse_over_ex(struct ARegion *region, int x, int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, + int x, + int y) ATTR_WARN_UNUSED_RESULT; bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1152,6 +1163,7 @@ uiBut *ui_but_next(uiBut *but) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_first(uiBlock *block) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_last(uiBlock *block) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_block_active_but_get(const uiBlock *block); bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_popover(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 38dc91fb57f..97d01ac3763 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2653,7 +2653,7 @@ static void panel_activate_state(const bContext *C, Panel *panel, const uiHandle /* Initiate edge panning during drags for scrolling beyond the initial region view. */ wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true); - ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT, true); + ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT); } else if (state == PANEL_STATE_ANIMATION) { panel_set_flag_recursive(panel, PNL_SELECT, false); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 025c242d0fc..ecb96a83355 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -266,11 +266,29 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv return BLI_rcti_isect_pt(&rect, x, y); } +static uiBut *ui_but_find(const ARegion *region, + const uiButFindPollFn find_poll, + const void *find_custom_data) +{ + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + if (find_poll && find_poll(but, find_custom_data) == false) { + continue; + } + return but; + } + } + + return NULL; +} + /* x and y are only used in case event is NULL... */ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, const int x, const int y, - const bool labeledit) + const bool labeledit, + const uiButFindPollFn find_poll, + const void *find_custom_data) { uiBut *butover = NULL; @@ -282,6 +300,9 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, ui_window_to_block_fl(region, block, &mx, &my); LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + if (find_poll && find_poll(but, find_custom_data) == false) { + continue; + } if (ui_but_is_interactive(but, labeledit)) { if (but->pie_dir != UI_RADIAL_NONE) { if (ui_but_isect_pie_seg(block, but)) { @@ -310,7 +331,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0); + return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0, NULL, NULL); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) @@ -351,7 +372,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) return butover; } -uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y) +uiBut *ui_list_find_mouse_over_ex(const ARegion *region, int x, int y) { if (!ui_region_contains_point_px(region, x, y)) { return NULL; @@ -369,11 +390,77 @@ uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y) return NULL; } -uiBut *ui_list_find_mouse_over(ARegion *region, const wmEvent *event) +uiBut *ui_list_find_mouse_over(const ARegion *region, const wmEvent *event) { + if (event == NULL) { + /* If there is no info about the mouse, just act as if there is nothing underneath it. */ + return NULL; + } return ui_list_find_mouse_over_ex(region, event->x, event->y); } +uiList *UI_list_find_mouse_over(const ARegion *region, const wmEvent *event) +{ + uiBut *list_but = ui_list_find_mouse_over(region, event); + if (!list_but) { + return NULL; + } + + return list_but->custom_data; +} + +static bool ui_list_contains_row(const uiBut *listbox_but, const uiBut *listrow_but) +{ + BLI_assert(listbox_but->type == UI_BTYPE_LISTBOX); + BLI_assert(listrow_but->type == UI_BTYPE_LISTROW); + /* The list box and its rows have the same RNA data (active data pointer/prop). */ + return ui_but_rna_equals(listbox_but, listrow_but); +} + +static bool ui_but_is_listbox_with_row(const uiBut *but, const void *customdata) +{ + const uiBut *row_but = customdata; + return (but->type == UI_BTYPE_LISTBOX) && ui_list_contains_row(but, row_but); +} + +uiBut *ui_list_find_from_row(const ARegion *region, const uiBut *row_but) +{ + return ui_but_find(region, ui_but_is_listbox_with_row, row_but); +} + +static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata)) +{ + return but->type == UI_BTYPE_LISTROW; +} + +uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int x, const int y) +{ + return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_listrow, NULL); +} + +struct ListRowFindIndexData { + int index; + uiBut *listbox; +}; + +static bool ui_but_is_listrow_at_index(const uiBut *but, const void *customdata) +{ + const struct ListRowFindIndexData *find_data = customdata; + + return ui_but_is_listrow(but, NULL) && ui_list_contains_row(find_data->listbox, but) && + (but->hardmax == find_data->index); +} + +uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut *listbox) +{ + BLI_assert(listbox->type == UI_BTYPE_LISTBOX); + struct ListRowFindIndexData data = { + .index = index, + .listbox = listbox, + }; + return ui_but_find(region, ui_but_is_listrow_at_index, &data); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 9fbaa62b77f..548852b407e 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -5835,6 +5835,15 @@ static void uilist_free_dyn_data(uiList *ui_list) return; } + if (dyn_data->custom_activate_opptr) { + WM_operator_properties_free(dyn_data->custom_activate_opptr); + MEM_freeN(dyn_data->custom_activate_opptr); + } + if (dyn_data->custom_drag_opptr) { + WM_operator_properties_free(dyn_data->custom_drag_opptr); + MEM_freeN(dyn_data->custom_drag_opptr); + } + MEM_SAFE_FREE(dyn_data->items_filter_flags); MEM_SAFE_FREE(dyn_data->items_filter_neworder); MEM_SAFE_FREE(dyn_data->customdata); @@ -6848,6 +6857,46 @@ void uiTemplateList(uiLayout *layout, NULL); } +/** + * \return: A RNA pointer for the operator properties. + */ +PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list, + const char *opname, + bool create_properties) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + dyn_data->custom_activate_optype = WM_operatortype_find(opname, false); + if (!dyn_data->custom_activate_optype) { + return NULL; + } + + if (create_properties) { + WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, NULL, opname); + } + + return dyn_data->custom_activate_opptr; +} + +/** + * \return: A RNA pointer for the operator properties. + */ +PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list, + const char *opname, + bool create_properties) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + dyn_data->custom_drag_optype = WM_operatortype_find(opname, false); + if (!dyn_data->custom_drag_optype) { + return NULL; + } + + if (create_properties) { + WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, NULL, opname); + } + + return dyn_data->custom_drag_opptr; +} + /** \} */ /* -------------------------------------------------------------------- */ -- cgit v1.2.3 From 8edcb3af963113e37490288876419e8f50d4f1e8 Mon Sep 17 00:00:00 2001 From: Nathan Craddock Date: Mon, 12 Jul 2021 18:45:24 +0200 Subject: UI: Support left-right arrow key walk navigation in UI lists Add improved arrow key walk navigation in grid layout UI List templates. Pressing up or down walks the active item to the adjacent row in that direction, while left and right walk through the items along the columns wrapping at the rows. Note from Julian: In combination with the following commit, this has the important limitation that the list's custom activate operator won't be called when "walking over" an item that is scrolled out of the list. That is because we don't actually create any buttons for those that could be used for the handling logic. For our purposes of the pose libraries that should be fine since the asset view list is always made big enough to display all items. Solving this might be difficult, we don't properly support nesting boxes with proper scrolling in regular layouts. It's all just hacked a bit for UI-lists to work. Overlaps quite a bit with T86149. Differential Revision: https://developer.blender.org/D11063 --- .../blender/editors/interface/interface_handlers.c | 33 ++++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 14fd9025c8f..2f03539873f 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -9469,6 +9469,27 @@ static void ui_list_activate_row_from_index( ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; } +static int ui_list_get_increment(const uiList *ui_list, const int type, const int columns) +{ + int increment = 0; + + /* Handle column offsets for grid layouts. */ + if (ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) && + ELEM(ui_list->layout_type, UILST_LAYOUT_GRID, UILST_LAYOUT_BIG_PREVIEW_GRID)) { + increment = (type == EVT_UPARROWKEY) ? -columns : columns; + } + else { + /* Left or right in grid layouts or any direction in single column layouts increments by 1. */ + increment = ELEM(type, EVT_UPARROWKEY, EVT_LEFTARROWKEY, WHEELUPMOUSE) ? -1 : 1; + } + + if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) { + increment *= -1; + } + + return increment; +} + static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox) { int retval = WM_UI_HANDLER_CONTINUE; @@ -9506,21 +9527,15 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi retval = ui_list_handle_click_drag(C, ui_list, region, event); } else if (val == KM_PRESS) { - if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) && + if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) || ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)))) { const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop); - int value, min, max, inc; + int value, min, max; - /* activate up/down the list */ value = value_orig; - if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) { - inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? 1 : -1; - } - else { - inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? -1 : 1; - } + const int inc = ui_list_get_increment(ui_list, type, dyn_data->columns); if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) { /* If we have a display order different from -- cgit v1.2.3 From 87c1c8112fa44ccb94a3e996b7499d6577d85d7f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 13 Jul 2021 15:01:00 +0200 Subject: UI: Support UI list tooltips, defined via Python scripts Makes it possible to create tooltips for UI list rows, which can be filled in .py scripts, similar to how they can extend other menus. This is used by the (to be committed) Pose Library add-on to display pose operations (selecting bones of a pose, blending a pose, etc). It's important that the Python scripts check if the UI list is the correct one by checking the list ID. For this to work, a new `bpy.context.ui_list` can be checked. For example, the Pose Library add-on does the following check: ``` def is_pose_asset_view() -> bool: # Important: Must check context first, or the menu is added for every kind of list. list = getattr(context, "ui_list", None) if not list or list.bl_idname != "UI_UL_asset_view" or list.list_id != "pose_assets": return False if not context.asset_handle: return False return True ``` --- .../blender/editors/interface/interface_context_menu.c | 16 +++++++++++++++- source/blender/editors/interface/interface_handlers.c | 2 +- source/blender/editors/interface/interface_intern.h | 2 +- .../editors/interface/interface_template_search_menu.c | 4 ++-- source/blender/editors/screen/screen_context.c | 11 +++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 2a7611eabb1..3049e2bd7b8 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -494,7 +494,7 @@ static void ui_but_menu_add_path_operators(uiLayout *layout, PointerRNA *ptr, Pr RNA_string_set(&props_ptr, "filepath", dir); } -bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) +bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *event) { /* ui_but_is_interactive() may let some buttons through that should not get a context menu - it * doesn't make sense for them. */ @@ -1226,6 +1226,20 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) } } + /* UI List item context menu. Scripts can add items to it, by default there's nothing shown. */ + ARegion *region = CTX_wm_region(C); + const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL; + const bool is_inside_listrow = is_inside_listbox ? + ui_list_row_find_mouse_over(region, event->x, event->y) != + NULL : + false; + if (is_inside_listrow) { + MenuType *mt = WM_menutype_find("UI_MT_list_item_context_menu", true); + if (mt) { + UI_menutype_draw(C, mt, uiLayoutColumn(layout, false)); + } + } + MenuType *mt = WM_menutype_find("WM_MT_button_context", true); if (mt) { UI_menutype_draw(C, mt, uiLayoutColumn(layout, false)); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2f03539873f..cd975e97613 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -7909,7 +7909,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && (event->val == KM_PRESS)) { /* RMB has two options now */ - if (ui_popup_context_menu_for_button(C, but)) { + if (ui_popup_context_menu_for_button(C, but, event)) { return WM_UI_HANDLER_BREAK; } } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index ab76de8bdba..efc94b89046 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1191,7 +1191,7 @@ struct ARegion *ui_screen_region_find_mouse_over(struct bScreen *screen, const struct wmEvent *event); /* interface_context_menu.c */ -bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but); +bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but, const struct wmEvent *event); void ui_popup_context_menu_for_panel(struct bContext *C, struct ARegion *region, struct Panel *panel); diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index f01dca7712c..3105891142f 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -1037,7 +1037,7 @@ static void menu_search_update_fn(const bContext *UNUSED(C), static bool ui_search_menu_create_context_menu(struct bContext *C, void *arg, void *active, - const struct wmEvent *UNUSED(event)) + const struct wmEvent *event) { struct MenuSearch_Data *data = arg; struct MenuSearch_Item *item = active; @@ -1058,7 +1058,7 @@ static bool ui_search_menu_create_context_menu(struct bContext *C, CTX_wm_region_set(C, item->wm_context->region); } - if (ui_popup_context_menu_for_button(C, but)) { + if (ui_popup_context_menu_for_button(C, but, event)) { has_menu = true; } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index ece6ba986f3..3ce2f326dca 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -112,6 +112,7 @@ const char *screen_context_dir[] = { "selected_editable_fcurves", "active_editable_fcurve", "selected_editable_keyframes", + "ui_list", "asset_library", NULL, }; @@ -1034,6 +1035,15 @@ static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataRe return CTX_RESULT_OK; } +static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult *result) +{ + wmWindow *win = CTX_wm_window(C); + ARegion *region = CTX_wm_region(C); + uiList *list = UI_list_find_mouse_over(region, win->eventstate); + CTX_data_pointer_set(result, NULL, &RNA_UIList, list); + return CTX_RESULT_OK; +} + /* Registry of context callback functions. */ typedef eContextResult (*context_callback)(const bContext *C, bContextDataResult *result); @@ -1109,6 +1119,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve); register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes); register_context_function("asset_library", screen_ctx_asset_library); + register_context_function("ui_list", screen_ctx_ui_list); } /* Entry point for the screen context. */ -- cgit v1.2.3 From 89fd3afd1efbb52ffc36aa253843317b63f7bdea Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 13 Jul 2021 15:44:44 +0200 Subject: UI: Support pressing Ctrl+F over UI lists to search Adds an operator invoked by default with Ctrl+F that while hovering a UI list, opens the search field of the list and enables text input for it. With this commit the search button may actually be out of view after Ctrl+F still. The following commit adds auto-scroll to solve that. A downside is that in the Properties, there also is Ctrl+F to start the editor-wide search. That's not unusual in Blender though (e.g. scolling with the mouse over a UI list also scrolls the list, not the region). --- source/blender/editors/interface/interface_ops.c | 92 ++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 5 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 4afe232e33e..376a41ff9bb 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -74,6 +74,32 @@ #include "ED_screen.h" #include "ED_text.h" +/* -------------------------------------------------------------------- */ +/** \name Immediate redraw helper + * + * Generally handlers shouldn't do any redrawing, that includes the layout/button definitions. That + * violates the Model-View-Controller pattern. + * + * But there are some operators which really need to re-run the layout definitions for various + * reasons. For example, "Edit Source" does it to find out which exact Python code added a button. + * Other operators may need to access buttons that aren't currently visible. In Blender's UI code + * design that typically means just not adding the button in the first place, for a particular + * redraw. So the operator needs to change context and re-create the layout, so the button becomes + * available to act on. + * + * \{ */ + +static void ui_region_redraw_immediately(bContext *C, ARegion *region) +{ + ED_region_do_layout(C, region); + WM_draw_region_viewport_bind(region); + ED_region_do_draw(C, region); + WM_draw_region_viewport_unbind(region); + region->do_draw = false; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Copy Data Path Operator * \{ */ @@ -1379,11 +1405,7 @@ static int editsource_exec(bContext *C, wmOperator *op) ui_editsource_active_but_set(but); /* redraw and get active button python info */ - ED_region_do_layout(C, region); - WM_draw_region_viewport_bind(region); - ED_region_do_draw(C, region); - WM_draw_region_viewport_unbind(region); - region->do_draw = false; + ui_region_redraw_immediately(C, region); for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash); BLI_ghashIterator_done(&ghi) == false; @@ -1835,6 +1857,64 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UI List Search Operator + * \{ */ + +static bool ui_list_focused_poll(bContext *C) +{ + const ARegion *region = CTX_wm_region(C); + const wmWindow *win = CTX_wm_window(C); + const uiList *list = UI_list_find_mouse_over(region, win->eventstate); + + return list != NULL; +} + +/** + * Ensure the filter options are set to be visible in the UI list. + * \return if the visibility changed, requiring a redraw. + */ +static bool ui_list_unhide_filter_options(uiList *list) +{ + if (list->filter_flag & UILST_FLT_SHOW) { + /* Nothing to be done. */ + return false; + } + + list->filter_flag |= UILST_FLT_SHOW; + return true; +} + +static int ui_list_start_filter_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + uiList *list = UI_list_find_mouse_over(region, event); + /* Poll should check. */ + BLI_assert(list != NULL); + + if (ui_list_unhide_filter_options(list)) { + ui_region_redraw_immediately(C, region); + } + + if (!UI_textbutton_activate_rna(C, region, list, "filter_name")) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_list_start_filter(wmOperatorType *ot) +{ + ot->name = "List Filter"; + ot->idname = "UI_OT_list_start_filter"; + ot->description = "Start entering filter text for the list in focus"; + + ot->invoke = ui_list_start_filter_invoke; + ot->poll = ui_list_focused_poll; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Operator & Keymap Registration * \{ */ @@ -1860,6 +1940,8 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_button_execute); WM_operatortype_append(UI_OT_button_string_clear); + WM_operatortype_append(UI_OT_list_start_filter); + /* external */ WM_operatortype_append(UI_OT_eyedropper_color); WM_operatortype_append(UI_OT_eyedropper_colorramp); -- cgit v1.2.3 From 0c83ef567c503abf1e8305062fc8b3e7de17b257 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 13 Jul 2021 17:06:15 +0200 Subject: UI: Auto-scroll to keep active text buttons in view If a text button is activated that is not in view (i.e. scrolled away), the scrolling will now be adjusted to have it in view (with some small additional margin). While entering text, the view may also be updated should the button move out of view, for whatever reason. For the most part, this feature shouldn't be needed and won't kick in, except when a clicked on text button is partially out of view or very close to the region edge. It's however quite important for the previously committed feature, that is, pressing Ctrl+F to start searching in a UI list. The end of the list where the scroll button appears may not be in view. Plus while filtering the number of visible items changes so the scrolling has to be updated to keep the search button visible. Note that I disabled the auto-scrolling for when the text button spawned an additional popup, like for search-box buttons. That is because current code assumes the button to have a fixed position while the popup is open. There is no code to update the popup position together with the button/scrolling. I also think that the logic added here could be used in more places, e.g. for the "ensure file in view" logic the File Browser does. --- source/blender/editors/include/UI_interface.h | 3 + source/blender/editors/interface/interface.c | 44 ++++++++-- .../blender/editors/interface/interface_handlers.c | 25 ++++++ .../blender/editors/interface/interface_intern.h | 12 +++ source/blender/editors/interface/interface_query.c | 18 +++- source/blender/editors/interface/interface_utils.c | 96 ++++++++++++++++++++++ source/blender/editors/screen/area.c | 2 + 7 files changed, 189 insertions(+), 11 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 58eaba480e1..020ab5f5b66 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -707,6 +707,7 @@ void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], void UI_block_end(const struct bContext *C, uiBlock *block); void UI_block_draw(const struct bContext *C, struct uiBlock *block); void UI_blocklist_update_window_matrix(const struct bContext *C, const struct ListBase *lb); +void UI_blocklist_update_view_for_buttons(const struct bContext *C, const struct ListBase *lb); void UI_blocklist_draw(const struct bContext *C, const struct ListBase *lb); void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block); @@ -2654,6 +2655,8 @@ bool UI_editsource_enable_check(void); void UI_editsource_active_but_test(uiBut *but); void UI_editsource_but_replace(const uiBut *old_but, uiBut *new_but); +void UI_but_ensure_in_view(const struct bContext *C, struct ARegion *region, const uiBut *but); + /* UI_butstore_ helpers */ typedef struct uiButStore uiButStore; typedef struct uiButStoreElem uiButStoreElem; diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 32b86119753..d5316df0e0c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -131,12 +131,10 @@ static bool ui_but_is_unit_radians(const uiBut *but) /* ************* window matrix ************** */ -void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) +void ui_block_to_region_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) { const int getsizex = BLI_rcti_size_x(®ion->winrct) + 1; const int getsizey = BLI_rcti_size_y(®ion->winrct) + 1; - const int sx = region->winrct.xmin; - const int sy = region->winrct.ymin; float gx = *r_x; float gy = *r_y; @@ -146,14 +144,19 @@ void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, fl gy += block->panel->ofsy; } - *r_x = ((float)sx) + - ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] + + *r_x = ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] + block->winmat[3][0])); - *r_y = ((float)sy) + - ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] + + *r_y = ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] + block->winmat[3][1])); } +void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) +{ + ui_block_to_region_fl(region, block, r_x, r_y); + *r_x += region->winrct.xmin; + *r_y += region->winrct.ymin; +} + void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_y) { float fx = *r_x; @@ -165,6 +168,16 @@ void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_ *r_y = (int)(fy + 0.5f); } +void ui_block_to_region_rctf(const ARegion *region, + uiBlock *block, + rctf *rct_dst, + const rctf *rct_src) +{ + *rct_dst = *rct_src; + ui_block_to_region_fl(region, block, &rct_dst->xmin, &rct_dst->ymin); + ui_block_to_region_fl(region, block, &rct_dst->xmax, &rct_dst->ymax); +} + void ui_block_to_window_rctf(const ARegion *region, uiBlock *block, rctf *rct_dst, @@ -249,6 +262,14 @@ void ui_window_to_region_rcti(const ARegion *region, rcti *rect_dst, const rcti rect_dst->ymax = rct_src->ymax - region->winrct.ymin; } +void ui_window_to_region_rctf(const ARegion *region, rctf *rect_dst, const rctf *rct_src) +{ + rect_dst->xmin = rct_src->xmin - region->winrct.xmin; + rect_dst->xmax = rct_src->xmax - region->winrct.xmin; + rect_dst->ymin = rct_src->ymin - region->winrct.ymin; + rect_dst->ymax = rct_src->ymax - region->winrct.ymin; +} + void ui_region_to_window(const ARegion *region, int *r_x, int *r_y) { *r_x += region->winrct.xmin; @@ -3440,6 +3461,15 @@ void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb) } } +void UI_blocklist_update_view_for_buttons(const bContext *C, const ListBase *lb) +{ + LISTBASE_FOREACH (uiBlock *, block, lb) { + if (block->active) { + ui_but_update_view_for_active(C, block); + } + } +} + void UI_blocklist_draw(const bContext *C, const ListBase *lb) { LISTBASE_FOREACH (uiBlock *, block, lb) { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index cd975e97613..5bd7ac28664 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3519,6 +3519,11 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) ui_but_update(but); + /* Popup blocks don't support moving after creation, so don't change the view for them. */ + if (!data->searchbox) { + UI_but_ensure_in_view(C, data->region, but); + } + WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT); #ifdef WITH_INPUT_IME @@ -8883,6 +8888,26 @@ void UI_context_update_anim_flag(const bContext *C) } } +/** + * In some cases we may want to update the view (#View2D) in-between layout definition and drawing. + * E.g. to make sure a button is visible while editing. + */ +void ui_but_update_view_for_active(const bContext *C, const uiBlock *block) +{ + uiBut *active_but = ui_block_active_but_get(block); + if (!active_but || !active_but->active || !active_but->changed || active_but->block != block) { + return; + } + /* If there is a search popup attached to the button, don't change the view. The popups don't + * support updating the position to the button position nicely. */ + uiHandleButtonData *data = active_but->active; + if (data->searchbox) { + return; + } + + UI_but_ensure_in_view(C, active_but->active->region, active_but); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index efc94b89046..a3a9776a510 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -597,11 +597,19 @@ typedef struct uiSafetyRct { void ui_fontscale(short *points, float aspect); +extern void ui_block_to_region_fl(const struct ARegion *region, + uiBlock *block, + float *r_x, + float *r_y); extern void ui_block_to_window_fl(const struct ARegion *region, uiBlock *block, float *x, float *y); extern void ui_block_to_window(const struct ARegion *region, uiBlock *block, int *x, int *y); +extern void ui_block_to_region_rctf(const struct ARegion *region, + uiBlock *block, + rctf *rct_dst, + const rctf *rct_src); extern void ui_block_to_window_rctf(const struct ARegion *region, uiBlock *block, rctf *rct_dst, @@ -620,6 +628,9 @@ extern void ui_window_to_region(const struct ARegion *region, int *x, int *y); extern void ui_window_to_region_rcti(const struct ARegion *region, rcti *rect_dst, const rcti *rct_src); +extern void ui_window_to_region_rctf(const struct ARegion *region, + rctf *rect_dst, + const rctf *rct_src); extern void ui_region_to_window(const struct ARegion *region, int *x, int *y); extern void ui_region_winrct_get_no_margin(const struct ARegion *region, struct rcti *r_rect); @@ -944,6 +955,7 @@ extern void ui_but_execute_end(struct bContext *C, uiBut *but, void *active_back); extern void ui_but_active_free(const struct bContext *C, uiBut *but); +extern void ui_but_update_view_for_active(const struct bContext *C, const uiBlock *block); extern int ui_but_menu_direction(uiBut *but); extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore); extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index ecb96a83355..8534c95b6fd 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -572,6 +572,17 @@ size_t ui_but_tip_len_only_first_line(const uiBut *but) /** \name Block (#uiBlock) State * \{ */ +uiBut *ui_block_active_but_get(const uiBlock *block) +{ + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active) { + return but; + } + } + + return NULL; +} + bool ui_block_is_menu(const uiBlock *block) { return (((block->flag & UI_BLOCK_LOOP) != 0) && @@ -675,10 +686,9 @@ uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, b uiBut *ui_region_find_active_but(ARegion *region) { LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but->active) { - return but; - } + uiBut *but = ui_block_active_but_get(block); + if (but) { + return but; } } diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 7ea02226f02..aa6dff3952a 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -29,6 +29,8 @@ #include "DNA_object_types.h" #include "DNA_screen_types.h" +#include "ED_screen.h" + #include "BLI_alloca.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -38,6 +40,7 @@ #include "BLT_translation.h" +#include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_report.h" @@ -48,6 +51,7 @@ #include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.h" +#include "UI_view2d.h" #include "WM_api.h" #include "WM_types.h" @@ -773,6 +777,98 @@ bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str, return false; } +/* -------------------------------------------------------------------- */ + +static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d) +{ + rctf region_rect; + ui_block_to_region_rctf(region, but->block, ®ion_rect, &but->rect); + + rctf view_rect; + UI_view2d_region_to_view_rctf(v2d, ®ion_rect, &view_rect); + + return view_rect; +} + +/** + * To get a margin (typically wanted), add the margin to \a rect directly. + * + * Based on #file_ensure_inside_viewbounds(), could probably share code. + * + * \return true if anything changed. + */ +static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect) +{ + const float rect_width = BLI_rctf_size_x(rect); + const float rect_height = BLI_rctf_size_y(rect); + + rctf *cur = &v2d->cur; + const float cur_width = BLI_rctf_size_x(cur); + const float cur_height = BLI_rctf_size_y(cur); + + bool changed = false; + + /* Snap to bottom edge. Also use if rect is higher than view bounds (could be a parameter). */ + if ((cur->ymin > rect->ymin) || (rect_height > cur_height)) { + cur->ymin = rect->ymin; + cur->ymax = cur->ymin + cur_height; + changed = true; + } + /* Snap to upper edge. */ + else if (cur->ymax < rect->ymax) { + cur->ymax = rect->ymax; + cur->ymin = cur->ymax - cur_height; + changed = true; + } + /* Snap to left edge. Also use if rect is wider than view bounds. */ + else if ((cur->xmin > rect->xmin) || (rect_width > cur_width)) { + cur->xmin = rect->xmin; + cur->xmax = cur->xmin + cur_width; + changed = true; + } + /* Snap to right edge. */ + else if (cur->xmax < rect->xmax) { + cur->xmax = rect->xmax; + cur->xmin = cur->xmax - cur_width; + changed = true; + } + else { + BLI_assert(BLI_rctf_inside_rctf(cur, rect)); + } + + return changed; +} + +/** + * Adjust the view so the rectangle of \a but is in view, with some extra margin. + * + * It's important that this is only executed after buttons received their final #uiBut.rect. E.g. + * #UI_panels_end() modifies them, so if that is executed, this function must not be called before + * it. + * + * \param region: The region the button is placed in. Make sure this is actually the one the button + * is placed in, not just the context region. + */ +void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but) +{ + View2D *v2d = ®ion->v2d; + /* Unitialized view or region that doesn't use View2D. */ + if ((v2d->flag & V2D_IS_INIT) == 0) { + return; + } + + rctf rect = ui_but_rect_to_view(but, region, v2d); + + const int margin = UI_UNIT_X * 0.5f; + BLI_rctf_pad(&rect, margin, margin); + + const bool changed = ui_view2d_cur_ensure_rect_in_view(v2d, &rect); + if (changed) { + UI_view2d_curRect_changed(C, v2d); + ED_region_tag_redraw_no_rebuild(region); + } +} + /* -------------------------------------------------------------------- */ /** \name Button Store * diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index f4109981aae..cb81857eb83 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -3018,6 +3018,8 @@ void ED_region_panels_layout_ex(const bContext *C, y = -y; } + UI_blocklist_update_view_for_buttons(C, ®ion->uiblocks); + if (update_tot_size) { /* this also changes the 'cur' */ UI_view2d_totRect_set(v2d, x, y); -- cgit v1.2.3 From bc4f99aa86809d564905a8e55786e04aa5bd16b4 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 13 Jul 2021 17:30:34 +0200 Subject: UI: New button/widget type for Asset Browser like preview tiles This button type shows a preview image above centered text, similar to the File Browser files in Thumbnail Display Mode or the default Asset Browser display. In fact we may want to port these over to use the new button type at some point. Will be used by the asset view UI template that will be added in a following commit. That is basically a mini version of the Asset Browser that can be displayed elsewhere in the UI. --- source/blender/editors/include/UI_interface.h | 3 + .../blender/editors/interface/interface_handlers.c | 1 + .../blender/editors/interface/interface_intern.h | 14 ++++- .../editors/interface/interface_region_search.c | 8 ++- .../blender/editors/interface/interface_widgets.c | 73 +++++++++++++++++----- 5 files changed, 78 insertions(+), 21 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 020ab5f5b66..747ec776d80 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -367,6 +367,9 @@ typedef enum { /** Buttons with value >= #UI_BTYPE_SEARCH_MENU don't get undo pushes. */ UI_BTYPE_SEARCH_MENU = 41 << 9, UI_BTYPE_EXTRA = 42 << 9, + /** A preview image (#PreviewImage), with text under it. Typically bigger than normal buttons and + * laid out in a grid, e.g. like the File Browser in thumbnail display mode. */ + UI_BTYPE_PREVIEW_TILE = 43 << 9, UI_BTYPE_HOTKEY_EVENT = 46 << 9, /** Non-interactive image, used for splash screen */ UI_BTYPE_IMAGE = 47 << 9, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 5bd7ac28664..858955271b2 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -7994,6 +7994,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_IMAGE: case UI_BTYPE_PROGRESS_BAR: case UI_BTYPE_NODE_SOCKET: + case UI_BTYPE_PREVIEW_TILE: retval = ui_do_but_EXIT(C, but, data, event); break; case UI_BTYPE_HISTOGRAM: diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index a3a9776a510..f3085c6b79b 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1055,8 +1055,18 @@ void ui_draw_menu_item(const struct uiFontStyle *fstyle, int state, uiMenuItemSeparatorType separator_type, int *r_xmax); -void ui_draw_preview_item( - const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state); +void ui_draw_preview_item(const struct uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + int state, + eFontStyle_Align text_align); +void ui_draw_preview_item_stateless(const struct uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + const uchar text_col[4], + eFontStyle_Align text_align); #define UI_TEXT_MARGIN_X 0.4f #define UI_POPUP_MARGIN (UI_DPI_FAC * 12) diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index c35dbc5d7a6..13436b6c6c8 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -599,8 +599,12 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) ui_searchbox_butrect(&rect, data, a); /* widget itself */ - ui_draw_preview_item( - &data->fstyle, &rect, data->items.names[a], data->items.icons[a], state); + ui_draw_preview_item(&data->fstyle, + &rect, + data->items.names[a], + data->items.icons[a], + state, + UI_STYLE_TEXT_LEFT); } /* indicate more */ diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 8abff4ad0d8..bcd945b21a9 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -105,6 +105,7 @@ typedef enum { /* specials */ UI_WTYPE_ICON, UI_WTYPE_ICON_LABEL, + UI_WTYPE_PREVIEW_TILE, UI_WTYPE_SWATCH, UI_WTYPE_RGB_PICKER, UI_WTYPE_UNITVEC, @@ -4018,6 +4019,14 @@ static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roun widgetbase_draw(&wtb, wcol); } +static void widget_preview_tile( + uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) +{ + const uiStyle *style = UI_style_get(); + ui_draw_preview_item_stateless( + &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER); +} + static void widget_menuiconbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), @@ -4483,6 +4492,13 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.custom = widget_icon_has_anim; break; + case UI_WTYPE_PREVIEW_TILE: + wt.draw = NULL; + /* Drawn via the `custom` callback. */ + wt.text = NULL; + wt.custom = widget_preview_tile; + break; + case UI_WTYPE_SWATCH: wt.custom = widget_swatch; break; @@ -4778,6 +4794,10 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu wt = widget_type(UI_WTYPE_BOX); break; + case UI_BTYPE_PREVIEW_TILE: + wt = widget_type(UI_WTYPE_PREVIEW_TILE); + break; + case UI_BTYPE_EXTRA: widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect); break; @@ -4931,13 +4951,15 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu wt->draw(&wt->wcol, rect, state, roundboxalign); } - if (use_alpha_blend) { - GPU_blend(GPU_BLEND_ALPHA); - } + if (wt->text) { + if (use_alpha_blend) { + GPU_blend(GPU_BLEND_ALPHA); + } - wt->text(fstyle, &wt->wcol, but, rect); - if (use_alpha_blend) { - GPU_blend(GPU_BLEND_NONE); + wt->text(fstyle, &wt->wcol, but, rect); + if (use_alpha_blend) { + GPU_blend(GPU_BLEND_NONE); + } } } @@ -5459,17 +5481,20 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, } } -void ui_draw_preview_item( - const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state) +/** + * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on + * state. It just draws the preview and text directly. + */ +void ui_draw_preview_item_stateless(const uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + const uchar text_col[4], + eFontStyle_Align text_align) { rcti trect = *rect; const float text_size = UI_UNIT_Y; float font_dims[2] = {0.0f, 0.0f}; - uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); - - /* drawing button background */ - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0); /* draw icon in rect above the space reserved for the label */ rect->ymin += text_size; @@ -5481,8 +5506,6 @@ void ui_draw_preview_item( fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]); /* text rect */ - trect.xmin += 0; - trect.xmax = trect.xmin + font_dims[0] + U.widget_unit / 2; trect.ymin += U.widget_unit / 2; trect.ymax = trect.ymin + font_dims[1]; if (trect.xmax > rect->xmax - PREVIEW_PAD) { @@ -5501,11 +5524,27 @@ void ui_draw_preview_item( UI_fontstyle_draw(fstyle, &trect, drawstr, - wt->wcol.text, + text_col, &(struct uiFontStyleDraw_Params){ - .align = UI_STYLE_TEXT_CENTER, + .align = text_align, }); } } +void ui_draw_preview_item(const uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + int state, + eFontStyle_Align text_align) +{ + uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); + + /* drawing button background */ + wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, 0, 0); + + ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align); +} + /** \} */ -- cgit v1.2.3 From 26b098c04fbeb7f4d7c320b6c2066d2b448ae136 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 13 Jul 2021 17:42:25 +0200 Subject: UI: Support defining UI lists in C So far all UI lists had to be defined in Python, this makes it possible to define them in C as well. Note that there is a whole bunch of special handling for the Python API that isn't there for C. I think most importantly custom properties support, which currently can't be added for C defined UI lists. The upcoming asset view UI template will use this, which needs to be defined in C. Adds a new file `interface_template_list.cc`, which at this point is mostly a dummy to have a place for the `ED_uilisttypes_ui()` definition. I plan a separate cleanup to move the UI-list template to that file. --- source/blender/editors/include/UI_interface.h | 1 + source/blender/editors/interface/CMakeLists.txt | 1 + .../editors/interface/interface_template_list.cc | 27 ++++++++++++++++++++++ source/blender/editors/space_api/spacetypes.c | 2 ++ 4 files changed, 31 insertions(+) create mode 100644 source/blender/editors/interface/interface_template_list.cc (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 747ec776d80..020ca1f8f55 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2558,6 +2558,7 @@ typedef struct uiDragColorHandle { void ED_operatortypes_ui(void); void ED_keymap_ui(struct wmKeyConfig *keyconf); +void ED_uilisttypes_ui(void); void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); bool UI_drop_color_poll(struct bContext *C, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 2cc3830042c..29f868eebb0 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -66,6 +66,7 @@ set(SRC interface_region_tooltip.c interface_regions.c interface_style.c + interface_template_list.cc interface_template_search_menu.c interface_template_search_operator.c interface_templates.c diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc new file mode 100644 index 00000000000..90980b3c963 --- /dev/null +++ b/source/blender/editors/interface/interface_template_list.cc @@ -0,0 +1,27 @@ +/* + * 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 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + * + * TODO: The UI list template implementation should be moved here. + */ + +#include "UI_interface.h" + +void ED_uilisttypes_ui(void) +{ +} diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index adb824b8934..b3b3eafb6e7 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -129,6 +129,8 @@ void ED_spacetypes_init(void) ED_screen_user_menu_register(); + ED_uilisttypes_ui(); + /* Gizmo types. */ ED_gizmotypes_button_2d(); ED_gizmotypes_dial_3d(); -- cgit v1.2.3 From 6bd42baaebac3255082eb46884bdd3e27361c135 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 13 Jul 2021 18:05:16 +0200 Subject: Cleanup: Use const for UI icon getter function --- source/blender/editors/include/UI_interface_icons.h | 5 ++++- source/blender/editors/interface/interface_icons.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 266a538b6c3..37cf7229ffb 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -105,7 +105,10 @@ int UI_iconfile_get_index(const char *filename); struct PreviewImage *UI_icon_to_preview(int icon_id); -int UI_icon_from_rnaptr(struct bContext *C, struct PointerRNA *ptr, int rnaicon, const bool big); +int UI_icon_from_rnaptr(const struct bContext *C, + struct PointerRNA *ptr, + int rnaicon, + const bool big); int UI_icon_from_idcode(const int idcode); int UI_icon_from_library(const struct ID *id); int UI_icon_from_object_mode(const int mode); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 2a505abe257..43ac646f053 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -2201,7 +2201,7 @@ int UI_icon_from_library(const ID *id) return ICON_NONE; } -int UI_icon_from_rnaptr(bContext *C, PointerRNA *ptr, int rnaicon, const bool big) +int UI_icon_from_rnaptr(const bContext *C, PointerRNA *ptr, int rnaicon, const bool big) { ID *id = NULL; -- cgit v1.2.3 From 8925d3b7bf989e1db364d989fbd0c6cdb9a2acdb Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 13 Jul 2021 18:07:57 +0200 Subject: UI/Assets: Initial Asset View UI template The asset view UI template is a mini-version of the Asset Browser that can be placed in regular layouts, regions or popups. At this point it's made specifically for placement in vertical layouts, it can be made more flexible in the future. Generally the way this is implemented will likely change a lot still as the asset system evolves. The Pose Library add-on will use the asset view to display pose libraries in the 3D View sidebar. References: * https://developer.blender.org/T86139 * https://code.blender.org/2021/06/asset-browser-project-update/#what-are-we-building * https://code.blender.org/2021/05/pose-library-v2-0/#use-from-3d-viewport Notes: * Important limitation: Due to the early & WIP implementation of the asset list, all asset views showing the same library will show the same assets. That is despite the ID type filter option the template provides. The first asset view created will determine what's visible. Of course this should be made to work eventually. * The template supports passing an activate and a drag operator name. The former is called when an asset is clicked on (e.g. to apply the asset) the latter when dragging (e.g. to .blend a pose asset). If no drag operator is set, regular asset drag & drop will be executed. * The template returns the properties for both operators (see example below). * The argument list for using the template is quite long, but we can't avoid that currently. The UI list design requires that we pass a number of RNA or custom properties to work with, that for the Pose Libraries should be registered at the Pose Library add-on level, not in core Blender. * Idea is that Python scripts or add-ons that want to use the asset view can register custom properties, to hold data like the list of assets, and the active asset index. Maybe that will change in future and we can manage these internally. As an example, the pose library add-on uses it like this: ``` activate_op_props, drag_op_props = layout.template_asset_view( "pose_assets", workspace, "active_asset_library", wm, "pose_assets", workspace, "active_pose_asset_index", filter_id_types={"filter_action"}, activate_operator="poselib.apply_pose_asset", drag_operator="poselib.blend_pose_asset", ) drag_op_props.release_confirm = True drag_op_props.flipped = wm.poselib_flipped activate_op_props.flipped = wm.poselib_flipped ``` --- source/blender/editors/include/UI_interface.h | 15 ++ source/blender/editors/interface/CMakeLists.txt | 1 + .../blender/editors/interface/interface_intern.h | 3 + .../interface/interface_template_asset_view.cc | 272 +++++++++++++++++++++ .../editors/interface/interface_template_list.cc | 5 + source/blender/editors/screen/area.c | 6 + 6 files changed, 302 insertions(+) create mode 100644 source/blender/editors/interface/interface_template_asset_view.cc (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 020ca1f8f55..a25aac5803c 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -35,6 +35,7 @@ extern "C" { /* Struct Declarations */ struct ARegion; +struct AssetFilterSettings; struct AutoComplete; struct EnumPropertyItem; struct FileSelectParams; @@ -2287,6 +2288,20 @@ int uiTemplateRecentFiles(struct uiLayout *layout, int rows); void uiTemplateFileSelectPath(uiLayout *layout, struct bContext *C, struct FileSelectParams *params); +void uiTemplateAssetView(struct uiLayout *layout, + struct bContext *C, + const char *list_id, + struct PointerRNA *asset_library_dataptr, + const char *asset_library_propname, + struct PointerRNA *assets_dataptr, + const char *assets_propname, + struct PointerRNA *active_dataptr, + const char *active_propname, + const struct AssetFilterSettings *filter_settings, + const char *activate_opname, + struct PointerRNA *r_activate_op_properties, + const char *drag_opname, + struct PointerRNA *r_drag_op_properties); struct PointerRNA *UI_list_custom_activate_operator_set(struct uiList *ui_list, const char *opname, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 29f868eebb0..39dd6143eb9 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -66,6 +66,7 @@ set(SRC interface_region_tooltip.c interface_regions.c interface_style.c + interface_template_asset_view.cc interface_template_list.cc interface_template_search_menu.c interface_template_search_operator.c diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index f3085c6b79b..a07f924e65b 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1241,6 +1241,9 @@ void UI_OT_eyedropper_driver(struct wmOperatorType *ot); /* interface_eyedropper_gpencil_color.c */ void UI_OT_eyedropper_gpencil_color(struct wmOperatorType *ot); +/* interface_template_asset_view.cc */ +struct uiListType *UI_UL_asset_view(void); + /** * For use with #ui_rna_collection_search_update_fn. */ diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc new file mode 100644 index 00000000000..2860abb32a1 --- /dev/null +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -0,0 +1,272 @@ +/* + * 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 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + */ + +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_screen.h" + +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_string_ref.hh" + +#include "BLO_readfile.h" + +#include "ED_asset.h" +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "interface_intern.h" + +struct AssetViewListData { + AssetLibraryReference asset_library; + bScreen *screen; +}; + +static void asset_view_item_but_drag_set(uiBut *but, + AssetViewListData *list_data, + AssetHandle *asset_handle) +{ + ID *id = asset_handle->file_data->id; + if (id != NULL) { + UI_but_drag_set_id(but, id); + return; + } + + const blender::StringRef asset_list_path = ED_assetlist_library_path(&list_data->asset_library); + char blend_path[FILE_MAX_LIBEXTRA]; + + char path[FILE_MAX_LIBEXTRA]; + BLI_join_dirfile(path, sizeof(path), asset_list_path.data(), asset_handle->file_data->relpath); + if (BLO_library_path_explode(path, blend_path, nullptr, nullptr)) { + ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle); + UI_but_drag_set_asset(but, + asset_handle->file_data->name, + BLI_strdup(blend_path), + asset_handle->file_data->blentype, + FILE_ASSET_IMPORT_APPEND, + asset_handle->file_data->preview_icon_id, + imbuf, + 1.0f); + } +} + +static void asset_view_draw_item(uiList *ui_list, + bContext *UNUSED(C), + uiLayout *layout, + PointerRNA *UNUSED(dataptr), + PointerRNA *itemptr, + int UNUSED(icon), + PointerRNA *UNUSED(active_dataptr), + const char *UNUSED(active_propname), + int UNUSED(index), + int UNUSED(flt_flag)) +{ + AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata; + + BLI_assert(RNA_struct_is_a(itemptr->type, &RNA_AssetHandle)); + AssetHandle *asset_handle = (AssetHandle *)itemptr->data; + + uiLayoutSetContextPointer(layout, "asset_handle", itemptr); + + uiBlock *block = uiLayoutGetBlock(layout); + /* TODO ED_fileselect_init_layout(). Share somehow? */ + const float size_x = (96.0f / 20.0f) * UI_UNIT_X; + const float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + uiBut *but = uiDefIconTextBut(block, + UI_BTYPE_PREVIEW_TILE, + 0, + asset_handle->file_data->preview_icon_id, + asset_handle->file_data->name, + 0, + 0, + size_x, + size_y, + nullptr, + 0, + 0, + 0, + 0, + ""); + ui_def_but_icon(but, + asset_handle->file_data->preview_icon_id, + /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ + UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + if (!ui_list->dyn_data->custom_drag_optype) { + asset_view_item_but_drag_set(but, list_data, asset_handle); + } +} + +static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params) +{ + AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata; + const wmNotifier *notifier = params->notifier; + + switch (notifier->category) { + case NC_ID: { + if (ELEM(notifier->action, NA_RENAME)) { + ED_assetlist_storage_tag_main_data_dirty(); + } + break; + } + } + + if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) { + ED_region_tag_redraw(params->region); + } +} + +uiListType *UI_UL_asset_view() +{ + uiListType *list_type = (uiListType *)MEM_callocN(sizeof(*list_type), __func__); + + BLI_strncpy(list_type->idname, "UI_UL_asset_view", sizeof(list_type->idname)); + list_type->draw_item = asset_view_draw_item; + list_type->listener = asset_view_listener; + + return list_type; +} + +static void asset_view_template_refresh_asset_collection( + const AssetLibraryReference &asset_library, + PointerRNA &assets_dataptr, + const char *assets_propname) +{ + PropertyRNA *assets_prop = RNA_struct_find_property(&assets_dataptr, assets_propname); + if (!assets_prop) { + RNA_warning("Asset collection not found"); + return; + } + if (!RNA_struct_is_a(RNA_property_pointer_type(&assets_dataptr, assets_prop), + &RNA_AssetHandle)) { + RNA_warning("Expected a collection property for AssetHandle items"); + return; + } + + RNA_property_collection_clear(&assets_dataptr, assets_prop); + + ED_assetlist_iterate(&asset_library, [&](FileDirEntry &file) { + PointerRNA itemptr, fileptr; + RNA_property_collection_add(&assets_dataptr, assets_prop, &itemptr); + + RNA_pointer_create(nullptr, &RNA_FileSelectEntry, &file, &fileptr); + RNA_pointer_set(&itemptr, "file_data", fileptr); + + /* Copy name from file to asset-handle name ID-property. */ + char name[MAX_NAME]; + PropertyRNA *file_name_prop = RNA_struct_name_property(fileptr.type); + RNA_property_string_get(&fileptr, file_name_prop, name); + PropertyRNA *asset_name_prop = RNA_struct_name_property(&RNA_AssetHandle); + RNA_property_string_set(&itemptr, asset_name_prop, name); + + return true; + }); +} + +void uiTemplateAssetView(uiLayout *layout, + bContext *C, + const char *list_id, + PointerRNA *asset_library_dataptr, + const char *asset_library_propname, + PointerRNA *assets_dataptr, + const char *assets_propname, + PointerRNA *active_dataptr, + const char *active_propname, + const AssetFilterSettings *filter_settings, + const char *activate_opname, + PointerRNA *r_activate_op_properties, + const char *drag_opname, + PointerRNA *r_drag_op_properties) +{ + if (!list_id || !list_id[0]) { + RNA_warning("Asset view needs a valid identifier"); + return; + } + + uiLayout *col = uiLayoutColumn(layout, false); + + PropertyRNA *asset_library_prop = RNA_struct_find_property(asset_library_dataptr, + asset_library_propname); + AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value( + RNA_property_enum_get(asset_library_dataptr, asset_library_prop)); + + uiLayout *row = uiLayoutRow(col, true); + uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); + if (asset_library.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); + } + + ED_assetlist_storage_fetch(&asset_library, filter_settings, C); + ED_assetlist_ensure_previews_job(&asset_library, C); + const int tot_items = ED_assetlist_size(&asset_library); + + asset_view_template_refresh_asset_collection(asset_library, *assets_dataptr, assets_propname); + + AssetViewListData *list_data = (AssetViewListData *)MEM_mallocN(sizeof(*list_data), + "AssetViewListData"); + list_data->asset_library = asset_library; + list_data->screen = CTX_wm_screen(C); + + /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading + * (of previews) of the items? */ + uiList *list = uiTemplateList_ex(col, + C, + "UI_UL_asset_view", + list_id, + assets_dataptr, + assets_propname, + active_dataptr, + active_propname, + nullptr, + tot_items, + 0, + UILST_LAYOUT_BIG_PREVIEW_GRID, + 0, + UI_TEMPLATE_LIST_NO_GRIP, + list_data); + if (!list) { + /* List creation failed. */ + MEM_freeN(list_data); + return; + } + + if (activate_opname) { + PointerRNA *ptr = UI_list_custom_activate_operator_set( + list, activate_opname, r_activate_op_properties != NULL); + if (r_activate_op_properties && ptr) { + *r_activate_op_properties = *ptr; + } + } + if (drag_opname) { + PointerRNA *ptr = UI_list_custom_drag_operator_set( + list, drag_opname, r_drag_op_properties != NULL); + if (r_drag_op_properties && ptr) { + *r_drag_op_properties = *ptr; + } + } +} diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 90980b3c963..334fc7ff057 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -20,8 +20,13 @@ * TODO: The UI list template implementation should be moved here. */ +#include "WM_api.h" + #include "UI_interface.h" +#include "interface_intern.h" + void ED_uilisttypes_ui(void) { + WM_uilisttype_add(UI_UL_asset_view()); } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index cb81857eb83..c351ade9954 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -161,6 +161,12 @@ void ED_region_do_listen(wmRegionListenerParams *params) if (region->type && region->type->listener) { region->type->listener(params); } + + LISTBASE_FOREACH (uiList *, list, ®ion->ui_lists) { + if (list->type && list->type->listener) { + list->type->listener(list, params); + } + } } /* only exported for WM */ -- cgit v1.2.3 From 9d9c879f4bdf05c92826023131e7eb2a57c06102 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 15 Jul 2021 16:56:18 +0200 Subject: Cleanup: Move UI list template code to own file (C++) This move was already prepared with 788d38046032 and 26b098c04fbe. The template is quite big already, better to give it its own file. Plus it could use some C++ features like RAII and maybe some more object oriented code. I plan further refactoring there. --- .../editors/interface/interface_template_list.cc | 1285 +++++++++++++++++++- .../editors/interface/interface_templates.c | 1255 ------------------- 2 files changed, 1282 insertions(+), 1258 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 334fc7ff057..dfbf64b79af 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -16,17 +16,1296 @@ /** \file * \ingroup edinterface - * - * TODO: The UI list template implementation should be moved here. */ -#include "WM_api.h" +#include +#include + +#include "BLI_fnmatch.h" +#include "BLI_listbase.h" +#include "BLI_math_base.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_screen.h" + +#include "BLT_translation.h" + +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" #include "UI_interface.h" +#include "UI_view2d.h" + +#include "WM_api.h" #include "interface_intern.h" +/** + * The validated data that was passed to #uiTemplateList (typically through Python). + * Populated through #ui_template_list_data_retrieve(). + */ +typedef struct { + PointerRNA dataptr; + PropertyRNA *prop; + PointerRNA active_dataptr; + PropertyRNA *activeprop; + const char *item_dyntip_propname; + + /* Index as stored in the input property. I.e. the index before sorting. */ + int active_item_idx; +} TemplateListInputData; + +/** + * Internal wrapper for a single item in the list (well, actually stored as a vector). + */ +typedef struct { + PointerRNA item; + int org_idx; + int flt_flag; +} _uilist_item; + +/** + * Container for the item vector and additional info. + */ +typedef struct { + _uilist_item *item_vec; + /* Index of the active item following visual order. I.e. unlike + * TemplateListInputData.active_item_idx, this is the index after sorting. */ + int active_item_idx; + int tot_items; +} TemplateListItems; + +typedef struct { + uiListDrawItemFunc draw_item; + uiListDrawFilterFunc draw_filter; + + int rows; + int maxrows; + int columns; +} TemplateListLayoutDrawData; + +typedef struct { + int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ + int start_idx; /* Index of first item to display. */ + int end_idx; /* Index of last item to display + 1. */ +} TemplateListVisualInfo; + +static void uilist_draw_item_default(struct uiList *ui_list, + struct bContext *UNUSED(C), + struct uiLayout *layout, + struct PointerRNA *UNUSED(dataptr), + struct PointerRNA *itemptr, + int icon, + struct PointerRNA *UNUSED(active_dataptr), + const char *UNUSED(active_propname), + int UNUSED(index), + int UNUSED(flt_flag)) +{ + PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type); + + /* Simplest one! */ + switch (ui_list->layout_type) { + case UILST_LAYOUT_GRID: + uiItemL(layout, "", icon); + break; + case UILST_LAYOUT_DEFAULT: + case UILST_LAYOUT_COMPACT: + default: + if (nameprop) { + uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon); + } + else { + uiItemL(layout, "", icon); + } + break; + } +} + +static void uilist_draw_filter_default(struct uiList *ui_list, + struct bContext *UNUSED(C), + struct uiLayout *layout) +{ + PointerRNA listptr; + RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr); + + uiLayout *row = uiLayoutRow(layout, false); + + uiLayout *subrow = uiLayoutRow(row, true); + uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE); + uiItemR(subrow, + &listptr, + "use_filter_invert", + UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, + "", + ICON_ARROW_LEFTRIGHT); + + if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) { + subrow = uiLayoutRow(row, true); + uiItemR(subrow, + &listptr, + "use_filter_sort_alpha", + UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, + "", + ICON_NONE); + uiItemR(subrow, + &listptr, + "use_filter_sort_reverse", + UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, + "", + (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC); + } +} + +typedef struct { + char name[MAX_IDPROP_NAME]; + int org_idx; +} StringCmp; + +static int cmpstringp(const void *p1, const void *p2) +{ + /* Case-insensitive comparison. */ + return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name); +} + +static void uilist_filter_items_default(struct uiList *ui_list, + struct bContext *UNUSED(C), + struct PointerRNA *dataptr, + const char *propname) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + PropertyRNA *prop = RNA_struct_find_property(dataptr, propname); + + const char *filter_raw = ui_list->filter_byname; + char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL; + const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; + const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == + UILST_FLT_SORT_ALPHA; + const int len = RNA_property_collection_length(dataptr, prop); + + dyn_data->items_shown = dyn_data->items_len = len; + + if (len && (order_by_name || filter_raw[0])) { + StringCmp *names = NULL; + int order_idx = 0, i = 0; + + if (order_by_name) { + names = static_cast(MEM_callocN(sizeof(StringCmp) * len, "StringCmp")); + } + if (filter_raw[0]) { + const size_t slen = strlen(filter_raw); + + dyn_data->items_filter_flags = static_cast( + MEM_callocN(sizeof(int) * len, "items_filter_flags")); + dyn_data->items_shown = 0; + + /* Implicitly add heading/trailing wildcards if needed. */ + if (slen + 3 <= sizeof(filter_buff)) { + filter = filter_buff; + } + else { + filter = filter_dyn = static_cast( + MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn")); + } + BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3); + } + + RNA_PROP_BEGIN (dataptr, itemptr, prop) { + bool do_order = false; + + char *namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); + const char *name = namebuf ? namebuf : ""; + + if (filter[0]) { + /* Case-insensitive! */ + if (fnmatch(filter, name, FNM_CASEFOLD) == 0) { + dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; + if (!filter_exclude) { + dyn_data->items_shown++; + do_order = order_by_name; + } + // printf("%s: '%s' matches '%s'\n", __func__, name, filter); + } + else if (filter_exclude) { + dyn_data->items_shown++; + do_order = order_by_name; + } + } + else { + do_order = order_by_name; + } + + if (do_order) { + names[order_idx].org_idx = order_idx; + BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME); + } + + /* free name */ + if (namebuf) { + MEM_freeN(namebuf); + } + i++; + } + RNA_PROP_END; + + if (order_by_name) { + int new_idx; + /* NOTE: order_idx equals either to ui_list->items_len if no filtering done, + * or to ui_list->items_shown if filter is enabled, + * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded. + * This way, we only sort items we actually intend to draw! + */ + qsort(names, order_idx, sizeof(StringCmp), cmpstringp); + + dyn_data->items_filter_neworder = static_cast( + MEM_mallocN(sizeof(int) * order_idx, "items_filter_neworder")); + for (new_idx = 0; new_idx < order_idx; new_idx++) { + dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx; + } + } + + if (filter_dyn) { + MEM_freeN(filter_dyn); + } + if (names) { + MEM_freeN(names); + } + } +} + +static void uilist_free_dyn_data(uiList *ui_list) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + if (!dyn_data) { + return; + } + + if (dyn_data->custom_activate_opptr) { + WM_operator_properties_free(dyn_data->custom_activate_opptr); + MEM_freeN(dyn_data->custom_activate_opptr); + } + if (dyn_data->custom_drag_opptr) { + WM_operator_properties_free(dyn_data->custom_drag_opptr); + MEM_freeN(dyn_data->custom_drag_opptr); + } + + MEM_SAFE_FREE(dyn_data->items_filter_flags); + MEM_SAFE_FREE(dyn_data->items_filter_neworder); + MEM_SAFE_FREE(dyn_data->customdata); +} + +/** + * Validate input parameters and initialize \a r_data from that. Plus find the list-type and return + * it in \a r_list_type. + * + * \return false if the input data isn't valid. Will also raise an RNA warning in that case. + */ +static bool ui_template_list_data_retrieve(const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + TemplateListInputData *r_input_data, + uiListType **r_list_type) +{ + memset(r_input_data, 0, sizeof(*r_input_data)); + + /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ + if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { + RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", + UI_UL_DEFAULT_CLASS_NAME); + return false; + } + + if (!active_dataptr->data) { + RNA_warning("No active data"); + return false; + } + + r_input_data->dataptr = *dataptr; + if (dataptr->data) { + r_input_data->prop = RNA_struct_find_property(dataptr, propname); + if (!r_input_data->prop) { + RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname); + return false; + } + } + + r_input_data->active_dataptr = *active_dataptr; + r_input_data->activeprop = RNA_struct_find_property(active_dataptr, active_propname); + if (!r_input_data->activeprop) { + RNA_warning( + "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname); + return false; + } + + if (r_input_data->prop) { + const PropertyType type = RNA_property_type(r_input_data->prop); + if (type != PROP_COLLECTION) { + RNA_warning("Expected a collection data property"); + return false; + } + } + + const PropertyType activetype = RNA_property_type(r_input_data->activeprop); + if (activetype != PROP_INT) { + RNA_warning("Expected an integer active data property"); + return false; + } + + /* Find the uiList type. */ + if (!(*r_list_type = WM_uilisttype_find(listtype_name, false))) { + RNA_warning("List type %s not found", listtype_name); + return false; + } + + r_input_data->active_item_idx = RNA_property_int_get(&r_input_data->active_dataptr, + r_input_data->activeprop); + r_input_data->item_dyntip_propname = item_dyntip_propname; + + return true; +} + +static void ui_template_list_collect_items(PointerRNA *list_ptr, + PropertyRNA *list_prop, + uiListDyn *dyn_data, + int filter_exclude, + bool order_reverse, + int activei, + TemplateListItems *r_items) +{ + int i = 0; + int reorder_i = 0; + bool activei_mapping_pending = true; + + RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) { + if (!dyn_data->items_filter_flags || + ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { + int new_order_idx; + if (dyn_data->items_filter_neworder) { + new_order_idx = dyn_data->items_filter_neworder[reorder_i++]; + new_order_idx = order_reverse ? dyn_data->items_shown - new_order_idx - 1 : new_order_idx; + } + else { + new_order_idx = order_reverse ? dyn_data->items_shown - ++reorder_i : reorder_i++; + } + // printf("%s: ii: %d\n", __func__, ii); + r_items->item_vec[new_order_idx].item = itemptr; + r_items->item_vec[new_order_idx].org_idx = i; + r_items->item_vec[new_order_idx].flt_flag = dyn_data->items_filter_flags ? + dyn_data->items_filter_flags[i] : + 0; + + if (activei_mapping_pending && activei == i) { + activei = new_order_idx; + /* So that we do not map again activei! */ + activei_mapping_pending = false; + } +#if 0 /* For now, do not alter active element, even if it will be hidden... */ + else if (activei < i) { + /* We do not want an active but invisible item! + * Only exception is when all items are filtered out... + */ + if (prev_order_idx >= 0) { + activei = prev_order_idx; + RNA_property_int_set(active_dataptr, activeprop, prev_i); + } + else { + activei = new_order_idx; + RNA_property_int_set(active_dataptr, activeprop, i); + } + } + prev_i = i; + prev_ii = new_order_idx; +#endif + } + i++; + } + RNA_PROP_END; + + /* If mapping is still pending, no active item was found. Mark as invalid (-1) */ + r_items->active_item_idx = activei_mapping_pending ? -1 : activei; +} + +/** + * Create the UI-list representation of the list items, sorted and filtered if needed. + */ +static void ui_template_list_collect_display_items(bContext *C, + uiList *ui_list, + TemplateListInputData *input_data, + const uiListFilterItemsFunc filter_items_fn, + TemplateListItems *r_items) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + memset(r_items, 0, sizeof(*r_items)); + + /* Filter list items! (not for compact layout, though) */ + if (input_data->dataptr.data && input_data->prop) { + const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; + int items_shown; +#if 0 + int prev_ii = -1, prev_i; +#endif + + if (ui_list->layout_type == UILST_LAYOUT_COMPACT) { + dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length( + &input_data->dataptr, input_data->prop); + } + else { + // printf("%s: filtering...\n", __func__); + filter_items_fn(ui_list, C, &input_data->dataptr, RNA_property_identifier(input_data->prop)); + // printf("%s: filtering done.\n", __func__); + } + + items_shown = dyn_data->items_shown; + if (items_shown >= 0) { + r_items->item_vec = static_cast<_uilist_item *>( + MEM_mallocN(sizeof(*r_items->item_vec) * items_shown, __func__)); + // printf("%s: items shown: %d.\n", __func__, items_shown); + + ui_template_list_collect_items(&input_data->dataptr, + input_data->prop, + dyn_data, + filter_exclude, + order_reverse, + input_data->active_item_idx, + r_items); + } + if (dyn_data->items_shown >= 0) { + r_items->tot_items = dyn_data->items_shown; + } + else { + r_items->tot_items = dyn_data->items_len; + } + } +} + +static void ui_template_list_free_items(TemplateListItems *items) +{ + if (items->item_vec) { + MEM_freeN(items->item_vec); + } +} + +static void uilist_prepare(uiList *ui_list, + const TemplateListItems *items, + const TemplateListLayoutDrawData *layout_data, + TemplateListVisualInfo *r_visual_info) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + const bool use_auto_size = (ui_list->list_grip < + (layout_data->rows - UI_LIST_AUTO_SIZE_THRESHOLD)); + + int actual_rows = layout_data->rows; + int actual_maxrows = layout_data->maxrows; + int columns = layout_data->columns; + + /* default rows */ + if (actual_rows <= 0) { + actual_rows = 5; + } + dyn_data->visual_height_min = actual_rows; + if (actual_maxrows < actual_rows) { + actual_maxrows = max_ii(actual_rows, 5); + } + if (columns <= 0) { + columns = 9; + } + + int activei_row; + if (columns > 1) { + dyn_data->height = (int)ceil((double)items->tot_items / (double)columns); + activei_row = (int)floor((double)items->active_item_idx / (double)columns); + } + else { + dyn_data->height = items->tot_items; + activei_row = items->active_item_idx; + } + + dyn_data->columns = columns; + + if (!use_auto_size) { + /* No auto-size, yet we clamp at min size! */ + actual_rows = max_ii(ui_list->list_grip, actual_rows); + } + else if ((actual_rows != actual_maxrows) && (dyn_data->height > actual_rows)) { + /* Expand size if needed and possible. */ + actual_rows = min_ii(dyn_data->height, actual_maxrows); + } + + /* If list length changes or list is tagged to check this, + * and active is out of view, scroll to it. */ + if ((ui_list->list_last_len != items->tot_items) || + (ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM)) { + if (activei_row < ui_list->list_scroll) { + ui_list->list_scroll = activei_row; + } + else if (activei_row >= ui_list->list_scroll + actual_rows) { + ui_list->list_scroll = activei_row - actual_rows + 1; + } + ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM; + } + + const int max_scroll = max_ii(0, dyn_data->height - actual_rows); + CLAMP(ui_list->list_scroll, 0, max_scroll); + ui_list->list_last_len = items->tot_items; + dyn_data->visual_height = actual_rows; + r_visual_info->visual_items = actual_rows * columns; + r_visual_info->start_idx = ui_list->list_scroll * columns; + r_visual_info->end_idx = min_ii(r_visual_info->start_idx + actual_rows * columns, + items->tot_items); +} + +static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2)) +{ + uiList *ui_list = static_cast(arg1); + uiListDyn *dyn_data = ui_list->dyn_data; + + /* This way we get diff in number of additional items to show (positive) or hide (negative). */ + const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) / + (float)UI_UNIT_Y); + + if (diff != 0) { + ui_list->list_grip += diff; + dyn_data->resize_prev += diff * UI_UNIT_Y; + ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; + } + + /* In case uilist is in popup, we need special refreshing */ + ED_region_tag_refresh_ui(CTX_wm_menu(C)); +} + +static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname) +{ + if (propname && propname[0] && itemptr && itemptr->data) { + PropertyRNA *prop = RNA_struct_find_property(itemptr, propname); + + if (prop && (RNA_property_type(prop) == PROP_STRING)) { + return RNA_property_string_get_alloc(itemptr, prop, NULL, 0, NULL); + } + } + return NULL; +} + +static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip) +{ + char *dyn_tooltip = static_cast(argN); + return BLI_sprintfN("%s - %s", tip, dyn_tooltip); +} + +/** + * \note Note that \a layout_type may be NULL. + */ +static uiList *ui_list_ensure(bContext *C, + uiListType *ui_list_type, + const char *list_id, + int layout_type, + bool sort_reverse, + bool sort_lock) +{ + /* Allows to work in popups. */ + ARegion *region = CTX_wm_menu(C); + if (region == NULL) { + region = CTX_wm_region(C); + } + + /* Find or add the uiList to the current Region. */ + + char full_list_id[UI_MAX_NAME_STR]; + WM_uilisttype_to_full_list_id(ui_list_type, list_id, full_list_id); + + uiList *ui_list = static_cast( + BLI_findstring(®ion->ui_lists, full_list_id, offsetof(uiList, list_id))); + + if (!ui_list) { + ui_list = static_cast(MEM_callocN(sizeof(uiList), "uiList")); + BLI_strncpy(ui_list->list_id, full_list_id, sizeof(ui_list->list_id)); + BLI_addtail(®ion->ui_lists, ui_list); + ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ + if (sort_reverse) { + ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE; + } + if (sort_lock) { + ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK; + } + } + + if (!ui_list->dyn_data) { + ui_list->dyn_data = static_cast( + MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data")); + } + uiListDyn *dyn_data = ui_list->dyn_data; + /* Note that this isn't a `uiListType` callback, it's stored in the runtime list data. Otherwise + * the runtime data could leak when the type is unregistered (e.g. on "Reload Scripts"). */ + dyn_data->free_runtime_data_fn = uilist_free_dyn_data; + + /* Because we can't actually pass type across save&load... */ + ui_list->type = ui_list_type; + ui_list->layout_type = layout_type; + + /* Reset filtering data. */ + MEM_SAFE_FREE(dyn_data->items_filter_flags); + MEM_SAFE_FREE(dyn_data->items_filter_neworder); + dyn_data->items_len = dyn_data->items_shown = -1; + + return ui_list; +} + +static void ui_template_list_layout_draw(bContext *C, + uiList *ui_list, + uiLayout *layout, + TemplateListInputData *input_data, + TemplateListItems *items, + const TemplateListLayoutDrawData *layout_data, + const enum uiTemplateListFlags flags) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + const char *active_propname = RNA_property_identifier(input_data->activeprop); + + uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; + char numstr[32]; + int rnaicon = ICON_NONE, icon = ICON_NONE; + uiBut *but; + + uiBlock *block = uiLayoutGetBlock(layout); + + /* get icon */ + if (input_data->dataptr.data && input_data->prop) { + StructRNA *ptype = RNA_property_pointer_type(&input_data->dataptr, input_data->prop); + rnaicon = RNA_struct_ui_icon(ptype); + } + + TemplateListVisualInfo visual_info; + switch (ui_list->layout_type) { + case UILST_LAYOUT_DEFAULT: { + /* layout */ + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); + glob = uiLayoutColumn(box, true); + row = uiLayoutRow(glob, false); + col = uiLayoutColumn(row, true); + + TemplateListLayoutDrawData adjusted_layout_data = *layout_data; + adjusted_layout_data.columns = 1; + /* init numbers */ + uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); + + int i = 0; + if (input_data->dataptr.data && input_data->prop) { + /* create list items */ + for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + void *dyntip_data; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; + uiBlock *subblock = uiLayoutGetBlock(col); + + overlap = uiLayoutOverlap(col); + + UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); + + /* list item behind label & other buttons */ + uiLayoutRow(overlap, false); + + but = uiDefButR_prop(subblock, + UI_BTYPE_LISTROW, + 0, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + org_i, + 0, + 0, + TIP_("Double click to rename")); + if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, + input_data->item_dyntip_propname))) { + UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN); + } + + sub = uiLayoutRow(overlap, false); + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + if (icon == ICON_DOT) { + icon = ICON_NONE; + } + layout_data->draw_item(ui_list, + C, + sub, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); + + /* Items should be able to set context pointers for the layout. But the list-row button + * swallows events, so it needs the context storage too for handlers to see it. */ + but->context = uiLayoutGetContextStore(sub); + + /* If we are "drawing" active item, set all labels as active. */ + if (i == items->active_item_idx) { + ui_layout_list_set_labels_active(sub); + } + + UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); + } + } + + /* add dummy buttons to fill space */ + for (; i < visual_info.start_idx + visual_info.visual_items; i++) { + uiItemL(col, "", ICON_NONE); + } + + /* add scrollbar */ + if (items->tot_items > visual_info.visual_items) { + uiLayoutColumn(row, false); + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + UI_UNIT_Y * dyn_data->visual_height, + &ui_list->list_scroll, + 0, + dyn_data->height - dyn_data->visual_height, + dyn_data->visual_height, + 0, + ""); + } + } break; + case UILST_LAYOUT_COMPACT: + row = uiLayoutRow(layout, true); + + if ((input_data->dataptr.data && input_data->prop) && (dyn_data->items_shown > 0) && + (items->active_item_idx >= 0) && (items->active_item_idx < dyn_data->items_shown)) { + PointerRNA *itemptr = &items->item_vec[items->active_item_idx].item; + const int org_i = items->item_vec[items->active_item_idx].org_idx; + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + if (icon == ICON_DOT) { + icon = ICON_NONE; + } + layout_data->draw_item(ui_list, + C, + row, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + 0); + } + /* if list is empty, add in dummy button */ + else { + uiItemL(row, "", ICON_NONE); + } + + /* next/prev button */ + BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown); + but = uiDefIconTextButR_prop(block, + UI_BTYPE_NUM, + 0, + 0, + numstr, + 0, + 0, + UI_UNIT_X * 5, + UI_UNIT_Y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + 0, + 0, + 0, + ""); + if (dyn_data->items_shown == 0) { + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + break; + case UILST_LAYOUT_GRID: { + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); + glob = uiLayoutColumn(box, true); + row = uiLayoutRow(glob, false); + col = uiLayoutColumn(row, true); + subrow = NULL; /* Quite gcc warning! */ + + uilist_prepare(ui_list, items, layout_data, &visual_info); + + int i = 0; + if (input_data->dataptr.data && input_data->prop) { + /* create list items */ + for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; + + /* create button */ + if (!(i % layout_data->columns)) { + subrow = uiLayoutRow(col, false); + } + + uiBlock *subblock = uiLayoutGetBlock(subrow); + overlap = uiLayoutOverlap(subrow); + + UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); + + /* list item behind label & other buttons */ + uiLayoutRow(overlap, false); + + but = uiDefButR_prop(subblock, + UI_BTYPE_LISTROW, + 0, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + org_i, + 0, + 0, + NULL); + UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); + + sub = uiLayoutRow(overlap, false); + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + layout_data->draw_item(ui_list, + C, + sub, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); + + /* If we are "drawing" active item, set all labels as active. */ + if (i == items->active_item_idx) { + ui_layout_list_set_labels_active(sub); + } + + UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); + } + } + + /* add dummy buttons to fill space */ + for (; i < visual_info.start_idx + visual_info.visual_items; i++) { + if (!(i % layout_data->columns)) { + subrow = uiLayoutRow(col, false); + } + uiItemL(subrow, "", ICON_NONE); + } + + /* add scrollbar */ + if (items->tot_items > visual_info.visual_items) { + /* col = */ uiLayoutColumn(row, false); + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + UI_UNIT_Y * dyn_data->visual_height, + &ui_list->list_scroll, + 0, + dyn_data->height - dyn_data->visual_height, + dyn_data->visual_height, + 0, + ""); + } + break; + } + case UILST_LAYOUT_BIG_PREVIEW_GRID: + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); + /* For grip button. */ + glob = uiLayoutColumn(box, true); + /* For scrollbar. */ + row = uiLayoutRow(glob, false); + + /* TODO ED_fileselect_init_layout(). Share somehow? */ + float size_x = (96.0f / 20.0f) * UI_UNIT_X; + float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + + const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); + uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); + + TemplateListLayoutDrawData adjusted_layout_data = *layout_data; + adjusted_layout_data.columns = cols_per_row; + uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); + + if (input_data->dataptr.data && input_data->prop) { + /* create list items */ + for (int i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; + + overlap = uiLayoutOverlap(grid); + col = uiLayoutColumn(overlap, false); + + uiBlock *subblock = uiLayoutGetBlock(col); + UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); + + but = uiDefButR_prop(subblock, + UI_BTYPE_LISTROW, + 0, + "", + 0, + 0, + size_x, + size_y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + org_i, + 0, + 0, + NULL); + UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); + + col = uiLayoutColumn(overlap, false); + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + layout_data->draw_item(ui_list, + C, + col, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); + + /* Items should be able to set context pointers for the layout. But the list-row button + * swallows events, so it needs the context storage too for handlers to see it. */ + but->context = uiLayoutGetContextStore(col); + + /* If we are "drawing" active item, set all labels as active. */ + if (i == items->active_item_idx) { + ui_layout_list_set_labels_active(col); + } + + UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); + } + } + + if (items->tot_items > visual_info.visual_items) { + /* col = */ uiLayoutColumn(row, false); + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + size_y * dyn_data->visual_height, + &ui_list->list_scroll, + 0, + dyn_data->height - dyn_data->visual_height, + dyn_data->visual_height, + 0, + ""); + } + break; + } + + if (glob) { + const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0; + + /* About #UI_BTYPE_GRIP drag-resize: + * We can't directly use results from a grip button, since we have a + * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature). + * Since we *never* know whether we are grip-resizing or not + * (because there is no callback for when a button enters/leaves its "edit mode"), + * we use the fact that grip-controlled value (dyn_data->resize) is completely handled + * by the grip during the grab resize, so settings its value here has no effect at all. + * + * It is only meaningful when we are not resizing, + * in which case this gives us the correct "init drag" value. + * Note we cannot affect `dyn_data->resize_prev here`, + * since this value is not controlled by the grip! + */ + dyn_data->resize = dyn_data->resize_prev + + (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y; + + row = uiLayoutRow(glob, true); + uiBlock *subblock = uiLayoutGetBlock(row); + UI_block_emboss_set(subblock, UI_EMBOSS_NONE); + + if (ui_list->filter_flag & UILST_FLT_SHOW) { + but = uiDefIconButBitI(subblock, + UI_BTYPE_TOGGLE, + UILST_FLT_SHOW, + 0, + ICON_DISCLOSURE_TRI_DOWN, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y * 0.5f, + &(ui_list->filter_flag), + 0, + 0, + 0, + 0, + TIP_("Hide filtering options")); + UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + if (add_grip_but) { + but = uiDefIconButI(subblock, + UI_BTYPE_GRIP, + 0, + ICON_GRIP, + 0, + 0, + UI_UNIT_X * 10.0f, + UI_UNIT_Y * 0.5f, + &dyn_data->resize, + 0.0, + 0.0, + 0, + 0, + ""); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + } + + UI_block_emboss_set(subblock, UI_EMBOSS); + + col = uiLayoutColumn(glob, false); + subblock = uiLayoutGetBlock(col); + uiDefBut(subblock, + UI_BTYPE_SEPR, + 0, + "", + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y * 0.05f, + NULL, + 0.0, + 0.0, + 0, + 0, + ""); + + layout_data->draw_filter(ui_list, C, col); + } + else { + but = uiDefIconButBitI(subblock, + UI_BTYPE_TOGGLE, + UILST_FLT_SHOW, + 0, + ICON_DISCLOSURE_TRI_RIGHT, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y * 0.5f, + &(ui_list->filter_flag), + 0, + 0, + 0, + 0, + TIP_("Show filtering options")); + UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + if (add_grip_but) { + but = uiDefIconButI(subblock, + UI_BTYPE_GRIP, + 0, + ICON_GRIP, + 0, + 0, + UI_UNIT_X * 10.0f, + UI_UNIT_Y * 0.5f, + &dyn_data->resize, + 0.0, + 0.0, + 0, + 0, + ""); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + } + + UI_block_emboss_set(subblock, UI_EMBOSS); + } + } +} + +uiList *uiTemplateList_ex(uiLayout *layout, + bContext *C, + const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags, + void *customdata) +{ + TemplateListInputData input_data = {0}; + uiListType *ui_list_type; + if (!ui_template_list_data_retrieve(listtype_name, + list_id, + dataptr, + propname, + active_dataptr, + active_propname, + item_dyntip_propname, + &input_data, + &ui_list_type)) { + return NULL; + } + + uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : + uilist_draw_item_default; + uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : + uilist_draw_filter_default; + uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : + uilist_filter_items_default; + + uiList *ui_list = ui_list_ensure(C, + ui_list_type, + list_id, + layout_type, + flags & UI_TEMPLATE_LIST_SORT_REVERSE, + flags & UI_TEMPLATE_LIST_SORT_LOCK); + uiListDyn *dyn_data = ui_list->dyn_data; + + MEM_SAFE_FREE(dyn_data->customdata); + dyn_data->customdata = customdata; + + /* When active item changed since last draw, scroll to it. */ + if (input_data.active_item_idx != ui_list->list_last_activei) { + ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; + ui_list->list_last_activei = input_data.active_item_idx; + } + + TemplateListItems items; + ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items); + + TemplateListLayoutDrawData layout_data = { + .draw_item = draw_item, + .draw_filter = draw_filter, + .rows = rows, + .maxrows = maxrows, + .columns = columns, + }; + + ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data, flags); + + ui_template_list_free_items(&items); + + return ui_list; +} + +void uiTemplateList(uiLayout *layout, + bContext *C, + const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags) +{ + uiTemplateList_ex(layout, + C, + listtype_name, + list_id, + dataptr, + propname, + active_dataptr, + active_propname, + item_dyntip_propname, + rows, + maxrows, + layout_type, + columns, + flags, + NULL); +} + +/** + * \return: A RNA pointer for the operator properties. + */ +PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list, + const char *opname, + bool create_properties) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + dyn_data->custom_activate_optype = WM_operatortype_find(opname, false); + if (!dyn_data->custom_activate_optype) { + return NULL; + } + + if (create_properties) { + WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, NULL, opname); + } + + return dyn_data->custom_activate_opptr; +} + +/** + * \return: A RNA pointer for the operator properties. + */ +PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list, + const char *opname, + bool create_properties) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + dyn_data->custom_drag_optype = WM_operatortype_find(opname, false); + if (!dyn_data->custom_drag_optype) { + return NULL; + } + + if (create_properties) { + WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, NULL, opname); + } + + return dyn_data->custom_drag_opptr; +} + +/* -------------------------------------------------------------------- */ + +/** \name List-types Registration + * \{ */ + void ED_uilisttypes_ui(void) { WM_uilisttype_add(UI_UL_asset_view()); } + +/** \} */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 548852b407e..766840909cc 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -5644,1261 +5644,6 @@ void uiTemplateLayers(uiLayout *layout, /** \} */ -/* -------------------------------------------------------------------- */ -/** \name List Template - * \{ */ - -static void uilist_draw_item_default(struct uiList *ui_list, - struct bContext *UNUSED(C), - struct uiLayout *layout, - struct PointerRNA *UNUSED(dataptr), - struct PointerRNA *itemptr, - int icon, - struct PointerRNA *UNUSED(active_dataptr), - const char *UNUSED(active_propname), - int UNUSED(index), - int UNUSED(flt_flag)) -{ - PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type); - - /* Simplest one! */ - switch (ui_list->layout_type) { - case UILST_LAYOUT_GRID: - uiItemL(layout, "", icon); - break; - case UILST_LAYOUT_DEFAULT: - case UILST_LAYOUT_COMPACT: - default: - if (nameprop) { - uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon); - } - else { - uiItemL(layout, "", icon); - } - break; - } -} - -static void uilist_draw_filter_default(struct uiList *ui_list, - struct bContext *UNUSED(C), - struct uiLayout *layout) -{ - PointerRNA listptr; - RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr); - - uiLayout *row = uiLayoutRow(layout, false); - - uiLayout *subrow = uiLayoutRow(row, true); - uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE); - uiItemR(subrow, - &listptr, - "use_filter_invert", - UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, - "", - ICON_ARROW_LEFTRIGHT); - - if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) { - subrow = uiLayoutRow(row, true); - uiItemR(subrow, - &listptr, - "use_filter_sort_alpha", - UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, - "", - ICON_NONE); - uiItemR(subrow, - &listptr, - "use_filter_sort_reverse", - UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, - "", - (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC); - } -} - -typedef struct { - char name[MAX_IDPROP_NAME]; - int org_idx; -} StringCmp; - -static int cmpstringp(const void *p1, const void *p2) -{ - /* Case-insensitive comparison. */ - return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name); -} - -static void uilist_filter_items_default(struct uiList *ui_list, - struct bContext *UNUSED(C), - struct PointerRNA *dataptr, - const char *propname) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - PropertyRNA *prop = RNA_struct_find_property(dataptr, propname); - - const char *filter_raw = ui_list->filter_byname; - char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL; - const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; - const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == - UILST_FLT_SORT_ALPHA; - const int len = RNA_property_collection_length(dataptr, prop); - - dyn_data->items_shown = dyn_data->items_len = len; - - if (len && (order_by_name || filter_raw[0])) { - StringCmp *names = NULL; - int order_idx = 0, i = 0; - - if (order_by_name) { - names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp"); - } - if (filter_raw[0]) { - const size_t slen = strlen(filter_raw); - - dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags"); - dyn_data->items_shown = 0; - - /* Implicitly add heading/trailing wildcards if needed. */ - if (slen + 3 <= sizeof(filter_buff)) { - filter = filter_buff; - } - else { - filter = filter_dyn = MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn"); - } - BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3); - } - - RNA_PROP_BEGIN (dataptr, itemptr, prop) { - bool do_order = false; - - char *namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); - const char *name = namebuf ? namebuf : ""; - - if (filter[0]) { - /* Case-insensitive! */ - if (fnmatch(filter, name, FNM_CASEFOLD) == 0) { - dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; - if (!filter_exclude) { - dyn_data->items_shown++; - do_order = order_by_name; - } - // printf("%s: '%s' matches '%s'\n", __func__, name, filter); - } - else if (filter_exclude) { - dyn_data->items_shown++; - do_order = order_by_name; - } - } - else { - do_order = order_by_name; - } - - if (do_order) { - names[order_idx].org_idx = order_idx; - BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME); - } - - /* free name */ - if (namebuf) { - MEM_freeN(namebuf); - } - i++; - } - RNA_PROP_END; - - if (order_by_name) { - int new_idx; - /* NOTE: order_idx equals either to ui_list->items_len if no filtering done, - * or to ui_list->items_shown if filter is enabled, - * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded. - * This way, we only sort items we actually intend to draw! - */ - qsort(names, order_idx, sizeof(StringCmp), cmpstringp); - - dyn_data->items_filter_neworder = MEM_mallocN(sizeof(int) * order_idx, - "items_filter_neworder"); - for (new_idx = 0; new_idx < order_idx; new_idx++) { - dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx; - } - } - - if (filter_dyn) { - MEM_freeN(filter_dyn); - } - if (names) { - MEM_freeN(names); - } - } -} - -static void uilist_free_dyn_data(uiList *ui_list) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - if (!dyn_data) { - return; - } - - if (dyn_data->custom_activate_opptr) { - WM_operator_properties_free(dyn_data->custom_activate_opptr); - MEM_freeN(dyn_data->custom_activate_opptr); - } - if (dyn_data->custom_drag_opptr) { - WM_operator_properties_free(dyn_data->custom_drag_opptr); - MEM_freeN(dyn_data->custom_drag_opptr); - } - - MEM_SAFE_FREE(dyn_data->items_filter_flags); - MEM_SAFE_FREE(dyn_data->items_filter_neworder); - MEM_SAFE_FREE(dyn_data->customdata); -} - -/** - * The validated data that was passed to #uiTemplateList (typically through Python). - * Populated through #ui_template_list_data_retrieve(). - */ -typedef struct { - PointerRNA dataptr; - PropertyRNA *prop; - PointerRNA active_dataptr; - PropertyRNA *activeprop; - const char *item_dyntip_propname; - - /* Index as stored in the input property. I.e. the index before sorting. */ - int active_item_idx; -} TemplateListInputData; - -/** - * Internal wrapper for a single item in the list (well, actually stored as a vector). - */ -typedef struct { - PointerRNA item; - int org_idx; - int flt_flag; -} _uilist_item; - -/** - * Container for the item vector and additional info. - */ -typedef struct { - _uilist_item *item_vec; - /* Index of the active item following visual order. I.e. unlike - * TemplateListInputData.active_item_idx, this is the index after sorting. */ - int active_item_idx; - int tot_items; -} TemplateListItems; - -typedef struct { - uiListDrawItemFunc draw_item; - uiListDrawFilterFunc draw_filter; - - int rows; - int maxrows; - int columns; -} TemplateListLayoutDrawData; - -typedef struct { - int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ - int start_idx; /* Index of first item to display. */ - int end_idx; /* Index of last item to display + 1. */ -} TemplateListVisualInfo; - -/** - * Validate input parameters and initialize \a r_data from that. Plus find the list-type and return - * it in \a r_list_type. - * - * \return false if the input data isn't valid. Will also raise an RNA warning in that case. - */ -static bool ui_template_list_data_retrieve(const char *listtype_name, - const char *list_id, - PointerRNA *dataptr, - const char *propname, - PointerRNA *active_dataptr, - const char *active_propname, - const char *item_dyntip_propname, - TemplateListInputData *r_input_data, - uiListType **r_list_type) -{ - memset(r_input_data, 0, sizeof(*r_input_data)); - - /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ - if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { - RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", - UI_UL_DEFAULT_CLASS_NAME); - return false; - } - - if (!active_dataptr->data) { - RNA_warning("No active data"); - return false; - } - - r_input_data->dataptr = *dataptr; - if (dataptr->data) { - r_input_data->prop = RNA_struct_find_property(dataptr, propname); - if (!r_input_data->prop) { - RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname); - return false; - } - } - - r_input_data->active_dataptr = *active_dataptr; - r_input_data->activeprop = RNA_struct_find_property(active_dataptr, active_propname); - if (!r_input_data->activeprop) { - RNA_warning( - "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname); - return false; - } - - if (r_input_data->prop) { - const PropertyType type = RNA_property_type(r_input_data->prop); - if (type != PROP_COLLECTION) { - RNA_warning("Expected a collection data property"); - return false; - } - } - - const PropertyType activetype = RNA_property_type(r_input_data->activeprop); - if (activetype != PROP_INT) { - RNA_warning("Expected an integer active data property"); - return false; - } - - /* Find the uiList type. */ - if (!(*r_list_type = WM_uilisttype_find(listtype_name, false))) { - RNA_warning("List type %s not found", listtype_name); - return false; - } - - r_input_data->active_item_idx = RNA_property_int_get(&r_input_data->active_dataptr, - r_input_data->activeprop); - r_input_data->item_dyntip_propname = item_dyntip_propname; - - return true; -} - -static void ui_template_list_collect_items(PointerRNA *list_ptr, - PropertyRNA *list_prop, - uiListDyn *dyn_data, - int filter_exclude, - bool order_reverse, - int activei, - TemplateListItems *r_items) -{ - int i = 0; - int reorder_i = 0; - bool activei_mapping_pending = true; - - RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) { - if (!dyn_data->items_filter_flags || - ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { - int new_order_idx; - if (dyn_data->items_filter_neworder) { - new_order_idx = dyn_data->items_filter_neworder[reorder_i++]; - new_order_idx = order_reverse ? dyn_data->items_shown - new_order_idx - 1 : new_order_idx; - } - else { - new_order_idx = order_reverse ? dyn_data->items_shown - ++reorder_i : reorder_i++; - } - // printf("%s: ii: %d\n", __func__, ii); - r_items->item_vec[new_order_idx].item = itemptr; - r_items->item_vec[new_order_idx].org_idx = i; - r_items->item_vec[new_order_idx].flt_flag = dyn_data->items_filter_flags ? - dyn_data->items_filter_flags[i] : - 0; - - if (activei_mapping_pending && activei == i) { - activei = new_order_idx; - /* So that we do not map again activei! */ - activei_mapping_pending = false; - } -#if 0 /* For now, do not alter active element, even if it will be hidden... */ - else if (activei < i) { - /* We do not want an active but invisible item! - * Only exception is when all items are filtered out... - */ - if (prev_order_idx >= 0) { - activei = prev_order_idx; - RNA_property_int_set(active_dataptr, activeprop, prev_i); - } - else { - activei = new_order_idx; - RNA_property_int_set(active_dataptr, activeprop, i); - } - } - prev_i = i; - prev_ii = new_order_idx; -#endif - } - i++; - } - RNA_PROP_END; - - /* If mapping is still pending, no active item was found. Mark as invalid (-1) */ - r_items->active_item_idx = activei_mapping_pending ? -1 : activei; -} - -/** - * Create the UI-list representation of the list items, sorted and filtered if needed. - */ -static void ui_template_list_collect_display_items(bContext *C, - uiList *ui_list, - TemplateListInputData *input_data, - const uiListFilterItemsFunc filter_items_fn, - TemplateListItems *r_items) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - memset(r_items, 0, sizeof(*r_items)); - - /* Filter list items! (not for compact layout, though) */ - if (input_data->dataptr.data && input_data->prop) { - const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; - const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; - int items_shown; -#if 0 - int prev_ii = -1, prev_i; -#endif - - if (ui_list->layout_type == UILST_LAYOUT_COMPACT) { - dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length( - &input_data->dataptr, input_data->prop); - } - else { - // printf("%s: filtering...\n", __func__); - filter_items_fn(ui_list, C, &input_data->dataptr, RNA_property_identifier(input_data->prop)); - // printf("%s: filtering done.\n", __func__); - } - - items_shown = dyn_data->items_shown; - if (items_shown >= 0) { - r_items->item_vec = MEM_mallocN(sizeof(*r_items->item_vec) * items_shown, __func__); - // printf("%s: items shown: %d.\n", __func__, items_shown); - - ui_template_list_collect_items(&input_data->dataptr, - input_data->prop, - dyn_data, - filter_exclude, - order_reverse, - input_data->active_item_idx, - r_items); - } - if (dyn_data->items_shown >= 0) { - r_items->tot_items = dyn_data->items_shown; - } - else { - r_items->tot_items = dyn_data->items_len; - } - } -} - -static void ui_template_list_free_items(TemplateListItems *items) -{ - if (items->item_vec) { - MEM_freeN(items->item_vec); - } -} - -static void uilist_prepare(uiList *ui_list, - const TemplateListItems *items, - const TemplateListLayoutDrawData *layout_data, - TemplateListVisualInfo *r_visual_info) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - const bool use_auto_size = (ui_list->list_grip < - (layout_data->rows - UI_LIST_AUTO_SIZE_THRESHOLD)); - - int actual_rows = layout_data->rows; - int actual_maxrows = layout_data->maxrows; - int columns = layout_data->columns; - - /* default rows */ - if (actual_rows <= 0) { - actual_rows = 5; - } - dyn_data->visual_height_min = actual_rows; - if (actual_maxrows < actual_rows) { - actual_maxrows = max_ii(actual_rows, 5); - } - if (columns <= 0) { - columns = 9; - } - - int activei_row; - if (columns > 1) { - dyn_data->height = (int)ceil((double)items->tot_items / (double)columns); - activei_row = (int)floor((double)items->active_item_idx / (double)columns); - } - else { - dyn_data->height = items->tot_items; - activei_row = items->active_item_idx; - } - - dyn_data->columns = columns; - - if (!use_auto_size) { - /* No auto-size, yet we clamp at min size! */ - actual_rows = max_ii(ui_list->list_grip, actual_rows); - } - else if ((actual_rows != actual_maxrows) && (dyn_data->height > actual_rows)) { - /* Expand size if needed and possible. */ - actual_rows = min_ii(dyn_data->height, actual_maxrows); - } - - /* If list length changes or list is tagged to check this, - * and active is out of view, scroll to it. */ - if ((ui_list->list_last_len != items->tot_items) || - (ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM)) { - if (activei_row < ui_list->list_scroll) { - ui_list->list_scroll = activei_row; - } - else if (activei_row >= ui_list->list_scroll + actual_rows) { - ui_list->list_scroll = activei_row - actual_rows + 1; - } - ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM; - } - - const int max_scroll = max_ii(0, dyn_data->height - actual_rows); - CLAMP(ui_list->list_scroll, 0, max_scroll); - ui_list->list_last_len = items->tot_items; - dyn_data->visual_height = actual_rows; - r_visual_info->visual_items = actual_rows * columns; - r_visual_info->start_idx = ui_list->list_scroll * columns; - r_visual_info->end_idx = min_ii(r_visual_info->start_idx + actual_rows * columns, - items->tot_items); -} - -static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2)) -{ - uiList *ui_list = arg1; - uiListDyn *dyn_data = ui_list->dyn_data; - - /* This way we get diff in number of additional items to show (positive) or hide (negative). */ - const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) / - (float)UI_UNIT_Y); - - if (diff != 0) { - ui_list->list_grip += diff; - dyn_data->resize_prev += diff * UI_UNIT_Y; - ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; - } - - /* In case uilist is in popup, we need special refreshing */ - ED_region_tag_refresh_ui(CTX_wm_menu(C)); -} - -static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname) -{ - if (propname && propname[0] && itemptr && itemptr->data) { - PropertyRNA *prop = RNA_struct_find_property(itemptr, propname); - - if (prop && (RNA_property_type(prop) == PROP_STRING)) { - return RNA_property_string_get_alloc(itemptr, prop, NULL, 0, NULL); - } - } - return NULL; -} - -static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip) -{ - char *dyn_tooltip = argN; - return BLI_sprintfN("%s - %s", tip, dyn_tooltip); -} - -/** - * \note Note that \a layout_type may be NULL. - */ -static uiList *ui_list_ensure(bContext *C, - uiListType *ui_list_type, - const char *list_id, - int layout_type, - bool sort_reverse, - bool sort_lock) -{ - /* Allows to work in popups. */ - ARegion *region = CTX_wm_menu(C); - if (region == NULL) { - region = CTX_wm_region(C); - } - - /* Find or add the uiList to the current Region. */ - - char full_list_id[UI_MAX_NAME_STR]; - WM_uilisttype_to_full_list_id(ui_list_type, list_id, full_list_id); - - uiList *ui_list = BLI_findstring(®ion->ui_lists, full_list_id, offsetof(uiList, list_id)); - - if (!ui_list) { - ui_list = MEM_callocN(sizeof(uiList), "uiList"); - BLI_strncpy(ui_list->list_id, full_list_id, sizeof(ui_list->list_id)); - BLI_addtail(®ion->ui_lists, ui_list); - ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ - if (sort_reverse) { - ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE; - } - if (sort_lock) { - ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK; - } - } - - if (!ui_list->dyn_data) { - ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data"); - } - uiListDyn *dyn_data = ui_list->dyn_data; - /* Note that this isn't a `uiListType` callback, it's stored in the runtime list data. Otherwise - * the runtime data could leak when the type is unregistered (e.g. on "Reload Scripts"). */ - dyn_data->free_runtime_data_fn = uilist_free_dyn_data; - - /* Because we can't actually pass type across save&load... */ - ui_list->type = ui_list_type; - ui_list->layout_type = layout_type; - - /* Reset filtering data. */ - MEM_SAFE_FREE(dyn_data->items_filter_flags); - MEM_SAFE_FREE(dyn_data->items_filter_neworder); - dyn_data->items_len = dyn_data->items_shown = -1; - - return ui_list; -} - -static void ui_template_list_layout_draw(bContext *C, - uiList *ui_list, - uiLayout *layout, - TemplateListInputData *input_data, - TemplateListItems *items, - const TemplateListLayoutDrawData *layout_data, - const enum uiTemplateListFlags flags) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - const char *active_propname = RNA_property_identifier(input_data->activeprop); - - uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; - char numstr[32]; - int rnaicon = ICON_NONE, icon = ICON_NONE; - uiBut *but; - - uiBlock *block = uiLayoutGetBlock(layout); - - /* get icon */ - if (input_data->dataptr.data && input_data->prop) { - StructRNA *ptype = RNA_property_pointer_type(&input_data->dataptr, input_data->prop); - rnaicon = RNA_struct_ui_icon(ptype); - } - - TemplateListVisualInfo visual_info; - switch (ui_list->layout_type) { - case UILST_LAYOUT_DEFAULT: { - /* layout */ - box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); - glob = uiLayoutColumn(box, true); - row = uiLayoutRow(glob, false); - col = uiLayoutColumn(row, true); - - TemplateListLayoutDrawData adjusted_layout_data = *layout_data; - adjusted_layout_data.columns = 1; - /* init numbers */ - uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); - - int i = 0; - if (input_data->dataptr.data && input_data->prop) { - /* create list items */ - for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { - PointerRNA *itemptr = &items->item_vec[i].item; - void *dyntip_data; - const int org_i = items->item_vec[i].org_idx; - const int flt_flag = items->item_vec[i].flt_flag; - uiBlock *subblock = uiLayoutGetBlock(col); - - overlap = uiLayoutOverlap(col); - - UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); - - /* list item behind label & other buttons */ - uiLayoutRow(overlap, false); - - but = uiDefButR_prop(subblock, - UI_BTYPE_LISTROW, - 0, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - &input_data->active_dataptr, - input_data->activeprop, - 0, - 0, - org_i, - 0, - 0, - TIP_("Double click to rename")); - if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, - input_data->item_dyntip_propname))) { - UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN); - } - - sub = uiLayoutRow(overlap, false); - - icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - if (icon == ICON_DOT) { - icon = ICON_NONE; - } - layout_data->draw_item(ui_list, - C, - sub, - &input_data->dataptr, - itemptr, - icon, - &input_data->active_dataptr, - active_propname, - org_i, - flt_flag); - - /* Items should be able to set context pointers for the layout. But the list-row button - * swallows events, so it needs the context storage too for handlers to see it. */ - but->context = uiLayoutGetContextStore(sub); - - /* If we are "drawing" active item, set all labels as active. */ - if (i == items->active_item_idx) { - ui_layout_list_set_labels_active(sub); - } - - UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); - } - } - - /* add dummy buttons to fill space */ - for (; i < visual_info.start_idx + visual_info.visual_items; i++) { - uiItemL(col, "", ICON_NONE); - } - - /* add scrollbar */ - if (items->tot_items > visual_info.visual_items) { - uiLayoutColumn(row, false); - uiDefButI(block, - UI_BTYPE_SCROLL, - 0, - "", - 0, - 0, - V2D_SCROLL_WIDTH, - UI_UNIT_Y * dyn_data->visual_height, - &ui_list->list_scroll, - 0, - dyn_data->height - dyn_data->visual_height, - dyn_data->visual_height, - 0, - ""); - } - } break; - case UILST_LAYOUT_COMPACT: - row = uiLayoutRow(layout, true); - - if ((input_data->dataptr.data && input_data->prop) && (dyn_data->items_shown > 0) && - (items->active_item_idx >= 0) && (items->active_item_idx < dyn_data->items_shown)) { - PointerRNA *itemptr = &items->item_vec[items->active_item_idx].item; - const int org_i = items->item_vec[items->active_item_idx].org_idx; - - icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - if (icon == ICON_DOT) { - icon = ICON_NONE; - } - layout_data->draw_item(ui_list, - C, - row, - &input_data->dataptr, - itemptr, - icon, - &input_data->active_dataptr, - active_propname, - org_i, - 0); - } - /* if list is empty, add in dummy button */ - else { - uiItemL(row, "", ICON_NONE); - } - - /* next/prev button */ - BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown); - but = uiDefIconTextButR_prop(block, - UI_BTYPE_NUM, - 0, - 0, - numstr, - 0, - 0, - UI_UNIT_X * 5, - UI_UNIT_Y, - &input_data->active_dataptr, - input_data->activeprop, - 0, - 0, - 0, - 0, - 0, - ""); - if (dyn_data->items_shown == 0) { - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - break; - case UILST_LAYOUT_GRID: { - box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); - glob = uiLayoutColumn(box, true); - row = uiLayoutRow(glob, false); - col = uiLayoutColumn(row, true); - subrow = NULL; /* Quite gcc warning! */ - - uilist_prepare(ui_list, items, layout_data, &visual_info); - - int i = 0; - if (input_data->dataptr.data && input_data->prop) { - /* create list items */ - for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { - PointerRNA *itemptr = &items->item_vec[i].item; - const int org_i = items->item_vec[i].org_idx; - const int flt_flag = items->item_vec[i].flt_flag; - - /* create button */ - if (!(i % layout_data->columns)) { - subrow = uiLayoutRow(col, false); - } - - uiBlock *subblock = uiLayoutGetBlock(subrow); - overlap = uiLayoutOverlap(subrow); - - UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); - - /* list item behind label & other buttons */ - uiLayoutRow(overlap, false); - - but = uiDefButR_prop(subblock, - UI_BTYPE_LISTROW, - 0, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - &input_data->active_dataptr, - input_data->activeprop, - 0, - 0, - org_i, - 0, - 0, - NULL); - UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); - - sub = uiLayoutRow(overlap, false); - - icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - layout_data->draw_item(ui_list, - C, - sub, - &input_data->dataptr, - itemptr, - icon, - &input_data->active_dataptr, - active_propname, - org_i, - flt_flag); - - /* If we are "drawing" active item, set all labels as active. */ - if (i == items->active_item_idx) { - ui_layout_list_set_labels_active(sub); - } - - UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); - } - } - - /* add dummy buttons to fill space */ - for (; i < visual_info.start_idx + visual_info.visual_items; i++) { - if (!(i % layout_data->columns)) { - subrow = uiLayoutRow(col, false); - } - uiItemL(subrow, "", ICON_NONE); - } - - /* add scrollbar */ - if (items->tot_items > visual_info.visual_items) { - /* col = */ uiLayoutColumn(row, false); - uiDefButI(block, - UI_BTYPE_SCROLL, - 0, - "", - 0, - 0, - V2D_SCROLL_WIDTH, - UI_UNIT_Y * dyn_data->visual_height, - &ui_list->list_scroll, - 0, - dyn_data->height - dyn_data->visual_height, - dyn_data->visual_height, - 0, - ""); - } - break; - } - case UILST_LAYOUT_BIG_PREVIEW_GRID: - box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); - /* For grip button. */ - glob = uiLayoutColumn(box, true); - /* For scrollbar. */ - row = uiLayoutRow(glob, false); - - /* TODO ED_fileselect_init_layout(). Share somehow? */ - float size_x = (96.0f / 20.0f) * UI_UNIT_X; - float size_y = (96.0f / 20.0f) * UI_UNIT_Y; - - const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); - uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); - - TemplateListLayoutDrawData adjusted_layout_data = *layout_data; - adjusted_layout_data.columns = cols_per_row; - uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); - - if (input_data->dataptr.data && input_data->prop) { - /* create list items */ - for (int i = visual_info.start_idx; i < visual_info.end_idx; i++) { - PointerRNA *itemptr = &items->item_vec[i].item; - const int org_i = items->item_vec[i].org_idx; - const int flt_flag = items->item_vec[i].flt_flag; - - overlap = uiLayoutOverlap(grid); - col = uiLayoutColumn(overlap, false); - - uiBlock *subblock = uiLayoutGetBlock(col); - UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); - - but = uiDefButR_prop(subblock, - UI_BTYPE_LISTROW, - 0, - "", - 0, - 0, - size_x, - size_y, - &input_data->active_dataptr, - input_data->activeprop, - 0, - 0, - org_i, - 0, - 0, - NULL); - UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); - - col = uiLayoutColumn(overlap, false); - - icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - layout_data->draw_item(ui_list, - C, - col, - &input_data->dataptr, - itemptr, - icon, - &input_data->active_dataptr, - active_propname, - org_i, - flt_flag); - - /* Items should be able to set context pointers for the layout. But the list-row button - * swallows events, so it needs the context storage too for handlers to see it. */ - but->context = uiLayoutGetContextStore(col); - - /* If we are "drawing" active item, set all labels as active. */ - if (i == items->active_item_idx) { - ui_layout_list_set_labels_active(col); - } - - UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); - } - } - - if (items->tot_items > visual_info.visual_items) { - /* col = */ uiLayoutColumn(row, false); - uiDefButI(block, - UI_BTYPE_SCROLL, - 0, - "", - 0, - 0, - V2D_SCROLL_WIDTH, - size_y * dyn_data->visual_height, - &ui_list->list_scroll, - 0, - dyn_data->height - dyn_data->visual_height, - dyn_data->visual_height, - 0, - ""); - } - break; - } - - if (glob) { - const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0; - - /* About #UI_BTYPE_GRIP drag-resize: - * We can't directly use results from a grip button, since we have a - * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature). - * Since we *never* know whether we are grip-resizing or not - * (because there is no callback for when a button enters/leaves its "edit mode"), - * we use the fact that grip-controlled value (dyn_data->resize) is completely handled - * by the grip during the grab resize, so settings its value here has no effect at all. - * - * It is only meaningful when we are not resizing, - * in which case this gives us the correct "init drag" value. - * Note we cannot affect `dyn_data->resize_prev here`, - * since this value is not controlled by the grip! - */ - dyn_data->resize = dyn_data->resize_prev + - (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y; - - row = uiLayoutRow(glob, true); - uiBlock *subblock = uiLayoutGetBlock(row); - UI_block_emboss_set(subblock, UI_EMBOSS_NONE); - - if (ui_list->filter_flag & UILST_FLT_SHOW) { - but = uiDefIconButBitI(subblock, - UI_BTYPE_TOGGLE, - UILST_FLT_SHOW, - 0, - ICON_DISCLOSURE_TRI_DOWN, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y * 0.5f, - &(ui_list->filter_flag), - 0, - 0, - 0, - 0, - TIP_("Hide filtering options")); - UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ - - if (add_grip_but) { - but = uiDefIconButI(subblock, - UI_BTYPE_GRIP, - 0, - ICON_GRIP, - 0, - 0, - UI_UNIT_X * 10.0f, - UI_UNIT_Y * 0.5f, - &dyn_data->resize, - 0.0, - 0.0, - 0, - 0, - ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); - } - - UI_block_emboss_set(subblock, UI_EMBOSS); - - col = uiLayoutColumn(glob, false); - subblock = uiLayoutGetBlock(col); - uiDefBut(subblock, - UI_BTYPE_SEPR, - 0, - "", - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y * 0.05f, - NULL, - 0.0, - 0.0, - 0, - 0, - ""); - - layout_data->draw_filter(ui_list, C, col); - } - else { - but = uiDefIconButBitI(subblock, - UI_BTYPE_TOGGLE, - UILST_FLT_SHOW, - 0, - ICON_DISCLOSURE_TRI_RIGHT, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y * 0.5f, - &(ui_list->filter_flag), - 0, - 0, - 0, - 0, - TIP_("Show filtering options")); - UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ - - if (add_grip_but) { - but = uiDefIconButI(subblock, - UI_BTYPE_GRIP, - 0, - ICON_GRIP, - 0, - 0, - UI_UNIT_X * 10.0f, - UI_UNIT_Y * 0.5f, - &dyn_data->resize, - 0.0, - 0.0, - 0, - 0, - ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); - } - - UI_block_emboss_set(subblock, UI_EMBOSS); - } - } -} - -uiList *uiTemplateList_ex(uiLayout *layout, - bContext *C, - const char *listtype_name, - const char *list_id, - PointerRNA *dataptr, - const char *propname, - PointerRNA *active_dataptr, - const char *active_propname, - const char *item_dyntip_propname, - int rows, - int maxrows, - int layout_type, - int columns, - enum uiTemplateListFlags flags, - void *customdata) -{ - TemplateListInputData input_data = {0}; - uiListType *ui_list_type; - if (!ui_template_list_data_retrieve(listtype_name, - list_id, - dataptr, - propname, - active_dataptr, - active_propname, - item_dyntip_propname, - &input_data, - &ui_list_type)) { - return NULL; - } - - uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : - uilist_draw_item_default; - uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : - uilist_draw_filter_default; - uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : - uilist_filter_items_default; - - uiList *ui_list = ui_list_ensure(C, - ui_list_type, - list_id, - layout_type, - flags & UI_TEMPLATE_LIST_SORT_REVERSE, - flags & UI_TEMPLATE_LIST_SORT_LOCK); - uiListDyn *dyn_data = ui_list->dyn_data; - - MEM_SAFE_FREE(dyn_data->customdata); - dyn_data->customdata = customdata; - - /* When active item changed since last draw, scroll to it. */ - if (input_data.active_item_idx != ui_list->list_last_activei) { - ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; - ui_list->list_last_activei = input_data.active_item_idx; - } - - TemplateListItems items; - ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items); - - TemplateListLayoutDrawData layout_data = { - .draw_item = draw_item, - .draw_filter = draw_filter, - .rows = rows, - .maxrows = maxrows, - .columns = columns, - }; - - ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data, flags); - - ui_template_list_free_items(&items); - - return ui_list; -} - -void uiTemplateList(uiLayout *layout, - bContext *C, - const char *listtype_name, - const char *list_id, - PointerRNA *dataptr, - const char *propname, - PointerRNA *active_dataptr, - const char *active_propname, - const char *item_dyntip_propname, - int rows, - int maxrows, - int layout_type, - int columns, - enum uiTemplateListFlags flags) -{ - uiTemplateList_ex(layout, - C, - listtype_name, - list_id, - dataptr, - propname, - active_dataptr, - active_propname, - item_dyntip_propname, - rows, - maxrows, - layout_type, - columns, - flags, - NULL); -} - -/** - * \return: A RNA pointer for the operator properties. - */ -PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list, - const char *opname, - bool create_properties) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - dyn_data->custom_activate_optype = WM_operatortype_find(opname, false); - if (!dyn_data->custom_activate_optype) { - return NULL; - } - - if (create_properties) { - WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, NULL, opname); - } - - return dyn_data->custom_activate_opptr; -} - -/** - * \return: A RNA pointer for the operator properties. - */ -PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list, - const char *opname, - bool create_properties) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - dyn_data->custom_drag_optype = WM_operatortype_find(opname, false); - if (!dyn_data->custom_drag_optype) { - return NULL; - } - - if (create_properties) { - WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, NULL, opname); - } - - return dyn_data->custom_drag_opptr; -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Running Jobs Template * \{ */ -- cgit v1.2.3 From a566dc25aacde568194e202a7d75ea26b948a4ce Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 15 Jul 2021 13:36:40 -0400 Subject: Cleanup: Remove use of designated initializers in C++ code Does not compile on Windows. --- source/blender/editors/interface/interface_template_list.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index dfbf64b79af..45f848ed96f 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -1211,13 +1211,12 @@ uiList *uiTemplateList_ex(uiLayout *layout, TemplateListItems items; ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items); - TemplateListLayoutDrawData layout_data = { - .draw_item = draw_item, - .draw_filter = draw_filter, - .rows = rows, - .maxrows = maxrows, - .columns = columns, - }; + TemplateListLayoutDrawData layout_data; + layout_data.draw_item = draw_item; + layout_data.draw_filter = draw_filter; + layout_data.rows = rows; + layout_data.maxrows = maxrows; + layout_data.columns = columns; ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data, flags); -- cgit v1.2.3 From 7d618b4dde6700a457e3738a1e17d58c43f4c6bb Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 15 Jul 2021 13:40:30 -0400 Subject: Cleanup: Clang tidy --- .../editors/interface/interface_template_list.cc | 68 +++++++++++----------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 45f848ed96f..eaab33e32c9 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -48,7 +48,7 @@ * The validated data that was passed to #uiTemplateList (typically through Python). * Populated through #ui_template_list_data_retrieve(). */ -typedef struct { +struct TemplateListInputData { PointerRNA dataptr; PropertyRNA *prop; PointerRNA active_dataptr; @@ -57,42 +57,42 @@ typedef struct { /* Index as stored in the input property. I.e. the index before sorting. */ int active_item_idx; -} TemplateListInputData; +}; /** * Internal wrapper for a single item in the list (well, actually stored as a vector). */ -typedef struct { +struct _uilist_item { PointerRNA item; int org_idx; int flt_flag; -} _uilist_item; +}; /** * Container for the item vector and additional info. */ -typedef struct { +struct TemplateListItems { _uilist_item *item_vec; /* Index of the active item following visual order. I.e. unlike * TemplateListInputData.active_item_idx, this is the index after sorting. */ int active_item_idx; int tot_items; -} TemplateListItems; +}; -typedef struct { +struct TemplateListLayoutDrawData { uiListDrawItemFunc draw_item; uiListDrawFilterFunc draw_filter; int rows; int maxrows; int columns; -} TemplateListLayoutDrawData; +}; -typedef struct { +struct TemplateListVisualInfo { int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ int start_idx; /* Index of first item to display. */ int end_idx; /* Index of last item to display + 1. */ -} TemplateListVisualInfo; +}; static void uilist_draw_item_default(struct uiList *ui_list, struct bContext *UNUSED(C), @@ -130,7 +130,7 @@ static void uilist_draw_filter_default(struct uiList *ui_list, struct uiLayout *layout) { PointerRNA listptr; - RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr); + RNA_pointer_create(nullptr, &RNA_UIList, ui_list, &listptr); uiLayout *row = uiLayoutRow(layout, false); @@ -160,10 +160,10 @@ static void uilist_draw_filter_default(struct uiList *ui_list, } } -typedef struct { +struct StringCmp { char name[MAX_IDPROP_NAME]; int org_idx; -} StringCmp; +}; static int cmpstringp(const void *p1, const void *p2) { @@ -180,7 +180,7 @@ static void uilist_filter_items_default(struct uiList *ui_list, PropertyRNA *prop = RNA_struct_find_property(dataptr, propname); const char *filter_raw = ui_list->filter_byname; - char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL; + char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = nullptr; const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == UILST_FLT_SORT_ALPHA; @@ -189,7 +189,7 @@ static void uilist_filter_items_default(struct uiList *ui_list, dyn_data->items_shown = dyn_data->items_len = len; if (len && (order_by_name || filter_raw[0])) { - StringCmp *names = NULL; + StringCmp *names = nullptr; int order_idx = 0, i = 0; if (order_by_name) { @@ -216,7 +216,7 @@ static void uilist_filter_items_default(struct uiList *ui_list, RNA_PROP_BEGIN (dataptr, itemptr, prop) { bool do_order = false; - char *namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); + char *namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr); const char *name = namebuf ? namebuf : ""; if (filter[0]) { @@ -587,10 +587,10 @@ static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *pr PropertyRNA *prop = RNA_struct_find_property(itemptr, propname); if (prop && (RNA_property_type(prop) == PROP_STRING)) { - return RNA_property_string_get_alloc(itemptr, prop, NULL, 0, NULL); + return RNA_property_string_get_alloc(itemptr, prop, nullptr, 0, nullptr); } } - return NULL; + return nullptr; } static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip) @@ -600,7 +600,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha } /** - * \note Note that \a layout_type may be NULL. + * \note Note that \a layout_type may be null. */ static uiList *ui_list_ensure(bContext *C, uiListType *ui_list_type, @@ -611,7 +611,7 @@ static uiList *ui_list_ensure(bContext *C, { /* Allows to work in popups. */ ARegion *region = CTX_wm_menu(C); - if (region == NULL) { + if (region == nullptr) { region = CTX_wm_region(C); } @@ -668,7 +668,7 @@ static void ui_template_list_layout_draw(bContext *C, uiListDyn *dyn_data = ui_list->dyn_data; const char *active_propname = RNA_property_identifier(input_data->activeprop); - uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; + uiLayout *glob = nullptr, *box, *row, *col, *subrow, *sub, *overlap; char numstr[32]; int rnaicon = ICON_NONE, icon = ICON_NONE; uiBut *but; @@ -843,7 +843,7 @@ static void ui_template_list_layout_draw(bContext *C, glob = uiLayoutColumn(box, true); row = uiLayoutRow(glob, false); col = uiLayoutColumn(row, true); - subrow = NULL; /* Quite gcc warning! */ + subrow = nullptr; /* Quite gcc warning! */ uilist_prepare(ui_list, items, layout_data, &visual_info); @@ -883,7 +883,7 @@ static void ui_template_list_layout_draw(bContext *C, org_i, 0, 0, - NULL); + nullptr); UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); sub = uiLayoutRow(overlap, false); @@ -983,7 +983,7 @@ static void ui_template_list_layout_draw(bContext *C, org_i, 0, 0, - NULL); + nullptr); UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); col = uiLayoutColumn(overlap, false); @@ -1089,7 +1089,7 @@ static void ui_template_list_layout_draw(bContext *C, 0, 0, ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, nullptr); } UI_block_emboss_set(subblock, UI_EMBOSS); @@ -1104,7 +1104,7 @@ static void ui_template_list_layout_draw(bContext *C, 0, UI_UNIT_X, UI_UNIT_Y * 0.05f, - NULL, + nullptr, 0.0, 0.0, 0, @@ -1146,7 +1146,7 @@ static void ui_template_list_layout_draw(bContext *C, 0, 0, ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, nullptr); } UI_block_emboss_set(subblock, UI_EMBOSS); @@ -1170,7 +1170,7 @@ uiList *uiTemplateList_ex(uiLayout *layout, enum uiTemplateListFlags flags, void *customdata) { - TemplateListInputData input_data = {0}; + TemplateListInputData input_data = {nullptr}; uiListType *ui_list_type; if (!ui_template_list_data_retrieve(listtype_name, list_id, @@ -1181,7 +1181,7 @@ uiList *uiTemplateList_ex(uiLayout *layout, item_dyntip_propname, &input_data, &ui_list_type)) { - return NULL; + return nullptr; } uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : @@ -1254,7 +1254,7 @@ void uiTemplateList(uiLayout *layout, layout_type, columns, flags, - NULL); + nullptr); } /** @@ -1267,11 +1267,11 @@ PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list, uiListDyn *dyn_data = ui_list->dyn_data; dyn_data->custom_activate_optype = WM_operatortype_find(opname, false); if (!dyn_data->custom_activate_optype) { - return NULL; + return nullptr; } if (create_properties) { - WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, NULL, opname); + WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, nullptr, opname); } return dyn_data->custom_activate_opptr; @@ -1287,11 +1287,11 @@ PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list, uiListDyn *dyn_data = ui_list->dyn_data; dyn_data->custom_drag_optype = WM_operatortype_find(opname, false); if (!dyn_data->custom_drag_optype) { - return NULL; + return nullptr; } if (create_properties) { - WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, NULL, opname); + WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, nullptr, opname); } return dyn_data->custom_drag_opptr; -- cgit v1.2.3 From eccd03a826b1c1465f28f93d35ca21359a6dc4e5 Mon Sep 17 00:00:00 2001 From: Red Mser Date: Thu, 15 Jul 2021 15:20:37 -0400 Subject: UI: Flip driver editor debug lines In driver editor, vertically flip the value debug lines to align them with the timeline header values. This makes it easier to read the values. Also set the line width explicitly, which was incorrect in some cases. Differential Revision: https://developer.blender.org/D8877 --- source/blender/editors/space_graph/graph_draw.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index 56592cbbd1b..af88bbced9c 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -1259,14 +1259,15 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) immUniformColor3f(0.9f, 0.9f, 0.9f); immUniform1f("dash_width", 10.0f); immUniform1f("dash_factor", 0.5f); + GPU_line_width(1.0f); - immBegin(GPU_PRIM_LINES, (y >= v2d->cur.ymin) ? 4 : 2); + immBegin(GPU_PRIM_LINES, (y <= v2d->cur.ymax) ? 4 : 2); /* x-axis lookup */ co[0] = x; - if (y >= v2d->cur.ymin) { - co[1] = v2d->cur.ymin - 1.0f; + if (y <= v2d->cur.ymax) { + co[1] = v2d->cur.ymax + 1.0f; immVertex2fv(shdr_pos, co); co[1] = y; -- cgit v1.2.3 From d3c454d66c0a730eafa7edecec158ca3b9edb2d7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 16 Jul 2021 11:45:03 +1000 Subject: Cleanup: compiler warnings --- source/blender/editors/asset/asset_list.cc | 2 +- source/blender/editors/include/ED_armature.h | 4 ++-- source/blender/editors/interface/interface_template_asset_view.cc | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/asset/asset_list.cc b/source/blender/editors/asset/asset_list.cc index 1ea948d97d4..e8ed3958231 100644 --- a/source/blender/editors/asset/asset_list.cc +++ b/source/blender/editors/asset/asset_list.cc @@ -301,7 +301,7 @@ void AssetList::clear(bContext *C) filelist_freelib(files); filelist_clear(files); - WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, NULL); + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr); } /** diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index eaa54f66928..51d8aa8b064 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -255,8 +255,8 @@ struct PoseBackup *ED_pose_backup_create_selected_bones( struct PoseBackup *ED_pose_backup_create_all_bones( const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT; bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup); -void ED_pose_backup_restore(const struct PoseBackup *pose_backup); -void ED_pose_backup_free(struct PoseBackup *pose_backup); +void ED_pose_backup_restore(const struct PoseBackup *pbd); +void ED_pose_backup_free(struct PoseBackup *pbd); #ifdef __cplusplus } diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index 2860abb32a1..5a05813f947 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -53,7 +53,7 @@ static void asset_view_item_but_drag_set(uiBut *but, AssetHandle *asset_handle) { ID *id = asset_handle->file_data->id; - if (id != NULL) { + if (id != nullptr) { UI_but_drag_set_id(but, id); return; } @@ -257,14 +257,14 @@ void uiTemplateAssetView(uiLayout *layout, if (activate_opname) { PointerRNA *ptr = UI_list_custom_activate_operator_set( - list, activate_opname, r_activate_op_properties != NULL); + list, activate_opname, r_activate_op_properties != nullptr); if (r_activate_op_properties && ptr) { *r_activate_op_properties = *ptr; } } if (drag_opname) { PointerRNA *ptr = UI_list_custom_drag_operator_set( - list, drag_opname, r_drag_op_properties != NULL); + list, drag_opname, r_drag_op_properties != nullptr); if (r_drag_op_properties && ptr) { *r_drag_op_properties = *ptr; } -- cgit v1.2.3 From 09b89da349fd08aea811cc770cf627fbda6d75ba Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 16 Jul 2021 11:45:51 +1000 Subject: Cleanup: spelling in comments --- source/blender/editors/asset/asset_edit.cc | 6 +++--- source/blender/editors/asset/asset_list.cc | 6 +++--- source/blender/editors/interface/interface_handlers.c | 8 ++++---- source/blender/editors/interface/interface_utils.c | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc index 0937af0dbf1..f4860737193 100644 --- a/source/blender/editors/asset/asset_edit.cc +++ b/source/blender/editors/asset/asset_edit.cc @@ -82,13 +82,13 @@ bool ED_asset_can_make_single_from_context(const bContext *C) } /* TODO better place? */ -/* TODO What about the setter and the itemf? */ +/* TODO What about the setter and the `itemf` callback? */ #include "BKE_preferences.h" #include "DNA_asset_types.h" #include "DNA_userdef_types.h" int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library) { - /* Simple case: Predefined repo, just set the value. */ + /* Simple case: Predefined repository, just set the value. */ if (library->type < ASSET_LIBRARY_CUSTOM) { return library->type; } @@ -109,7 +109,7 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) { AssetLibraryReference library; - /* Simple case: Predefined repo, just set the value. */ + /* Simple case: Predefined repository, just set the value. */ if (value < ASSET_LIBRARY_CUSTOM) { library.type = value; library.custom_library_index = -1; diff --git a/source/blender/editors/asset/asset_list.cc b/source/blender/editors/asset/asset_list.cc index e8ed3958231..dd1c5f360a0 100644 --- a/source/blender/editors/asset/asset_list.cc +++ b/source/blender/editors/asset/asset_list.cc @@ -60,7 +60,7 @@ class AssetLibraryReferenceWrapper { const AssetLibraryReference reference_; public: - /* Intentionally not `explicit`, allow implicit conversion for convienience. Might have to be + /* Intentionally not `explicit`, allow implicit conversion for convenience. Might have to be * NOLINT */ AssetLibraryReferenceWrapper(const AssetLibraryReference &reference); ~AssetLibraryReferenceWrapper() = default; @@ -348,7 +348,7 @@ void AssetList::tagMainDataDirty() const void AssetList::remapID(ID * /*id_old*/, ID * /*id_new*/) const { - /* Trigger full refetch of the file list if main data was changed, don't even attempt remap + /* Trigger full re-fetch of the file list if main data was changed, don't even attempt remap * pointers. We could give file list types a id-remap callback, but it's probably not worth it. * Refreshing local file lists is relatively cheap. */ tagMainDataDirty(); @@ -605,7 +605,7 @@ int ED_assetlist_size(const AssetLibraryReference *library_reference) } /** - * Tag all asset lists in the storage that show main data as needing an update (refetch). + * Tag all asset lists in the storage that show main data as needing an update (re-fetch). * * This only tags the data. If the asset list is visible on screen, the space is still responsible * for ensuring the necessary redraw. It can use #ED_assetlist_listen() to check if the asset-list diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 858955271b2..4f8bb6342f7 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -770,7 +770,7 @@ static uiAfterFunc *ui_afterfunc_new(void) * \param context_but: A button from which to get the context from (`uiBut.context`) for the * operator execution. * - * \note Ownership over \a properties is moved here. The after-func owns it now. + * \note Ownership over \a properties is moved here. The #uiAfterFunc owns it now. * \note Can only call while handling buttons. */ static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot, @@ -1157,7 +1157,7 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu } /** - * \note Ownership of \a properties is moved here. The after-func owns it now. + * \note Ownership of \a properties is moved here. The #uiAfterFunc owns it now. * * \param context_but: The button to use context from when calling or polling the operator. * @@ -9458,10 +9458,10 @@ static int ui_list_handle_click_drag(bContext *C, activate = true; } } - /* KM_CLICK is only sent after an uncaught release event, so the forground button gets all + /* #KM_CLICK is only sent after an uncaught release event, so the foreground button gets all * regular events (including mouse presses to start dragging) and this part only kicks in if it * hasn't handled the release event. Note that if there's no overlaid button, the row selects - * on the press event already via regular UI_BTYPE_LISTROW handling. */ + * on the press event already via regular #UI_BTYPE_LISTROW handling. */ else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) { activate = true; } diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index aa6dff3952a..93a790b53d0 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -852,7 +852,7 @@ static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect) void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but) { View2D *v2d = ®ion->v2d; - /* Unitialized view or region that doesn't use View2D. */ + /* Uninitialized view or region that doesn't use View2D. */ if ((v2d->flag & V2D_IS_INIT) == 0) { return; } -- cgit v1.2.3 From bf5b1fa7266c510b22428f7f176aef545846004c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 16 Jul 2021 11:45:52 +1000 Subject: Cleanup: remove redundant parentheses --- source/blender/editors/gpencil/gpencil_interpolate.c | 11 +++++------ source/blender/editors/gpencil/gpencil_sculpt_paint.c | 7 +++---- source/blender/editors/gpencil/gpencil_vertex_paint.c | 2 +- source/blender/editors/interface/interface.c | 2 +- .../blender/editors/interface/interface_region_search.c | 8 ++++---- source/blender/editors/sculpt_paint/paint_image_2d.c | 9 ++++----- source/blender/editors/space_graph/graph_select.c | 2 +- source/blender/editors/space_image/image_buttons.c | 16 ++++++++-------- source/blender/editors/space_image/image_ops.c | 2 +- source/blender/editors/space_text/text_format_lua.c | 6 +++--- source/blender/editors/space_text/text_format_osl.c | 10 +++++----- source/blender/editors/space_text/text_format_pov.c | 10 +++++----- source/blender/editors/space_text/text_format_pov_ini.c | 4 ++-- source/blender/editors/space_text/text_format_py.c | 8 ++++---- source/blender/editors/transform/transform.c | 2 +- source/blender/editors/undo/ed_undo.c | 2 +- source/blender/editors/uvedit/uvedit_smart_stitch.c | 6 +++--- source/blender/editors/uvedit/uvedit_unwrap_ops.c | 4 ++-- 18 files changed, 54 insertions(+), 57 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 0062e363cdf..8640ffa67cf 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -278,7 +278,7 @@ static void gpencil_stroke_pair_table(bContext *C, tGPDinterpolate_layer *tgpil) { bGPdata *gpd = tgpi->gpd; - const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) && + const bool only_selected = (GPENCIL_EDIT_MODE(gpd) && ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) != 0)); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); @@ -291,8 +291,7 @@ static void gpencil_stroke_pair_table(bContext *C, LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) { bGPDstroke *gps_to = NULL; /* only selected */ - if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) && - ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + if (GPENCIL_EDIT_MODE(gpd) && (only_selected) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { continue; } /* skip strokes that are invalid for current view */ @@ -712,7 +711,7 @@ static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGP tgpi->flag, (RNA_enum_get(op->ptr, "layers") == 1), GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS); SET_FLAG_FROM_TEST( tgpi->flag, - ((GPENCIL_EDIT_MODE(tgpi->gpd)) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))), + (GPENCIL_EDIT_MODE(tgpi->gpd) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))), GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED); tgpi->flipmode = RNA_enum_get(op->ptr, "flip"); @@ -1249,7 +1248,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) const int step = RNA_int_get(op->ptr, "step"); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); const bool all_layers = (bool)(RNA_enum_get(op->ptr, "layers") == 1); - const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) && + const bool only_selected = (GPENCIL_EDIT_MODE(gpd) && (RNA_boolean_get(op->ptr, "interpolate_selected_only") != 0)); eGP_InterpolateFlipMode flipmode = RNA_enum_get(op->ptr, "flip"); @@ -1309,7 +1308,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) { bGPDstroke *gps_to = NULL; /* Only selected. */ - if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) && + if (GPENCIL_EDIT_MODE(gpd) && (only_selected) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 462462cf341..14caf0c08a7 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -1352,7 +1352,7 @@ static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso * - This is useful when animating as it saves that "uh-oh" moment when you realize you've * spent too much time editing the wrong frame. */ - if ((IS_AUTOKEY_ON(scene)) && (gpf->framenum != cfra)) { + if (IS_AUTOKEY_ON(scene) && (gpf->framenum != cfra)) { BKE_gpencil_frame_addcopy(gpl, cfra); /* Need tag to recalculate evaluated data to avoid crashes. */ DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); @@ -1377,7 +1377,7 @@ static float gpencil_sculpt_rotation_eval_get(tGP_BrushEditData *gso, int idx_eval) { /* If multiframe or no modifiers, return 0. */ - if ((GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd)) || (!gso->is_transformed)) { + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd) || (!gso->is_transformed)) { return 0.0f; } @@ -1513,8 +1513,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso, } pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; /* If masked and the point is not selected, skip it. */ - if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) && - ((pt_active->flag & GP_SPOINT_SELECT) == 0)) { + if (GPENCIL_ANY_SCULPT_MASK(gso->mask) && ((pt_active->flag & GP_SPOINT_SELECT) == 0)) { continue; } index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index 16605b6c634..633e371cbd1 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -919,7 +919,7 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, pt_active = pt->runtime.pt_orig; if (pt_active != NULL) { /* If masked and the point is not selected, skip it. */ - if ((GPENCIL_ANY_VERTEX_MASK(gso->mask)) && + if (GPENCIL_ANY_VERTEX_MASK(gso->mask) && ((pt_active->flag & GP_SPOINT_SELECT) == 0)) { continue; } diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index d5316df0e0c..ddde4f5a9dc 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2453,7 +2453,7 @@ bool ui_but_is_rna_valid(uiBut *but) */ bool ui_but_supports_cycling(const uiBut *but) { - return ((ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_LISTBOX)) || + return (ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_LISTBOX) || (but->type == UI_BTYPE_MENU && ui_but_menu_step_poll(but)) || (but->type == UI_BTYPE_COLOR && ((uiButColor *)but)->is_pallete_color) || (but->menu_step_func != NULL)); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 13436b6c6c8..c863b1f8bdf 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -688,13 +688,13 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN); GPU_blend(GPU_BLEND_NONE); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP); GPU_blend(GPU_BLEND_NONE); } } @@ -990,13 +990,13 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN); GPU_blend(GPU_BLEND_NONE); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP); GPU_blend(GPU_BLEND_NONE); } } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index ffa6f6ac962..23b90171a1d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -784,11 +784,10 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, bool do_random = false; bool do_partial_update = false; - bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) && - ((ELEM(brush->gradient_stroke_mode, - BRUSH_GRADIENT_SPACING_REPEAT, - BRUSH_GRADIENT_SPACING_CLAMP)) || - (cache->last_pressure != pressure))); + bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) && (ELEM(brush->gradient_stroke_mode, + BRUSH_GRADIENT_SPACING_REPEAT, + BRUSH_GRADIENT_SPACING_CLAMP) || + (cache->last_pressure != pressure))); float tex_rotation = -brush->mtex.rot; float mask_rotation = -brush->mask_mtex.rot; diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 1421be41124..a853efb1ace 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -859,7 +859,7 @@ static int graphkeys_box_select_exec(bContext *C, wmOperator *op) * as frame-range one is often used for tweaking timing when "blocking", * while channels is not that useful. */ - if ((BLI_rcti_size_x(&rect)) >= (BLI_rcti_size_y(&rect))) { + if (BLI_rcti_size_x(&rect) >= BLI_rcti_size_y(&rect)) { mode = BEZT_OK_FRAMERANGE; } else { diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 50b0ea75052..4779a82948d 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1013,14 +1013,14 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma uiLayoutRow(col, true), imfptr, "color_mode", UI_ITEM_R_EXPAND, IFACE_("Color"), ICON_NONE); /* only display depth setting if multiple depths can be used */ - if ((ELEM(depth_ok, - R_IMF_CHAN_DEPTH_1, - R_IMF_CHAN_DEPTH_8, - R_IMF_CHAN_DEPTH_10, - R_IMF_CHAN_DEPTH_12, - R_IMF_CHAN_DEPTH_16, - R_IMF_CHAN_DEPTH_24, - R_IMF_CHAN_DEPTH_32)) == 0) { + if (ELEM(depth_ok, + R_IMF_CHAN_DEPTH_1, + R_IMF_CHAN_DEPTH_8, + R_IMF_CHAN_DEPTH_10, + R_IMF_CHAN_DEPTH_12, + R_IMF_CHAN_DEPTH_16, + R_IMF_CHAN_DEPTH_24, + R_IMF_CHAN_DEPTH_32) == 0) { uiItemR(uiLayoutRow(col, true), imfptr, "color_depth", UI_ITEM_R_EXPAND, NULL, ICON_NONE); } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 6b9821745c7..dad354ba8ee 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1995,7 +1995,7 @@ static bool image_save_as_draw_check_prop(PointerRNA *ptr, return !(STREQ(prop_id, "filepath") || STREQ(prop_id, "directory") || STREQ(prop_id, "filename") || /* when saving a copy, relative path has no effect */ - ((STREQ(prop_id, "relative_path")) && RNA_boolean_get(ptr, "copy"))); + (STREQ(prop_id, "relative_path") && RNA_boolean_get(ptr, "copy"))); } static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c index 16eb66624ce..0cd2d9baa0b 100644 --- a/source/blender/editors/space_text/text_format_lua.c +++ b/source/blender/editors/space_text/text_format_lua.c @@ -165,9 +165,9 @@ static char txtfmt_lua_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_lua_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_lua_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_lua_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_lua_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c index 1a024779a83..97d9ec546ca 100644 --- a/source/blender/editors/space_text/text_format_osl.c +++ b/source/blender/editors/space_text/text_format_osl.c @@ -189,11 +189,11 @@ static char txtfmt_osl_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_osl_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_osl_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else if ((txtfmt_osl_find_reserved(str)) != -1) { fmt = FMT_TYPE_RESERVED; - } else if ((txtfmt_osl_find_preprocessor(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_osl_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_osl_find_builtinfunc(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else if (txtfmt_osl_find_reserved(str) != -1) { fmt = FMT_TYPE_RESERVED; + } else if (txtfmt_osl_find_preprocessor(str) != -1) { fmt = FMT_TYPE_DIRECTIVE; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index 1200dda7533..ea3d0ec1478 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -762,11 +762,11 @@ static char txtfmt_pov_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_pov_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_pov_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else if ((txtfmt_pov_find_reserved_keywords(str)) != -1) { fmt = FMT_TYPE_RESERVED; - } else if ((txtfmt_pov_find_reserved_builtins(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_pov_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_pov_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else if (txtfmt_pov_find_reserved_keywords(str) != -1) { fmt = FMT_TYPE_RESERVED; + } else if (txtfmt_pov_find_reserved_builtins(str) != -1) { fmt = FMT_TYPE_DIRECTIVE; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index 1c6a93d2d7a..259ad02a6b7 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -347,10 +347,10 @@ static int txtfmt_ini_find_bool(const char *string) static char txtfmt_pov_ini_format_identifier(const char *str) { char fmt; - if ((txtfmt_ini_find_keyword(str)) != -1) { + if (txtfmt_ini_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD; } - else if ((txtfmt_ini_find_reserved(str)) != -1) { + else if (txtfmt_ini_find_reserved(str) != -1) { fmt = FMT_TYPE_RESERVED; } else { diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index 2717c0bf5b0..e2a01a8d85d 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -315,10 +315,10 @@ static char txtfmt_py_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_py_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_py_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else if ((txtfmt_py_find_decorator(str)) != -1) { fmt = FMT_TYPE_RESERVED; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_py_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_py_find_builtinfunc(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else if (txtfmt_py_find_decorator(str) != -1) { fmt = FMT_TYPE_RESERVED; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index d34cc6f424f..efcf7d587e1 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -78,7 +78,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]); bool transdata_check_local_islands(TransInfo *t, short around) { - return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH, OB_GPENCIL)))); + return ((around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->obedit_type, OB_MESH, OB_GPENCIL))); } /* ************************** SPACE DEPENDENT CODE **************************** */ diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 0368252f54b..3e0029156c1 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -695,7 +695,7 @@ int ED_undo_operator_repeat(bContext *C, wmOperator *op) CTX_wm_region_set(C, region_win); } - if ((WM_operator_repeat_check(C, op)) && (WM_operator_poll(C, op->type)) && + if (WM_operator_repeat_check(C, op) && WM_operator_poll(C, op->type) && /* NOTE: undo/redo can't run if there are jobs active, * check for screen jobs only so jobs like material/texture/world preview * (which copy their data), won't stop redo, see T29579], diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 28853bcdedf..b40e50851c3 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1989,7 +1989,7 @@ static StitchState *stitch_init(bContext *C, /* Now, on to generate our uv connectivity data */ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!(ts->uv_flag & UV_SYNC_SELECTION) && - ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { + (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { continue; } @@ -2172,8 +2172,8 @@ static StitchState *stitch_init(bContext *C, "uv_stitch_selection_stack"); BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!(ts->uv_flag & UV_SYNC_SELECTION) && ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { + if (!(ts->uv_flag & UV_SYNC_SELECTION) && + (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { continue; } diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 0e9669d0f60..3d5dabda23d 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -315,7 +315,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -404,7 +404,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } -- cgit v1.2.3 From 42017b006efef2626482304eb9ecd82e047a8426 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 16 Jul 2021 11:48:54 +1000 Subject: Cleanup: sort struct declarations --- source/blender/editors/include/ED_anim_api.h | 2 +- source/blender/editors/include/ED_armature.h | 2 +- source/blender/editors/include/ED_asset.h | 2 +- source/blender/editors/include/ED_node.h | 2 +- source/blender/editors/include/ED_particle.h | 2 +- source/blender/editors/include/ED_spreadsheet.h | 10 +++++----- source/blender/editors/io/io_gpencil.h | 2 +- source/blender/editors/space_buttons/buttons_intern.h | 2 +- .../editors/space_spreadsheet/spreadsheet_cell_value.hh | 2 +- .../editors/space_spreadsheet/spreadsheet_dataset_draw.hh | 2 +- source/blender/editors/space_spreadsheet/spreadsheet_draw.hh | 4 ++-- 11 files changed, 16 insertions(+), 16 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 5cf2a9c9dd0..50e53acb376 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -34,9 +34,9 @@ struct ListBase; struct ARegion; struct ARegionType; +struct FModifier; struct Main; struct NlaStrip; -struct FModifier; struct PanelType; struct ReportList; struct ScrArea; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 51d8aa8b064..868235c36e5 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -31,7 +31,6 @@ extern "C" { #endif -struct bAction; struct Base; struct Bone; struct Depsgraph; @@ -46,6 +45,7 @@ struct Scene; struct UndoType; struct View3D; struct ViewLayer; +struct bAction; struct bArmature; struct bContext; struct bPoseChannel; diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index d33085f1cc4..0058c0615c3 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -28,9 +28,9 @@ extern "C" { struct AssetFilterSettings; struct AssetLibraryReference; -struct bContext; struct Main; struct ReportList; +struct bContext; struct wmNotifier; typedef struct AssetTempIDConsumer AssetTempIDConsumer; diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 058d4fa91a3..6e4002fcc0a 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -31,6 +31,7 @@ struct ID; struct Main; struct Scene; struct ScrArea; +struct SpaceNode; struct Tex; struct View2D; struct bContext; @@ -40,7 +41,6 @@ struct bNodeSocketType; struct bNodeTree; struct bNodeTreeType; struct bNodeType; -struct SpaceNode; typedef enum { NODE_TOP = 1, diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h index 6d0172e724a..5318c653b6d 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -34,9 +34,9 @@ struct ParticleSystem; struct Scene; struct UndoType; struct ViewLayer; -struct wmGenericUserData; struct bContext; struct rcti; +struct wmGenericUserData; /* particle edit mode */ void PE_free_ptcache_edit(struct PTCacheEdit *edit); diff --git a/source/blender/editors/include/ED_spreadsheet.h b/source/blender/editors/include/ED_spreadsheet.h index ff77135a51c..dfa8aa7bfbc 100644 --- a/source/blender/editors/include/ED_spreadsheet.h +++ b/source/blender/editors/include/ED_spreadsheet.h @@ -16,14 +16,14 @@ #pragma once -struct SpreadsheetContext; -struct SpaceSpreadsheet; -struct SpaceNode; struct ID; -struct bNode; struct Main; -struct bContext; struct Object; +struct SpaceNode; +struct SpaceSpreadsheet; +struct SpreadsheetContext; +struct bContext; +struct bNode; #ifdef __cplusplus extern "C" { diff --git a/source/blender/editors/io/io_gpencil.h b/source/blender/editors/io/io_gpencil.h index b347be00412..428b09f0e9c 100644 --- a/source/blender/editors/io/io_gpencil.h +++ b/source/blender/editors/io/io_gpencil.h @@ -25,8 +25,8 @@ */ struct ARegion; -struct bContext; struct View3D; +struct bContext; struct wmOperatorType; void WM_OT_gpencil_import_svg(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index 7564fa4b930..9cb363ff0c9 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -34,8 +34,8 @@ struct Tex; struct bContext; struct bContextDataResult; struct bNode; -struct bNodeTree; struct bNodeSocket; +struct bNodeTree; struct wmOperatorType; struct SpaceProperties_Runtime { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index c9b73aabf96..680da9b6794 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -22,8 +22,8 @@ #include "BLI_float2.hh" #include "BLI_float3.hh" -struct Object; struct Collection; +struct Object; namespace blender::ed::spreadsheet { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh index d9e6d882c2a..19906d73e7f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh @@ -23,9 +23,9 @@ #include "spreadsheet_dataset_layout.hh" struct ARegion; -struct uiBlock; struct View2D; struct bContext; +struct uiBlock; namespace blender::ed::spreadsheet { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh index 647587ec8b0..9accd1d3d09 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh @@ -18,9 +18,9 @@ #include "BLI_vector.hh" -struct uiBlock; -struct bContext; struct ARegion; +struct bContext; +struct uiBlock; namespace blender::ed::spreadsheet { -- cgit v1.2.3 From 038345fa5650350f68df690fcd77f1ddc451b07b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 16 Jul 2021 17:47:42 +1000 Subject: Fix T89861: Checking face selection breaks UV stitch operator --- source/blender/editors/uvedit/uvedit_smart_stitch.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index b40e50851c3..535a0e00347 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1928,6 +1928,11 @@ static StitchState *stitch_init(bContext *C, state->obedit = obedit; state->em = em; + /* Workaround for sync-select & face-select mode which implies all selected faces are detached, + * for stitch this isn't useful behavior, see T86924. */ + const int selectmode_orig = scene->toolsettings->selectmode; + scene->toolsettings->selectmode = SCE_SELECT_VERTEX; + /* in uv synch selection, all uv's are visible */ if (ts->uv_flag & UV_SYNC_SELECTION) { state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true); @@ -1935,6 +1940,9 @@ static StitchState *stitch_init(bContext *C, else { state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true); } + + scene->toolsettings->selectmode = selectmode_orig; + if (!state->element_map) { state_delete(state); return NULL; -- cgit v1.2.3 From e78e235cc5ea3bb4c02cc4e8a39a47f611db1f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 16 Jul 2021 10:22:09 +0200 Subject: Cleanup: preview rendering, update assumptions in comment The `action_preview_render()` function used to just render, but now it also temporarily applies the pose. Its comment is now updated for this. No functional changes. --- source/blender/editors/render/render_preview.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 9abf15d2198..c7fa2a0ec87 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -861,9 +861,9 @@ static void action_preview_render_cleanup(IconPreview *preview, struct PoseBacku DEG_id_tag_update(&preview->active_object->id, ID_RECALC_GEOMETRY); } -/* Render a pose. It is assumed that the pose has already been applied and that the scene camera is - * capturing the pose. In other words, this function just renders from the scene camera without - * evaluating the Action stored in preview->id. */ +/* Render a pose from the scene camera. It is assumed that the scene camera is + * capturing the pose. The pose is applied temporarily to the current object + * before rendering. */ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview_sized) { char err_out[256] = ""; -- cgit v1.2.3 From 6025897cd6023babfcffed33a6be3ce0bbd127b9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 16 Jul 2021 12:05:21 -0400 Subject: Cleanup: Get vertex group names directly from grease pencil data --- source/blender/editors/gpencil/gpencil_primitive.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 78eaab01b1a..cf49aefe2ea 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1316,8 +1316,7 @@ static void gpencil_primitive_interaction_end(bContext *C, BrushGpencilSettings *brush_settings = brush->gpencil_settings; const int def_nr = tgpi->gpd->vertex_group_active_index - 1; - const ListBase *defbase = BKE_object_defgroup_list(tgpi->ob); - const bool have_weight = (bool)BLI_findlink(defbase, def_nr); + const bool have_weight = BLI_findlink(&tgpi->gpd->vertex_group_names, def_nr) != NULL; /* return to normal cursor and header status */ ED_workspace_status_text(C, NULL); -- cgit v1.2.3 From c4df8ac1a40c7a98a4244c7c14e19791b1e1e99f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 16 Jul 2021 12:23:55 -0400 Subject: Fix T89899: Crashes when accessing vertex groups from objects We need to be more strict about trying to retrieve a list of vertex group names from objects now, as only three object types support them. This commit adds a check for vertex group support in a few places, the data transfer operator/modifier, copying vertex groups to selected objects, and the vertex group remove and clear functions. Differential Revision: https://developer.blender.org/D11947 --- .../blender/editors/object/object_data_transfer.c | 87 ++++++++++------------ source/blender/editors/object/object_vgroup.c | 6 +- 2 files changed, 44 insertions(+), 49 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index 2109fe2a822..6251fb799c5 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -123,9 +123,14 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, RNA_enum_items_add_value( &item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_ALL_SRC); - if (data_type == DT_TYPE_MDEFORMVERT) { - Object *ob_src = CTX_data_active_object(C); + Object *ob_src = CTX_data_active_object(C); + if (ob_src == NULL) { + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; + } + if (data_type == DT_TYPE_MDEFORMVERT && BKE_object_supports_vertex_groups(ob_src)) { if (BKE_object_pose_armature_get(ob_src)) { RNA_enum_items_add_value( &item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_SELECT); @@ -133,67 +138,57 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, &item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_DEFORM); } - if (ob_src) { - const bDeformGroup *dg; - int i; + const bDeformGroup *dg; + int i; - RNA_enum_item_add_separator(&item, &totitem); + RNA_enum_item_add_separator(&item, &totitem); - const ListBase *defbase = BKE_object_defgroup_list(ob_src); - for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = dg->name; - RNA_enum_item_add(&item, &totitem, &tmp_item); - } + const ListBase *defbase = BKE_object_defgroup_list(ob_src); + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = dg->name; + RNA_enum_item_add(&item, &totitem, &tmp_item); } } else if (data_type == DT_TYPE_SHAPEKEY) { /* TODO */ } else if (data_type == DT_TYPE_UV) { - Object *ob_src = CTX_data_active_object(C); - - if (ob_src) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); - CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; - cddata_masks.lmask |= CD_MASK_MLOOPUV; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); - int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPUV); + CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; + cddata_masks.lmask |= CD_MASK_MLOOPUV; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); + int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPUV); - RNA_enum_item_add_separator(&item, &totitem); + RNA_enum_item_add_separator(&item, &totitem); - for (int i = 0; i < num_data; i++) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( - &me_eval->ldata, CD_MLOOPUV, i); - RNA_enum_item_add(&item, &totitem, &tmp_item); - } + for (int i = 0; i < num_data; i++) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( + &me_eval->ldata, CD_MLOOPUV, i); + RNA_enum_item_add(&item, &totitem, &tmp_item); } } else if (data_type == DT_TYPE_VCOL) { - Object *ob_src = CTX_data_active_object(C); - - if (ob_src) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); - CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; - cddata_masks.lmask |= CD_MASK_MLOOPCOL; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); - int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL); + CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; + cddata_masks.lmask |= CD_MASK_MLOOPCOL; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); + int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL); - RNA_enum_item_add_separator(&item, &totitem); + RNA_enum_item_add_separator(&item, &totitem); - for (int i = 0; i < num_data; i++) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( - &me_eval->ldata, CD_MLOOPCOL, i); - RNA_enum_item_add(&item, &totitem, &tmp_item); - } + for (int i = 0; i < num_data; i++) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( + &me_eval->ldata, CD_MLOOPCOL, i); + RNA_enum_item_add(&item, &totitem, &tmp_item); } } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 4ea599fd30e..f64f95c5322 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3892,7 +3892,7 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op) int fail = 0; CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (obact != ob) { + if (obact != ob && BKE_object_supports_vertex_groups(ob)) { if (ED_vgroup_array_copy(ob, obact)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); DEG_relations_tag_update(CTX_data_main(C)); @@ -3909,8 +3909,8 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op) if ((changed_tot == 0 && fail == 0) || fail) { BKE_reportf(op->reports, RPT_ERROR, - "Copy vertex groups to selected: %d done, %d failed (object data must have " - "matching indices)", + "Copy vertex groups to selected: %d done, %d failed (object data must support " + "vertex groups and have matching indices)", changed_tot, fail); } -- cgit v1.2.3 From 1dcf0f9cf11808a9659b456a424a6ac5e04257e6 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 16 Jul 2021 19:04:49 +0200 Subject: Fix T89722: Duplicate macro can cause strips to overlap Bug caused by 78693d524c13 accidentally removing overlap handling when transform operator is canceled. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D11899 --- source/blender/editors/transform/transform_convert_sequencer.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index a6f5aba5a1d..17512c79d03 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -262,8 +262,16 @@ static void free_transform_custom_data(TransCustomData *custom_data) /* Canceled, need to update the strips display. */ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips) { + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); + Sequence *seq; SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + /* Handle pre-existing overlapping strips even when operator is canceled. + * This is necessary for SEQUENCER_OT_duplicate_move macro for example. */ + if (SEQ_transform_test_overlap(seqbase, seq)) { + SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene); + } + SEQ_time_update_sequence_bounds(t->scene, seq); } } -- cgit v1.2.3