Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Eagar <joeedh@gmail.com>2021-07-17 10:44:01 +0300
committerJoseph Eagar <joeedh@gmail.com>2021-07-17 10:44:01 +0300
commit7c4eb4f8dbcf5fd98d8b089aa3cd7291b3910357 (patch)
treea3585864d68cc1a2a6b5f0be5da616c7d31cc590 /source/blender/editors
parent792292e3de00ed758cf4f5b675938b59f4a4cf21 (diff)
parent118803893e65c52c02ed274f7be2d60c668d5b56 (diff)
Merge branch 'master' into temp_bmesh_multires
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/time_scrub_ui.c2
-rw-r--r--source/blender/editors/armature/CMakeLists.txt3
-rw-r--r--source/blender/editors/armature/armature_intern.h4
-rw-r--r--source/blender/editors/armature/armature_ops.c2
-rw-r--r--source/blender/editors/armature/armature_skinning.c2
-rw-r--r--source/blender/editors/armature/editarmature_undo.c2
-rw-r--r--source/blender/editors/armature/pose_backup.c139
-rw-r--r--source/blender/editors/armature/pose_lib_2.c638
-rw-r--r--source/blender/editors/armature/pose_slide.c2
-rw-r--r--source/blender/editors/asset/CMakeLists.txt5
-rw-r--r--source/blender/editors/asset/asset_edit.cc88
-rw-r--r--source/blender/editors/asset/asset_list.cc637
-rw-r--r--source/blender/editors/asset/asset_ops.cc33
-rw-r--r--source/blender/editors/asset/asset_temp_id_consumer.cc113
-rw-r--r--source/blender/editors/curve/editcurve_add.c2
-rw-r--r--source/blender/editors/curve/editcurve_undo.c2
-rw-r--r--source/blender/editors/curve/editfont_undo.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c82
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_armature.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.c7
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c51
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c11
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c11
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c16
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c6
-rw-r--r--source/blender/editors/include/ED_anim_api.h2
-rw-r--r--source/blender/editors/include/ED_armature.h16
-rw-r--r--source/blender/editors/include/ED_asset.h63
-rw-r--r--source/blender/editors/include/ED_gpencil.h7
-rw-r--r--source/blender/editors/include/ED_keyframing.h2
-rw-r--r--source/blender/editors/include/ED_mesh.h2
-rw-r--r--source/blender/editors/include/ED_node.h2
-rw-r--r--source/blender/editors/include/ED_particle.h2
-rw-r--r--source/blender/editors/include/ED_spreadsheet.h10
-rw-r--r--source/blender/editors/include/UI_interface.h111
-rw-r--r--source/blender/editors/include/UI_interface_icons.h5
-rw-r--r--source/blender/editors/interface/CMakeLists.txt2
-rw-r--r--source/blender/editors/interface/interface.c46
-rw-r--r--source/blender/editors/interface/interface_context_menu.c16
-rw-r--r--source/blender/editors/interface/interface_handlers.c573
-rw-r--r--source/blender/editors/interface/interface_icons.c4
-rw-r--r--source/blender/editors/interface/interface_intern.h60
-rw-r--r--source/blender/editors/interface/interface_ops.c92
-rw-r--r--source/blender/editors/interface/interface_panel.c2
-rw-r--r--source/blender/editors/interface/interface_query.c113
-rw-r--r--source/blender/editors/interface/interface_region_search.c16
-rw-r--r--source/blender/editors/interface/interface_template_asset_view.cc272
-rw-r--r--source/blender/editors/interface/interface_template_list.cc1310
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c4
-rw-r--r--source/blender/editors/interface/interface_templates.c881
-rw-r--r--source/blender/editors/interface/interface_utils.c96
-rw-r--r--source/blender/editors/interface/interface_widgets.c80
-rw-r--r--source/blender/editors/interface/view2d_edge_pan.c2
-rw-r--r--source/blender/editors/io/io_gpencil.h2
-rw-r--r--source/blender/editors/lattice/editlattice_select.c2
-rw-r--r--source/blender/editors/lattice/editlattice_undo.c2
-rw-r--r--source/blender/editors/mask/mask_ops.c2
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c1
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c4
-rw-r--r--source/blender/editors/mesh/editmesh_path.c1
-rw-r--r--source/blender/editors/mesh/editmesh_select.c5
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c13
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c3
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c17
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c48
-rw-r--r--source/blender/editors/mesh/mesh_data.c1
-rw-r--r--source/blender/editors/mesh/meshtools.c29
-rw-r--r--source/blender/editors/metaball/editmball_undo.c2
-rw-r--r--source/blender/editors/object/object_data_transfer.c87
-rw-r--r--source/blender/editors/object/object_edit.c2
-rw-r--r--source/blender/editors/object/object_hook.c10
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c2
-rw-r--r--source/blender/editors/object/object_transform.c490
-rw-r--r--source/blender/editors/object/object_vgroup.c198
-rw-r--r--source/blender/editors/render/render_preview.c67
-rw-r--r--source/blender/editors/render/render_shading.c2
-rw-r--r--source/blender/editors/screen/area.c10
-rw-r--r--source/blender/editors/screen/area_query.c2
-rw-r--r--source/blender/editors/screen/glutil.c4
-rw-r--r--source/blender/editors/screen/screen_context.c22
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c9
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c10
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_ops.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c20
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c22
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.c12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c8
-rw-r--r--source/blender/editors/space_action/action_data.c4
-rw-r--r--source/blender/editors/space_api/spacetypes.c2
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h2
-rw-r--r--source/blender/editors/space_clip/clip_editor.c2
-rw-r--r--source/blender/editors/space_file/file_draw.c2
-rw-r--r--source/blender/editors/space_file/filelist.c34
-rw-r--r--source/blender/editors/space_file/filelist.h6
-rw-r--r--source/blender/editors/space_file/filesel.c16
-rw-r--r--source/blender/editors/space_file/space_file.c26
-rw-r--r--source/blender/editors/space_graph/graph_draw.c7
-rw-r--r--source/blender/editors/space_graph/graph_select.c2
-rw-r--r--source/blender/editors/space_image/image_buttons.c16
-rw-r--r--source/blender/editors/space_image/image_ops.c2
-rw-r--r--source/blender/editors/space_image/image_undo.c2
-rw-r--r--source/blender/editors/space_node/drawnode.cc32
-rw-r--r--source/blender/editors/space_node/node_add.cc2
-rw-r--r--source/blender/editors/space_node/node_draw.cc193
-rw-r--r--source/blender/editors/space_node/node_edit.cc28
-rw-r--r--source/blender/editors/space_node/node_intern.h2
-rw-r--r--source/blender/editors/space_node/node_relationships.cc17
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c5
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c33
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library.cc34
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_id.cc2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c24
-rw-r--r--source/blender/editors/space_sequencer/sequencer_scopes.c6
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.hh4
-rw-r--r--source/blender/editors/space_text/text_format_lua.c6
-rw-r--r--source/blender/editors/space_text/text_format_osl.c10
-rw-r--r--source/blender/editors/space_text/text_format_pov.c10
-rw-r--r--source/blender/editors/space_text/text_format_pov_ini.c4
-rw-r--r--source/blender/editors/space_text/text_format_py.c8
-rw-r--r--source/blender/editors/space_text/text_undo.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c111
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c70
-rw-r--r--source/blender/editors/transform/transform.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c26
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_edge.c1
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c1
-rw-r--r--source/blender/editors/transform/transform_convert_object.c4
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c8
-rw-r--r--source/blender/editors/transform/transform_generics.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c2
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c2
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c5
-rw-r--r--source/blender/editors/undo/ed_undo.c9
-rw-r--r--source/blender/editors/util/ed_util.c3
-rw-r--r--source/blender/editors/util/ed_util_ops.cc3
-rw-r--r--source/blender/editors/util/select_utils.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_islands.c1
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c14
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c4
158 files changed, 5791 insertions, 1836 deletions
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/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/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/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/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 <string.h>
+
+#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 <math.h>
+#include <string.h>
+
+#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/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/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt
index 8c5f91561b7..a27975bc37b 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
@@ -30,10 +31,14 @@ set(INC_SYS
set(SRC
asset_edit.cc
+ asset_list.cc
asset_ops.cc
+ asset_temp_id_consumer.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 d20de4141cb..f4860737193 100644
--- a/source/blender/editors/asset/asset_edit.cc
+++ b/source/blender/editors/asset/asset_edit.cc
@@ -18,11 +18,18 @@
* \ingroup edasset
*/
+#include <memory>
+#include <string>
+
#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"
#include "UI_interface_icons.h"
@@ -30,6 +37,8 @@
#include "ED_asset.h"
+using namespace blender;
+
bool ED_asset_mark_id(const bContext *C, ID *id)
{
if (id->asset_data) {
@@ -45,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;
}
@@ -57,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;
}
@@ -65,3 +80,76 @@ 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` 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 repository, 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 repository, 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;
+}
+
+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..dd1c5f360a0
--- /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 <optional>
+#include <string>
+
+#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 convenience. 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<decltype(reference_.type)>{}(reference_.type);
+ if (reference_.type != ASSET_LIBRARY_CUSTOM) {
+ return hash1;
+ }
+
+ uint64_t hash2 = DefaultHash<decltype(reference_.custom_library_index)>{}(
+ 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<FileList, decltype(&filelist_free_fn)> 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 &notifier) 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, nullptr);
+}
+
+/**
+ * \return True if the asset-list needs a UI redraw.
+ */
+bool AssetList::listen(const wmNotifier &notifier) 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 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();
+}
+
+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<AssetLibraryReferenceWrapper, AssetList>;
+
+ 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<eFileSelectType> asset_library_reference_to_fileselect_type(
+ const AssetLibraryReference &library_reference);
+
+ using is_new_t = bool;
+ static std::tuple<AssetList &, is_new_t> 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<eFileSelectType> 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<AssetList &, AssetListStorage::is_new_t> 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 (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
+ * 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/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<AssetTempIDConsumer *>(
+ 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<AssetTemporaryIDConsumer *>(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/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/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/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/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_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;
}
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_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_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_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..cf49aefe2ea 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1315,8 +1315,8 @@ 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 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);
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index efd0f86df03..14caf0c08a7 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. */
@@ -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_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/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_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/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/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 85563b76f38..868235c36e5 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -23,6 +23,10 @@
#pragma once
+#include <stdbool.h>
+
+#include "BLI_listbase.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -41,6 +45,7 @@ struct Scene;
struct UndoType;
struct View3D;
struct ViewLayer;
+struct bAction;
struct bArmature;
struct bContext;
struct bPoseChannel;
@@ -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 *pbd);
+void ED_pose_backup_free(struct PoseBackup *pbd);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h
index dd505167fe5..0058c0615c3 100644
--- a/source/blender/editors/include/ED_asset.h
+++ b/source/blender/editors/include/ED_asset.h
@@ -20,17 +20,80 @@
#pragma once
+#include "DNA_ID_enums.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+struct AssetFilterSettings;
+struct AssetLibraryReference;
+struct Main;
+struct ReportList;
+struct bContext;
+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);
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_asset_handle_get_full_library_path(const struct bContext *C,
+ const AssetLibraryReference *asset_library,
+ 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);
+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 <string>
+
+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<bool(FileDirEntry &)>;
+void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn);
+#endif
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]);
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/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/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/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 6901176a587..a25aac5803c 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
@@ -34,6 +35,7 @@ extern "C" {
/* Struct Declarations */
struct ARegion;
+struct AssetFilterSettings;
struct AutoComplete;
struct EnumPropertyItem;
struct FileSelectParams;
@@ -58,6 +60,7 @@ struct bNodeTree;
struct bScreen;
struct rctf;
struct rcti;
+struct uiBlockInteraction_Handle;
struct uiButSearch;
struct uiFontStyle;
struct uiList;
@@ -365,6 +368,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,
@@ -514,6 +520,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);
@@ -538,6 +592,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
@@ -655,6 +711,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);
@@ -2145,6 +2202,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,
@@ -2158,8 +2226,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,
@@ -2205,6 +2288,27 @@ 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,
+ 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);
@@ -2469,6 +2573,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,
@@ -2569,6 +2674,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/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/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 2cc3830042c..39dd6143eb9 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -66,6 +66,8 @@ 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
interface_templates.c
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 32b86119753..ddde4f5a9dc 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(&region->winrct) + 1;
const int getsizey = BLI_rcti_size_y(&region->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;
@@ -2432,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));
@@ -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_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 743f646d4df..4f8bb6342f7 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];
@@ -733,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 #uiAfterFunc 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)
@@ -769,72 +814,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();
+ 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;
- }
+ 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->func_arg1 = but->func_arg1;
- after->func_arg2 = but->func_arg2;
+ after->func_arg1 = but->func_arg1;
+ after->func_arg2 = but->func_arg2;
- after->funcN = but->funcN;
- after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL;
+ after->funcN = but->funcN;
+ after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL;
- after->rename_func = but->rename_func;
- after->rename_arg1 = but->rename_arg1;
- after->rename_orig = but->rename_orig; /* needs free! */
+ after->rename_func = but->rename_func;
+ after->rename_arg1 = but->rename_arg1;
+ after->rename_orig = but->rename_orig; /* needs free! */
- after->handle_func = block->handle_func;
- after->handle_func_arg = block->handle_func_arg;
- after->retval = but->retval;
+ after->handle_func = block->handle_func;
+ after->handle_func_arg = block->handle_func_arg;
+ after->retval = but->retval;
- 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;
- }
+ 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;
+ }
- if (block->handle) {
- after->popup_op = block->handle->popup_op;
- }
+ if (block->handle) {
+ after->popup_op = block->handle->popup_op;
+ }
- after->optype = but->optype;
- after->opcontext = but->opcontext;
- after->opptr = but->opptr;
+ after->optype = but->optype;
+ after->opcontext = but->opcontext;
+ after->opptr = but->opptr;
- after->rnapoin = but->rnapoin;
- after->rnaprop = but->rnaprop;
+ after->rnapoin = but->rnapoin;
+ after->rnaprop = but->rnaprop;
- 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;
- }
+ 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;
+ }
- if (but->context) {
- after->context = CTX_store_copy(but->context);
+ 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++;
+ }
}
+ }
- 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() */
@@ -997,6 +1065,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]) {
@@ -1076,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 #uiAfterFunc 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) {
@@ -1548,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) {
@@ -1619,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);
@@ -2181,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;
@@ -2283,6 +2401,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);
+ }
}
/** \} */
@@ -3396,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
@@ -4210,7 +4338,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 */
@@ -4704,6 +4832,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;
}
@@ -4852,6 +4989,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 +5501,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;
@@ -6435,7 +6576,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;
}
@@ -7773,7 +7914,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;
}
}
@@ -7853,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:
@@ -8239,6 +8381,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 +8619,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);
@@ -8722,6 +8889,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);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -9186,6 +9373,149 @@ 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 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. */
+ 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_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;
@@ -9219,22 +9549,19 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
}
}
- if (val == KM_PRESS) {
- if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) &&
+ 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, 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
@@ -9281,12 +9608,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;
@@ -11314,3 +11636,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_icons.c b/source/blender/editors/interface/interface_icons.c
index aa957afbf8f..43ac646f053 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 */
@@ -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;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index a05afb6e542..a07f924e65b 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 *);
@@ -594,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,
@@ -617,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);
@@ -928,9 +942,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);
@@ -943,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);
@@ -1042,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)
@@ -1125,19 +1148,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;
@@ -1149,6 +1185,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;
@@ -1176,7 +1213,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);
@@ -1204,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_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
@@ -75,6 +75,32 @@
#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;
@@ -1836,6 +1858,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);
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..8534c95b6fd 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, &region->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);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -485,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) &&
@@ -588,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, &region->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_region_search.c b/source/blender/editors/interface/interface_region_search.c
index c35dbc5d7a6..c863b1f8bdf 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 */
@@ -684,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);
}
}
@@ -986,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/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc
new file mode 100644
index 00000000000..5a05813f947
--- /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 != nullptr) {
+ 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 != 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 != nullptr);
+ 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
new file mode 100644
index 00000000000..eaab33e32c9
--- /dev/null
+++ b/source/blender/editors/interface/interface_template_list.cc
@@ -0,0 +1,1310 @@
+/*
+ * 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 <cstdlib>
+#include <cstring>
+
+#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().
+ */
+struct TemplateListInputData {
+ 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;
+};
+
+/**
+ * Internal wrapper for a single item in the list (well, actually stored as a vector).
+ */
+struct _uilist_item {
+ PointerRNA item;
+ int org_idx;
+ int flt_flag;
+};
+
+/**
+ * Container for the item vector and additional info.
+ */
+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;
+};
+
+struct TemplateListLayoutDrawData {
+ uiListDrawItemFunc draw_item;
+ uiListDrawFilterFunc draw_filter;
+
+ int rows;
+ int maxrows;
+ int columns;
+};
+
+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. */
+};
+
+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(nullptr, &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);
+ }
+}
+
+struct StringCmp {
+ char name[MAX_IDPROP_NAME];
+ int org_idx;
+};
+
+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 = 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;
+ 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 = nullptr;
+ int order_idx = 0, i = 0;
+
+ if (order_by_name) {
+ names = static_cast<StringCmp *>(MEM_callocN(sizeof(StringCmp) * len, "StringCmp"));
+ }
+ if (filter_raw[0]) {
+ const size_t slen = strlen(filter_raw);
+
+ dyn_data->items_filter_flags = static_cast<int *>(
+ 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<char *>(
+ 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, nullptr, 0, nullptr);
+ 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<int *>(
+ 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<uiList *>(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, nullptr, 0, nullptr);
+ }
+ }
+ return nullptr;
+}
+
+static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip)
+{
+ char *dyn_tooltip = static_cast<char *>(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 == nullptr) {
+ 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<uiList *>(
+ BLI_findstring(&region->ui_lists, full_list_id, offsetof(uiList, list_id)));
+
+ if (!ui_list) {
+ ui_list = static_cast<uiList *>(MEM_callocN(sizeof(uiList), "uiList"));
+ BLI_strncpy(ui_list->list_id, full_list_id, sizeof(ui_list->list_id));
+ BLI_addtail(&region->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<uiListDyn *>(
+ 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 = nullptr, *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 = nullptr; /* 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,
+ nullptr);
+ 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,
+ nullptr);
+ 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, nullptr);
+ }
+
+ 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,
+ nullptr,
+ 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, nullptr);
+ }
+
+ 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 = {nullptr};
+ 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 nullptr;
+ }
+
+ 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;
+ 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);
+
+ 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,
+ nullptr);
+}
+
+/**
+ * \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 nullptr;
+ }
+
+ if (create_properties) {
+ WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, nullptr, 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 nullptr;
+ }
+
+ if (create_properties) {
+ WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, nullptr, 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_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/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 9c17486aea4..766840909cc 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -5645,887 +5645,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);
- }
- }
-}
-
-typedef struct {
- PointerRNA item;
- int org_idx;
- int flt_flag;
-} _uilist_item;
-
-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;
-
-static void uilist_prepare(uiList *ui_list,
- int len,
- int activei,
- int rows,
- int maxrows,
- int columns,
- uiListLayoutdata *layoutdata)
-{
- uiListDyn *dyn_data = ui_list->dyn_data;
- const bool use_auto_size = (ui_list->list_grip < (rows - UI_LIST_AUTO_SIZE_THRESHOLD));
-
- /* default rows */
- if (rows <= 0) {
- rows = 5;
- }
- dyn_data->visual_height_min = rows;
- if (maxrows < rows) {
- maxrows = max_ii(rows, 5);
- }
- if (columns <= 0) {
- columns = 9;
- }
-
- int activei_row;
- if (columns > 1) {
- dyn_data->height = (int)ceil((double)len / (double)columns);
- activei_row = (int)floor((double)activei / (double)columns);
- }
- else {
- dyn_data->height = len;
- activei_row = activei;
- }
-
- if (!use_auto_size) {
- /* No auto-size, yet we clamp at min size! */
- maxrows = rows = max_ii(ui_list->list_grip, rows);
- }
- else if ((rows != maxrows) && (dyn_data->height > rows)) {
- /* Expand size if needed and possible. */
- rows = min_ii(dyn_data->height, 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 (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;
- }
- ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM;
- }
-
- const int max_scroll = max_ii(0, dyn_data->height - 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);
-}
-
-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);
-}
-
-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)
-{
- 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(&region->ui_lists, ui_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_addtail(&region->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;
-
- /* 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;
-
- /* 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;
- }
-
- /* 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
-
- 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__);
- }
-
- 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;
-
- 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;
- }
- }
-
- switch (layout_type) {
- case UILST_LAYOUT_DEFAULT:
- /* layout */
- box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
- glob = uiLayoutColumn(box, true);
- row = uiLayoutRow(glob, false);
- col = uiLayoutColumn(row, true);
-
- /* init numbers */
- uilist_prepare(ui_list, len, activei, rows, maxrows, 1, &layoutdata);
-
- if (dataptr->data && prop) {
- /* create list items */
- for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
- PointerRNA *itemptr = &items_ptr[i].item;
- void *dyntip_data;
- const int org_i = items_ptr[i].org_idx;
- const int flt_flag = items_ptr[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 */
- sub = uiLayoutRow(overlap, false);
-
- but = uiDefButR_prop(subblock,
- UI_BTYPE_LISTROW,
- 0,
- "",
- 0,
- 0,
- UI_UNIT_X * 10,
- UI_UNIT_Y,
- active_dataptr,
- activeprop,
- 0,
- 0,
- org_i,
- 0,
- 0,
- TIP_("Double click to rename"));
- if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, 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;
- }
- draw_item(ui_list,
- C,
- sub,
- dataptr,
- itemptr,
- icon,
- 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) {
- ui_layout_list_set_labels_active(sub);
- }
-
- UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
- }
- }
-
- /* add dummy buttons to fill space */
- for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
- uiItemL(col, "", ICON_NONE);
- }
-
- /* add scrollbar */
- if (len > layoutdata.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_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;
-
- 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);
- }
- /* 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,
- active_dataptr,
- 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, active_dataptr, 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);
-
- if (dataptr->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;
-
- /* create button */
- if (!(i % 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 */
- sub = uiLayoutRow(overlap, false);
-
- but = uiDefButR_prop(subblock,
- UI_BTYPE_LISTROW,
- 0,
- "",
- 0,
- 0,
- UI_UNIT_X * 10,
- UI_UNIT_Y,
- active_dataptr,
- 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);
- draw_item(ui_list,
- C,
- sub,
- dataptr,
- itemptr,
- icon,
- active_dataptr,
- active_propname,
- org_i,
- flt_flag);
-
- /* If we are "drawing" active item, set all labels as active. */
- if (i == activei) {
- ui_layout_list_set_labels_active(sub);
- }
-
- UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
- }
- }
-
- /* add dummy buttons to fill space */
- for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
- if (!(i % columns)) {
- subrow = uiLayoutRow(col, false);
- }
- uiItemL(subrow, "", ICON_NONE);
- }
-
- /* add scrollbar */
- if (len > layoutdata.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;
- }
-
- if (glob) {
- /* 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 */
-
- 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,
- "");
-
- 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 */
-
- 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);
- }
- }
-
- if (items_ptr) {
- MEM_freeN(items_ptr);
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Running Jobs Template
* \{ */
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 7ea02226f02..93a790b53d0 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"
@@ -774,6 +778,98 @@ bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str,
}
/* -------------------------------------------------------------------- */
+
+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, &region_rect, &but->rect);
+
+ rctf view_rect;
+ UI_view2d_region_to_view_rctf(v2d, &region_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 = &region->v2d;
+ /* Uninitialized 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
*
* Modal Button Store API.
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 7b2e0fec1d0..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,
@@ -1377,8 +1378,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 +1419,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 */
@@ -3116,7 +3116,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]);
@@ -4019,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),
@@ -4484,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;
@@ -4779,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;
@@ -4932,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);
+ }
}
}
@@ -5375,7 +5396,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) {
@@ -5460,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;
@@ -5482,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) {
@@ -5502,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);
+}
+
/** \} */
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/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/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(&lt->vertex_group_names) || lt->dvert == NULL) {
continue;
}
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(&lt->id, ID_RECALC_GEOMETRY);
}
/* The first element is always active */
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/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_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c
index db35742fa78..38e6bb80c0f 100644
--- a/source/blender/editors/mesh/editmesh_mask_extract.c
+++ b/source/blender/editors/mesh/editmesh_mask_extract.c
@@ -129,7 +129,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);
@@ -209,7 +209,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_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..830c9abb41e 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -35,6 +35,8 @@
#include "BLI_utildefines_stack.h"
#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
#include "BKE_report.h"
@@ -4739,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 ff4db96061f..c452f7a7487 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -29,6 +29,8 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
#include "BKE_material.h"
@@ -1041,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;
}
@@ -1090,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);
}
@@ -1128,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;
}
@@ -1141,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 2e85817b9d0..4278d3906ff 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"
@@ -5743,7 +5744,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/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 512481cdbfe..8dc7a1d7675 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"
@@ -671,7 +672,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){
@@ -681,13 +682,21 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *
BM_mesh_bm_from_me(NULL, 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, true);
+ em_tmp = BKE_editmesh_create(bm);
*em = *em_tmp;
+ /* 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;
@@ -865,7 +874,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/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index f4456f0b465..d765f6b50af 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;
@@ -326,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)
{
@@ -347,25 +344,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)
@@ -373,8 +351,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;
@@ -390,9 +368,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,
@@ -400,7 +378,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);
}
/** \} */
@@ -1472,8 +1450,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/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/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/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 */
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 742c7abb7ff..6251fb799c5 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"
@@ -122,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);
@@ -132,66 +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) {
- bDeformGroup *dg;
- int i;
+ const bDeformGroup *dg;
+ int i;
- RNA_enum_item_add_separator(&item, &totitem);
+ RNA_enum_item_add_separator(&item, &totitem);
- for (i = 0, dg = ob_src->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_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;
}
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_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/object/object_transform.c b/source/blender/editors/object/object_transform.c
index e350653e178..4c4727f51ee 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -1152,51 +1152,53 @@ 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;
+ 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);
- }
+ if (centermode == ORIGIN_TO_CURSOR) {
+ copy_v3_v3(cent, cursor);
+ invert_m4_m4(ob->imat, ob->obmat);
+ mul_m4_v3(ob->imat, cent);
+ }
- 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++;
+ 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 {
- 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);
- }
+ 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);
+ add_v3_v3(ob->instance_collection->instance_offset, cent);
- tot_change++;
- ob->instance_collection->id.tag |= LIB_TAG_DOIT;
- do_inverse_offset = true;
- }
+ tot_change++;
+ ob->instance_collection->id.tag |= LIB_TAG_DOIT;
+ do_inverse_offset = true;
}
}
- else if (ID_IS_LINKED(ob->data)) {
- tot_lib_error++;
- }
-
- if (obedit == NULL && ob->type == OB_MESH) {
+ }
+ 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) {
@@ -1222,265 +1224,265 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
me->id.tag |= LIB_TAG_DOIT;
do_inverse_offset = true;
}
- else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
- Curve *cu = ob->data;
+ }
+ else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ Curve *cu = ob->data;
- 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);
- }
+ 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;
- }
+ /* 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);
+ 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;
+ 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;
+ 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/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 37075a29b92..f64f95c5322 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;
}
@@ -3860,55 +3882,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
* \{ */
@@ -3919,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));
@@ -3936,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);
}
@@ -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/render/render_preview.c b/source/blender/editors/render/render_preview.c
index e8f69aded5b..c7fa2a0ec87 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;
/** \} */
@@ -253,7 +260,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 +342,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;
}
@@ -813,9 +820,50 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
/** \name Action Preview
* \{ */
-/* 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. */
+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 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] = "";
@@ -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);
}
@@ -1469,7 +1522,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;
}
@@ -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);
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..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, &region->ui_lists) {
+ if (list->type && list->type->listener) {
+ list->type->listener(list, params);
+ }
+ }
}
/* only exported for WM */
@@ -3018,6 +3024,8 @@ void ED_region_panels_layout_ex(const bContext *C,
y = -y;
}
+ UI_blocklist_update_view_for_buttons(C, &region->uiblocks);
+
if (update_tot_size) {
/* this also changes the 'cur' */
UI_view2d_totRect_set(v2d, x, y);
@@ -3672,7 +3680,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/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index d50962a56a9..3ce2f326dca 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,8 @@ const char *screen_context_dir[] = {
"selected_editable_fcurves",
"active_editable_fcurve",
"selected_editable_keyframes",
+ "ui_list",
+ "asset_library",
NULL,
};
@@ -1024,6 +1027,23 @@ 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;
+}
+
+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);
@@ -1098,6 +1118,8 @@ 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);
+ register_context_function("ui_list", screen_ctx_ui_list);
}
/* Entry point for the screen context. */
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/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/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 0bae6162f3a..f7785df0d3c 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -3103,7 +3103,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 f3b03805b27..135d9af1af2 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c
@@ -260,8 +260,8 @@ static void SCULPT_topology_automasking_init(Sculpt *sd,
Brush *brush = BKE_paint_brush(&sd->paint);
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
- BLI_assert(!"Topology masking: pmap missing");
- return;
+ BLI_assert_msg(0, "Topology masking: pmap missing");
+ return NULL;
}
const int totvert = SCULPT_vertex_count_get(ss);
@@ -302,8 +302,8 @@ static void sculpt_face_sets_automasking_init(Sculpt *sd,
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
- BLI_assert(!"Face Sets automasking: pmap missing");
- return;
+ BLI_assert_msg(0, "Face Sets automasking: pmap missing");
+ return NULL;
}
int tot_vert = SCULPT_vertex_count_get(ss);
@@ -329,8 +329,8 @@ void SCULPT_boundary_automasking_init(Object *ob,
SculptSession *ss = ob->sculpt;
if (!ss->pmap) {
- BLI_assert(!"Boundary Edges masking: pmap missing");
- return;
+ BLI_assert_msg(0, "Boundary Edges masking: pmap missing");
+ return NULL;
}
const int totvert = SCULPT_vertex_count_get(ss);
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 82818632782..6a37fd55562 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -676,7 +676,7 @@ void SCULPT_smooth(Sculpt *sd,
#endif
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 831ee3a6163..df4b43d21a2 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -1025,7 +1025,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:
- printf("Dynamic topology should've already been handled\n");
+ BLI_assert_msg(0, "Dynamic topology should've already been handled");
break;
}
}
@@ -1361,7 +1361,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:
- printf("Dynamic topology should've already been handled\n");
+ BLI_assert_msg(0, "Dynamic topology should've already been handled");
case SCULPT_UNDO_GEOMETRY:
case SCULPT_UNDO_FACE_SETS:
break;
@@ -1757,7 +1757,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:
- printf("Dynamic topology should've already been handled\n");
+ BLI_assert_msg(0, "Dynamic topology should've already been handled");
case SCULPT_UNDO_GEOMETRY:
case SCULPT_UNDO_FACE_SETS:
break;
@@ -2017,7 +2017,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_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_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();
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_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/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..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 */
@@ -1050,7 +1051,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);
@@ -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/filesel.c b/source/blender/editors/space_file/filesel.c
index b7accbf71e5..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;
}
@@ -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;
}
@@ -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)
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 05d484d8e2e..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);
}
}
@@ -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) {
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;
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_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_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 243652da608..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);
}
@@ -3911,8 +3909,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 +3927,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 +4046,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 +4123,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 +4143,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 +4158,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 +4172,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 +4207,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(&region->v2d, link)) {
continue;
}
if (add_reroute_intersect_check(link, mcoords, i, insert_point)) {
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<int>()) {
+ ss << *value.get<int>() << TIP_(" (Integer)");
+ }
+ else if (value.is_type<float>()) {
+ ss << *value.get<float>() << TIP_(" (Float)");
+ }
+ else if (value.is_type<blender::float3>()) {
+ ss << *value.get<blender::float3>() << TIP_(" (Vector)");
+ }
+ else if (value.is_type<bool>()) {
+ ss << (*value.get<bool>() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)");
+ }
+ else if (value.is_type<std::string>()) {
+ ss << *value.get<std::string>() << TIP_(" (String)");
+ }
+ else if (value.is_type<Object *>()) {
+ id_to_inspection_string((ID *)*value.get<Object *>());
+ }
+ else if (value.is_type<Material *>()) {
+ id_to_inspection_string((ID *)*value.get<Material *>());
+ }
+ else if (value.is_type<Tex *>()) {
+ id_to_inspection_string((ID *)*value.get<Tex *>());
+ }
+ else if (value.is_type<Collection *>()) {
+ id_to_inspection_string((ID *)*value.get<Collection *>());
+ }
+}
+
+static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log,
+ std::stringstream &ss)
+{
+ Span<GeometryComponentType> 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<std::string> 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<const geo_log::GenericValueLog *>(value_log)) {
+ create_inspection_string_for_generic_value(*generic_value_log, ss);
+ }
+ else if (const geo_log::GeometryValueLog *geo_value_log =
+ dynamic_cast<const geo_log::GeometryValueLog *>(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<std::string> 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);
}
/**
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 5dd935bdd76..af9c888cbf7 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 <algorithm>
+
#include "MEM_guardedalloc.h"
#include "DNA_light_types.h"
@@ -1226,6 +1228,32 @@ int node_find_indicated_socket(
return 0;
}
+/* ****************** Link Dimming *********************** */
+
+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)),
+ 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(&region->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(&region->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(&region->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(&region->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(&region->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(&region->v2d, link)) {
continue;
}
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..c5ec656080a 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;
+ }
}
}
@@ -588,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);
@@ -901,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_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<ID>(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<ID>(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_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_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;
}
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_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 45c6931364d..4b26469aad3 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
@@ -2929,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))
@@ -2998,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");
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;
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_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<MeshComponent>();
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_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 {
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/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);
}
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 2e203d06b12..3428a738dde 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 */
@@ -1211,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);
@@ -1237,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;
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/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/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) {
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/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index c1ee6edfef6..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,7 +2115,10 @@ 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);
+ const bool is_deform_only = tc_mesh_is_deform_only_update(t, tc);
+
+ DEG_id_tag_update(tc->obedit->data,
+ is_deform_only ? ID_RECALC_GEOMETRY_DEFORM : ID_RECALC_GEOMETRY);
tc_mesh_partial_update(t, tc, &partial_state);
}
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/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,
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);
}
}
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_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_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/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;
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index 00efdb87fea..3e0029156c1 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);
}
@@ -321,7 +324,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 +372,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);
@@ -692,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/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;
}
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;
}
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"
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 28853bcdedf..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;
@@ -1989,7 +1997,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 +2180,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 0a668144f09..84be69b7c4f 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;
}