Blender V5.0
interface_anim.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdio>
10#include <cstdlib>
11#include <cstring>
12
13#include "DNA_anim_types.h"
14#include "DNA_screen_types.h"
15
16#include "BLI_listbase.h"
17#include "BLI_string.h"
18#include "BLI_string_utf8.h"
19#include "BLI_utildefines.h"
20
21#include "BKE_animsys.h"
22#include "BKE_context.hh"
23#include "BKE_fcurve.hh"
24#include "BKE_fcurve_driver.h"
25#include "BKE_global.hh"
26#include "BKE_nla.hh"
27
29
30#include "ED_keyframing.hh"
31
32#include "ANIM_fcurve.hh"
33#include "ANIM_keyframing.hh"
34
35#include "RNA_access.hh"
36#include "RNA_path.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "interface_intern.hh"
42
44 uiBut *but, AnimData **adt, bAction **action, bool *r_driven, bool *r_special)
45{
46 /* for entire array buttons we check the first component, it's not perfect
47 * but works well enough in typical cases */
48 const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex;
49
50 return BKE_fcurve_find_by_rna_context_ui(static_cast<bContext *>(but->block->evil_C),
51 &but->rnapoin,
52 but->rnaprop,
53 rnaindex,
54 adt,
55 action,
56 r_driven,
57 r_special);
58}
59
60void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
61{
62 /* Clear the flags that this function might set. */
65
66 /* NOTE: "special" is reserved for special F-Curves stored on the animation data
67 * itself (which are used to animate properties of the animation data).
68 * We count those as "animated" too for now
69 */
70 AnimData *adt;
71 bAction *act;
72 bool driven;
73 bool special;
74 FCurve *fcu = ui_but_get_fcurve(but, &adt, &act, &driven, &special);
75
76 if (!fcu) {
77 return;
78 }
79 if (driven) {
80 but->flag |= UI_BUT_DRIVEN;
81 return;
82 }
83
84 /* Empty curves are ignored by the animation evaluation system. */
85 if (BKE_fcurve_is_empty(fcu)) {
86 return;
87 }
88
89 but->flag |= UI_BUT_ANIMATED;
90
91 /* #41525 - When the active action is a NLA strip being edited,
92 * we need to correct the frame number to "look inside" the
93 * remapped action
94 */
95 float cfra = anim_eval_context->eval_time;
96 if (adt) {
98 }
99
102 }
103
104 /* This feature is not implemented at all for the NLA. However, if the NLA just consists of
105 * stashed (i.e. deactivated) Actions, it doesn't do anything, and we can treat it as
106 * non-existent here. Note that this is mostly to play nice with stashed Actions, and doesn't
107 * fully look at all the track & strip flags. */
108 if (adt) {
109 LISTBASE_FOREACH (NlaTrack *, nla_track, &adt->nla_tracks) {
110 if (!(nla_track->flag & NLATRACK_MUTED)) {
111 /* Found a non-muted track, so this NLA is not purely for stashing Actions. */
112 return;
113 }
114 }
115 }
116
118 anim_eval_context, cfra);
119 if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) {
121 }
122}
123
125{
126 uiBut *but_iter = nullptr;
127
130 if (but->block->buttons.is_empty()) {
131 return nullptr;
132 }
133 int i = but->block->but_index(but);
134 i = i > 0 ? i - 1 : but->block->buttons.size() - 1;
135 const int start = i;
136 do {
137 but_iter = but->block->buttons[i].get();
138 if (but_iter != but &&
140 but_iter, &but->decorated_rnapoin, but->decorated_rnaprop, but->decorated_rnaindex))
141 {
142 return but_iter;
143 }
144 i = i > 0 ? i - 1 : but->block->buttons.size() - 1;
145 } while (i != start);
146
147 return nullptr;
148}
149
151{
152 if (!but->decorated_rnapoin.data || !but->decorated_rnaprop) {
153 /* Nothing to do. */
154 return;
155 }
156
158
159 if (!but_anim) {
160 printf("Could not find button with matching property to decorate (%s.%s)\n",
163 return;
164 }
165
166 const int flag = but_anim->flag;
167
168 if (flag & UI_BUT_DRIVEN) {
169 but->icon = ICON_DECORATE_DRIVER;
170 but->toggle_keyframe_on_click = false;
171 }
172 else if (flag & UI_BUT_ANIMATED_KEY) {
173 but->icon = ICON_DECORATE_KEYFRAME;
174 but->toggle_keyframe_on_click = true;
175 }
176 else if (flag & UI_BUT_ANIMATED) {
177 but->icon = ICON_DECORATE_ANIMATE;
178 but->toggle_keyframe_on_click = true;
179 }
180 else if (flag & UI_BUT_OVERRIDDEN) {
181 but->icon = ICON_DECORATE_OVERRIDE;
182 but->toggle_keyframe_on_click = false;
183 }
184 else {
185 but->icon = ICON_DECORATE;
186 but->toggle_keyframe_on_click = true;
187 }
188
189 const int flag_copy = (UI_BUT_DISABLED | UI_BUT_INACTIVE);
190 but->flag = (but->flag & ~flag_copy) | (flag & flag_copy);
191}
192
193bool ui_but_anim_expression_get(uiBut *but, char *str, size_t str_maxncpy)
194{
195 FCurve *fcu;
196 ChannelDriver *driver;
197 bool driven, special;
198
199 fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special);
200
201 if (fcu && driven) {
202 driver = fcu->driver;
203
204 if (driver && driver->type == DRIVER_TYPE_PYTHON) {
205 if (str) {
206 BLI_strncpy_utf8(str, driver->expression, str_maxncpy);
207 }
208 return true;
209 }
210 }
211
212 return false;
213}
214
215bool ui_but_anim_expression_set(uiBut *but, const char *str)
216{
217 FCurve *fcu;
218 ChannelDriver *driver;
219 bool driven, special;
220
221 fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special);
222
223 if (fcu && driven) {
224 driver = fcu->driver;
225
226 if (driver && (driver->type == DRIVER_TYPE_PYTHON)) {
227 bContext *C = static_cast<bContext *>(but->block->evil_C);
228
229 STRNCPY_UTF8(driver->expression, str);
230
231 /* tag driver as needing to be recompiled */
232 BKE_driver_invalidate_expression(driver, true, false);
233
234 /* clear invalid flags which may prevent this from working */
235 driver->flag &= ~DRIVER_FLAG_INVALID;
236 fcu->flag &= ~FCURVE_DISABLED;
237
238 /* this notifier should update the Graph Editor and trigger depsgraph refresh? */
240
242
243 return true;
244 }
245 }
246
247 return false;
248}
249
251{
252 bContext *C = static_cast<bContext *>(but->block->evil_C);
253 ID *id;
254 FCurve *fcu;
255 bool ok = false;
256
257 /* button must have RNA-pointer to a numeric-capable property */
258 if (ELEM(nullptr, but->rnapoin.data, but->rnaprop)) {
259 if (G.debug & G_DEBUG) {
260 printf("ERROR: create expression failed - button has no RNA info attached\n");
261 }
262 return false;
263 }
264
265 if (RNA_property_array_check(but->rnaprop) != 0) {
266 if (but->rnaindex == -1) {
267 if (G.debug & G_DEBUG) {
268 printf("ERROR: create expression failed - cannot create expression for entire array\n");
269 }
270 return false;
271 }
272 }
273
274 /* Make sure we have animation-data for this. */
275 /* FIXME: until materials can be handled by depsgraph,
276 * don't allow drivers to be created for them. */
277 id = but->rnapoin.owner_id;
278 if ((id == nullptr) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) {
279 if (G.debug & G_DEBUG) {
280 printf("ERROR: create expression failed - invalid data-block for adding drivers (%p)\n", id);
281 }
282 return false;
283 }
284
285 /* get path */
286 const std::optional<std::string> path = RNA_path_from_ID_to_property(&but->rnapoin,
287 but->rnaprop);
288 if (!path) {
289 return false;
290 }
291
292 /* create driver */
293 fcu = verify_driver_fcurve(id, path->c_str(), but->rnaindex, DRIVER_FCURVE_KEYFRAMES);
294 if (fcu) {
295 ChannelDriver *driver = fcu->driver;
296
297 if (driver) {
298 /* set type of driver */
299 driver->type = DRIVER_TYPE_PYTHON;
300
301 /* set the expression */
302 /* TODO: need some way of identifying variables used */
303 STRNCPY_UTF8(driver->expression, str);
304
305 /* updates */
306 BKE_driver_invalidate_expression(driver, true, false);
309 ok = true;
310 }
311 }
312
313 return ok;
314}
315
316void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
317{
319 C, scene, &but->rnapoin, but->rnaprop, but->rnaindex, cfra, true);
320}
321
323{
324 /* this operator calls UI_context_active_but_prop_get */
326 "ANIM_OT_copy_driver_button",
328 nullptr,
329 nullptr);
330}
331
333{
334 /* this operator calls UI_context_active_but_prop_get */
336 "ANIM_OT_paste_driver_button",
338 nullptr,
339 nullptr);
340}
341
342void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void * /*arg_dummy*/)
343{
345 uiButDecorator *but_decorate = static_cast<uiButDecorator *>(arg_but);
346 if (!but_decorate->toggle_keyframe_on_click) {
347 return;
348 }
349
350 uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but_decorate);
351 if (!but_anim) {
352 return;
353 }
354
355 /* While click drag the active button may not be `but_decorate`, instead is the but where the
356 * drag started, temporarily override `but_anim` as active. */
357 but_anim->flag |= UI_BUT_ACTIVE_OVERRIDE;
358 wm->op_undo_depth++;
359
360 if (but_anim->flag & UI_BUT_ANIMATED_KEY) {
361 PointerRNA props_ptr;
362 wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false);
364 RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
366 C, ot, blender::wm::OpCallContext::InvokeDefault, &props_ptr, nullptr);
367 WM_operator_properties_free(&props_ptr);
368 }
369 else {
370 PointerRNA props_ptr;
371 wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false);
373 RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
375 C, ot, blender::wm::OpCallContext::InvokeDefault, &props_ptr, nullptr);
376 WM_operator_properties_free(&props_ptr);
377 }
378
379 but_anim->flag &= ~UI_BUT_ACTIVE_OVERRIDE;
380 wm->op_undo_depth--;
381}
Functions to modify FCurves.
Functions to insert, delete or modify keyframes.
AnimationEvalContext BKE_animsys_eval_context_construct_at(const AnimationEvalContext *anim_eval_context, float eval_time) ATTR_WARN_UNUSED_RESULT
Definition anim_sys.cc:746
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
FCurve * BKE_fcurve_find_by_rna_context_ui(bContext *C, const PointerRNA *ptr, PropertyRNA *prop, int rnaindex, AnimData **r_animdata, bAction **r_action, bool *r_driven, bool *r_special)
bool BKE_fcurve_is_empty(const FCurve *fcu)
void BKE_driver_invalidate_expression(struct ChannelDriver *driver, bool expr_changed, bool varname_changed)
@ G_DEBUG
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:549
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, eNlaTime_ConvertModes mode)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
#define ELEM(...)
void DEG_relations_tag_update(Main *bmain)
@ ID_TE
@ ID_MA
@ DRIVER_TYPE_PYTHON
@ DRIVER_FLAG_INVALID
@ FCURVE_DISABLED
@ NLATRACK_MUTED
@ DRIVER_FCURVE_KEYFRAMES
#define C
Definition RandGen.cpp:29
@ UI_BUT_ANIMATED
@ UI_BUT_DISABLED
@ UI_BUT_INACTIVE
@ UI_BUT_OVERRIDDEN
@ UI_BUT_DRIVEN
@ UI_BUT_ANIMATED_KEY
@ UI_BUT_ANIMATED_CHANGED
#define UI_but_is_decorator(but)
#define NC_ANIMATION
Definition WM_types.hh:388
#define ND_KEYFRAME
Definition WM_types.hh:494
int64_t size() const
bool is_empty() const
FCurve * verify_driver_fcurve(ID *id, const char rna_path[], const int array_index, eDriverFCurveCreationMode creation_mode)
Definition drivers.cc:51
bool fcurve_is_changed(PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, const AnimationEvalContext *anim_eval_context)
Lesser Keyframe Checking API call.
#define str(s)
#define GS(x)
#define printf(...)
bool ui_but_rna_equals_ex(const uiBut *but, const PointerRNA *ptr, const PropertyRNA *prop, int index)
Definition interface.cc:771
static FCurve * ui_but_get_fcurve(uiBut *but, AnimData **adt, bAction **action, bool *r_driven, bool *r_special)
void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *)
bool ui_but_anim_expression_create(uiBut *but, const char *str)
void ui_but_anim_decorate_update_from_flag(uiButDecorator *but)
void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
bool ui_but_anim_expression_get(uiBut *but, char *str, size_t str_maxncpy)
static uiBut * ui_but_anim_decorate_find_attached_button(uiButDecorator *but)
void ui_but_anim_copy_driver(bContext *C)
void ui_but_anim_paste_driver(bContext *C)
bool ui_but_anim_expression_set(uiBut *but, const char *str)
@ UI_BUT_ACTIVE_OVERRIDE
#define G(x, y, z)
bool fcurve_frame_has_keyframe(const FCurve *fcu, float frame)
bool autokeyframe_property(bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra, bool only_if_property_keyed)
bool RNA_property_array_check(PropertyRNA *prop)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
const char * RNA_struct_identifier(const StructRNA *type)
const char * RNA_property_identifier(const PropertyRNA *prop)
std::optional< std::string > RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
Definition rna_path.cc:1173
ListBase nla_tracks
char expression[256]
ChannelDriver * driver
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
ID * owner_id
Definition RNA_types.hh:51
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
blender::Vector< std::unique_ptr< uiBut > > buttons
int but_index(const uiBut *but) const
Definition interface.cc:274
PointerRNA decorated_rnapoin
PropertyRNA * decorated_rnaprop
PropertyRNA * rnaprop
uiBlock * block
BIFIconID icon
PointerRNA rnapoin
i
Definition text_draw.cc:230
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
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)
uint8_t flag
Definition wm_window.cc:145