Blender V5.0
interface_template_constraint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BKE_constraint.h"
12#include "BKE_context.hh"
13#include "BKE_library.hh"
14#include "BKE_screen.hh"
15
16#include "BLI_listbase.h"
17#include "BLI_string_utils.hh"
18
19#include "BLT_translation.hh"
20
22
23#include "ED_object.hh"
24
25#include "RNA_access.hh"
26#include "RNA_prototypes.hh"
27
28#include "WM_api.hh"
29
31#include "interface_intern.hh"
33
34static void constraint_active_func(bContext * /*C*/, void *ob_v, void *con_v)
35{
37 static_cast<bConstraint *>(con_v));
38}
39
40static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v)
41{
42 PointerRNA op_ptr;
43 uiLayout *row;
44 bConstraint *con = (bConstraint *)con_v;
45
47
48 PointerRNA ptr = RNA_pointer_create_discrete(&ob->id, &RNA_Constraint, con);
49 layout->context_ptr_set("constraint", &ptr);
51
52 layout->ui_units_x_set(4.0f);
53
54 /* Apply. */
55 layout->op("CONSTRAINT_OT_apply",
57 ICON_CHECKMARK);
58
59 /* Duplicate. */
60 layout->op("CONSTRAINT_OT_copy",
62 ICON_DUPLICATE);
63
64 layout->op("CONSTRAINT_OT_copy_to_selected",
66 0);
67
68 layout->separator();
69
70 /* Move to first. */
71 row = &layout->column(false);
72 op_ptr = row->op("CONSTRAINT_OT_move_to_index",
73 IFACE_("Move to First"),
74 ICON_TRIA_UP,
77 RNA_int_set(&op_ptr, "index", 0);
78 if (!con->prev) {
79 row->enabled_set(false);
80 }
81
82 /* Move to last. */
83 row = &layout->column(false);
84 op_ptr = row->op("CONSTRAINT_OT_move_to_index",
85 IFACE_("Move to Last"),
86 ICON_TRIA_DOWN,
90 ob, con, nullptr);
91 RNA_int_set(&op_ptr, "index", BLI_listbase_count(constraint_list) - 1);
92 if (!con->next) {
93 row->enabled_set(false);
94 }
95}
96
97/* -------------------------------------------------------------------- */
100
101static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
102{
103 /* unless button has its own callback, it adds this callback to button */
104 uiBlock *block = layout->block();
106
107 PointerRNA ptr = RNA_pointer_create_discrete(&ob->id, &RNA_Constraint, con);
108
109 if (block->panel) {
110 UI_panel_context_pointer_set(block->panel, "constraint", &ptr);
111 }
112 else {
113 layout->context_ptr_set("constraint", &ptr);
114 }
115
116 /* Constraint type icon. */
117 uiLayout *sub = &layout->row(false);
120 sub->label("", RNA_struct_ui_icon(ptr.type));
121
123
124 uiLayout *row = &layout->row(true);
125
126 row->prop(&ptr, "name", UI_ITEM_NONE, "", ICON_NONE);
127
128 /* Enabled eye icon. */
129 row->prop(&ptr, "enabled", UI_ITEM_NONE, "", ICON_NONE);
130
131 /* Extra operators menu. */
132 row->menu_fn("", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con);
133
134 /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
135 sub = &row->row(false);
138 sub->op("CONSTRAINT_OT_delete", "", ICON_X);
139
140 /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
141 layout->separator();
142
143 /* clear any locks set up for proxies/lib-linking */
144 UI_block_lock_clear(block);
145}
146
148{
149 /* verify we have valid data */
150 if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
151 RNA_warning("Expected constraint on object");
152 return;
153 }
154
155 Object *ob = (Object *)ptr->owner_id;
156 bConstraint *con = static_cast<bConstraint *>(ptr->data);
157
158 if (!ob || !(GS(ob->id.name) == ID_OB)) {
159 RNA_warning("Expected constraint on object");
160 return;
161 }
162
164
165 draw_constraint_header(layout, ob, con);
166}
167
169
170/* -------------------------------------------------------------------- */
175
177#define CONSTRAINT_TYPE_PANEL_PREFIX "OBJECT_PT_"
178#define CONSTRAINT_BONE_TYPE_PANEL_PREFIX "BONE_PT_"
179
184{
185 return (panel->panelname[0] == 'B') && (panel->panelname[1] == 'O') &&
186 (panel->panelname[2] == 'N') && (panel->panelname[3] == 'E');
187}
188
192static void constraint_reorder(bContext *C, Panel *panel, int new_index)
193{
194 const bool constraint_from_bone = constraint_panel_is_bone(panel);
195
196 PointerRNA *con_ptr = UI_panel_custom_data_get(panel);
197 bConstraint *con = (bConstraint *)con_ptr->data;
198
199 PointerRNA props_ptr;
200 wmOperatorType *ot = WM_operatortype_find("CONSTRAINT_OT_move_to_index", false);
202 RNA_string_set(&props_ptr, "constraint", con->name);
203 RNA_int_set(&props_ptr, "index", new_index);
204 /* Set owner to #EDIT_CONSTRAINT_OWNER_OBJECT or #EDIT_CONSTRAINT_OWNER_BONE. */
205 RNA_enum_set(&props_ptr, "owner", constraint_from_bone ? 1 : 0);
207 WM_operator_properties_free(&props_ptr);
208}
209
213static short get_constraint_expand_flag(const bContext * /*C*/, Panel *panel)
214{
215 PointerRNA *con_ptr = UI_panel_custom_data_get(panel);
216 bConstraint *con = (bConstraint *)con_ptr->data;
217
218 return con->ui_expand_flag;
219}
220
224static void set_constraint_expand_flag(const bContext * /*C*/, Panel *panel, short expand_flag)
225{
226 PointerRNA *con_ptr = UI_panel_custom_data_get(panel);
227 bConstraint *con = (bConstraint *)con_ptr->data;
228 con->ui_expand_flag = expand_flag;
229}
230
237static void object_constraint_panel_id(void *md_link, char *r_idname)
238{
239 bConstraint *con = (bConstraint *)md_link;
241
242 /* Cannot get TypeInfo for invalid/legacy constraints. */
243 if (cti == nullptr) {
244 return;
245 }
247}
248
249static void bone_constraint_panel_id(void *md_link, char *r_idname)
250{
251 bConstraint *con = (bConstraint *)md_link;
253
254 /* Cannot get TypeInfo for invalid/legacy constraints. */
255 if (cti == nullptr) {
256 return;
257 }
259}
260
261void uiTemplateConstraints(uiLayout * /*layout*/, bContext *C, bool use_bone_constraints)
262{
263 ARegion *region = CTX_wm_region(C);
264
266 ListBase *constraints = {nullptr};
267 if (use_bone_constraints) {
269 }
270 else if (ob != nullptr) {
272 }
273
274 /* Switch between the bone panel ID function and the object panel ID function. */
275 uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id :
277
278 const bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func);
279
280 if (!panels_match) {
282 for (bConstraint *con =
283 (constraints == nullptr) ? nullptr : static_cast<bConstraint *>(constraints->first);
284 con;
285 con = con->next)
286 {
287 /* Don't show invalid/legacy constraints. */
288 if (con->type == CONSTRAINT_TYPE_NULL) {
289 continue;
290 }
291 /* Don't show temporary constraints (AutoIK and target-less IK constraints). */
292 if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
293 bKinematicConstraint *data = static_cast<bKinematicConstraint *>(con->data);
294 if (data->flag & CONSTRAINT_IK_TEMP) {
295 continue;
296 }
297 }
298
299 char panel_idname[MAX_NAME];
300 panel_id_func(con, panel_idname);
301
302 /* Create custom data RNA pointer. */
303 PointerRNA *con_ptr = MEM_new<PointerRNA>(__func__);
304 *con_ptr = RNA_pointer_create_discrete(&ob->id, &RNA_Constraint, con);
305
306 Panel *new_panel = UI_panel_add_instanced(C, region, &region->panels, panel_idname, con_ptr);
307
308 if (new_panel) {
309 /* Set the list panel functionality function pointers since we don't do it with python. */
312 new_panel->type->reorder = constraint_reorder;
313 }
314 }
315 }
316 else {
317 /* Assuming there's only one group of instanced panels, update the custom data pointers. */
318 Panel *panel = static_cast<Panel *>(region->panels.first);
320 /* Don't show invalid/legacy constraints. */
321 if (con->type == CONSTRAINT_TYPE_NULL) {
322 continue;
323 }
324 /* Don't show temporary constraints (AutoIK and target-less IK constraints). */
325 if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
326 bKinematicConstraint *data = static_cast<bKinematicConstraint *>(con->data);
327 if (data->flag & CONSTRAINT_IK_TEMP) {
328 continue;
329 }
330 }
331
332 /* Move to the next instanced panel corresponding to the next constraint. */
333 while ((panel->type == nullptr) || !(panel->type->flag & PANEL_TYPE_INSTANCED)) {
334 panel = panel->next;
335 BLI_assert(panel != nullptr); /* There shouldn't be fewer panels than constraint panels. */
336 }
337
338 PointerRNA *con_ptr = MEM_new<PointerRNA>(__func__);
339 *con_ptr = RNA_pointer_create_discrete(&ob->id, &RNA_Constraint, con);
340 UI_panel_custom_data_set(panel, con_ptr);
341
342 panel = panel->next;
343 }
344 }
345}
346
347#undef CONSTRAINT_TYPE_PANEL_PREFIX
348#undef CONSTRAINT_BONE_TYPE_PANEL_PREFIX
349
const bConstraintTypeInfo * BKE_constraint_typeinfo_from_type(int type)
ARegion * CTX_wm_region(const bContext *C)
#define BKE_ST_MAXNAME
Definition BKE_screen.hh:72
@ PANEL_TYPE_INSTANCED
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
#define BLI_string_join(...)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
@ ID_OB
@ CONSTRAINT_DISABLE
@ CONSTRAINT_IK_TEMP
@ CONSTRAINT_TYPE_KINEMATIC
@ CONSTRAINT_TYPE_NULL
#define MAX_NAME
Definition DNA_defs.h:50
void(*)(void *data_link, char *r_idname) uiListPanelIDFromDataFunc
#define RNA_warning(format,...)
#define C
Definition RandGen.cpp:29
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
void UI_block_lock_clear(uiBlock *block)
void UI_panels_free_instanced(const bContext *C, ARegion *region)
void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
void UI_block_func_set(uiBlock *block, uiButHandleFunc func, void *arg1, void *arg2)
bool UI_panel_list_matches_data(ARegion *region, ListBase *data, uiListPanelIDFromDataFunc panel_idname_func)
PointerRNA * UI_panel_custom_data_get(const Panel *panel)
void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *ptr)
Panel * UI_panel_add_instanced(const bContext *C, ARegion *region, ListBase *panels, const char *panel_idname, PointerRNA *custom_data)
void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr)
#define UI_ITEM_NONE
BMesh const char void * data
btSequentialImpulseConstraintSolverMt int btPersistentManifold int btTypedConstraint ** constraints
#define GS(x)
static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
static void constraint_active_func(bContext *, void *ob_v, void *con_v)
void uiTemplateConstraintHeader(uiLayout *layout, PointerRNA *ptr)
static void bone_constraint_panel_id(void *md_link, char *r_idname)
static bool constraint_panel_is_bone(Panel *panel)
void uiTemplateConstraints(uiLayout *, bContext *C, bool use_bone_constraints)
static void object_constraint_panel_id(void *md_link, char *r_idname)
static short get_constraint_expand_flag(const bContext *, Panel *panel)
#define CONSTRAINT_BONE_TYPE_PANEL_PREFIX
static void constraint_reorder(bContext *C, Panel *panel, int new_index)
static void set_constraint_expand_flag(const bContext *, Panel *panel, short expand_flag)
#define CONSTRAINT_TYPE_PANEL_PREFIX
static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v)
#define ERROR_LIBDATA_MESSAGE
void constraint_active_set(Object *ob, bConstraint *con)
ListBase * pose_constraint_list(const bContext *C)
Object * context_active_object(const bContext *C)
ListBase * constraint_list_from_constraint(Object *ob, bConstraint *con, bPoseChannel **r_pchan)
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_struct_ui_icon(const StructRNA *type)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
ListBase panels
char name[258]
Definition DNA_ID.h:432
void * first
ListBase constraints
void(* reorder)(bContext *C, Panel *pa, int new_index)
void(* set_list_data_expand_flag)(const bContext *C, Panel *pa, short expand_flag)
short(* get_list_data_expand_flag)(const bContext *C, Panel *pa)
struct PanelType * type
char panelname[64]
struct Panel * next
void * data
Definition RNA_types.hh:53
struct bConstraint * prev
struct bConstraint * next
uiBlock * block() const
void operator_context_set(blender::wm::OpCallContext opcontext)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void red_alert_set(bool red_alert)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void enabled_set(bool enabled)
void menu_fn(blender::StringRefNull name, int icon, uiMenuCreateFunc func, void *arg)
void context_ptr_set(blender::StringRef name, const PointerRNA *ptr)
void ui_units_x_set(float width)
uiLayout & row(bool align)
void emboss_set(blender::ui::EmbossType emboss)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)