1 /*************************************************************************/
2 /* animation_editor.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30 #include "animation_editor.h"
31 #include "editor/plugins/animation_player_editor_plugin.h"
32 #include "editor_node.h"
33 #include "editor_settings.h"
34 #include "io/resource_saver.h"
35 #include "os/keyboard.h"
36 #include "os/os.h"
37 #include "pair.h"
38 #include "scene/gui/separator.h"
39 #include "scene/main/viewport.h"
40 /* Missing to fix:
41
42 *Set
43 *Find better source for hint for edited value keys
44 * + button on track to add a key
45 * when clicked for first time, erase selection of not selected at first
46 * automatically create discrete/continuous tracks!!
47 *when create track do undo/redo
48 */
49
50 class AnimationCurveEdit : public Control {
51 OBJ_TYPE(AnimationCurveEdit, Control);
52
53 public:
54 enum Mode {
55 MODE_DISABLED,
56 MODE_SINGLE,
57 MODE_MULTIPLE
58 };
59
60 private:
61 Set<float> multiples;
62 float transition;
63 Mode mode;
64
_notification(int p_what)65 void _notification(int p_what) {
66
67 if (p_what == NOTIFICATION_DRAW) {
68
69 RID ci = get_canvas_item();
70
71 Size2 s = get_size();
72 Rect2 r(Point2(), s);
73
74 //r=r.grow(3);
75 Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
76 sb->draw(ci, r);
77 r.size -= sb->get_minimum_size();
78 r.pos += sb->get_offset();
79 //VisualServer::get_singleton()->canvas_item_add
80
81 Ref<Font> f = get_font("font", "Label");
82 r = r.grow(-2);
83 Color color = get_color("font_color", "Label");
84
85 int points = 48;
86 if (mode == MODE_MULTIPLE) {
87
88 Color mcolor = color;
89 mcolor.a *= 0.3;
90
91 Set<float>::Element *E = multiples.front();
92 for (int j = 0; j < 16; j++) {
93
94 if (!E)
95 break;
96
97 float prev = 1.0;
98 float exp = E->get();
99 bool flip = false; //hint_text=="attenuation";
100
101 for (int i = 1; i <= points; i++) {
102
103 float ifl = i / float(points);
104 float iflp = (i - 1) / float(points);
105
106 float h = 1.0 - Math::ease(ifl, exp);
107
108 if (flip) {
109 ifl = 1.0 - ifl;
110 iflp = 1.0 - iflp;
111 }
112
113 VisualServer::get_singleton()->canvas_item_add_line(ci, r.pos + Point2(iflp * r.size.width, prev * r.size.height), r.pos + Point2(ifl * r.size.width, h * r.size.height), mcolor);
114 prev = h;
115 }
116
117 E = E->next();
118 }
119 }
120
121 float exp = transition;
122 if (mode != MODE_DISABLED) {
123
124 float prev = 1.0;
125
126 bool flip = false; //hint_text=="attenuation";
127
128 for (int i = 1; i <= points; i++) {
129
130 float ifl = i / float(points);
131 float iflp = (i - 1) / float(points);
132
133 float h = 1.0 - Math::ease(ifl, exp);
134
135 if (flip) {
136 ifl = 1.0 - ifl;
137 iflp = 1.0 - iflp;
138 }
139
140 VisualServer::get_singleton()->canvas_item_add_line(ci, r.pos + Point2(iflp * r.size.width, prev * r.size.height), r.pos + Point2(ifl * r.size.width, h * r.size.height), color);
141 prev = h;
142 }
143 }
144
145 String txt = String::num(exp, 2);
146 if (mode == MODE_DISABLED) {
147 txt = TTR("Disabled");
148 } else if (mode == MODE_MULTIPLE) {
149 txt += " - " + TTR("All Selection");
150 }
151
152 f->draw(ci, Point2(10, 10 + f->get_ascent()), txt, color);
153 }
154 }
155
_input_event(const InputEvent & p_ev)156 void _input_event(const InputEvent &p_ev) {
157 if (p_ev.type == InputEvent::MOUSE_MOTION && p_ev.mouse_motion.button_mask & BUTTON_MASK_LEFT) {
158
159 if (mode == MODE_DISABLED)
160 return;
161
162 float rel = p_ev.mouse_motion.relative_x;
163 if (rel == 0)
164 return;
165
166 bool flip = false;
167
168 if (flip)
169 rel = -rel;
170
171 float val = transition;
172 if (val == 0)
173 return;
174 bool sg = val < 0;
175 val = Math::absf(val);
176
177 val = Math::log(val) / Math::log(2);
178 //logspace
179 val += rel * 0.05;
180 //
181
182 val = Math::pow(2, val);
183 if (sg)
184 val = -val;
185
186 transition = val;
187 update();
188 //emit_signal("variant_changed");
189 emit_signal("transition_changed", transition);
190 }
191 }
192
193 public:
_bind_methods()194 static void _bind_methods() {
195
196 // ObjectTypeDB::bind_method("_update_obj",&AnimationKeyEdit::_update_obj);
197 ObjectTypeDB::bind_method("_input_event", &AnimationCurveEdit::_input_event);
198 ADD_SIGNAL(MethodInfo("transition_changed"));
199 }
200
set_mode(Mode p_mode)201 void set_mode(Mode p_mode) {
202
203 mode = p_mode;
204 update();
205 }
206
clear_multiples()207 void clear_multiples() {
208 multiples.clear();
209 update();
210 }
set_multiple(float p_transition)211 void set_multiple(float p_transition) {
212
213 multiples.insert(p_transition);
214 }
215
set_transition(float p_transition)216 void set_transition(float p_transition) {
217 transition = p_transition;
218 update();
219 }
220
get_transition() const221 float get_transition() const {
222 return transition;
223 }
224
force_transition(float p_value)225 void force_transition(float p_value) {
226 if (mode == MODE_DISABLED)
227 return;
228 transition = p_value;
229 emit_signal("transition_changed", p_value);
230 update();
231 }
232
AnimationCurveEdit()233 AnimationCurveEdit() {
234
235 transition = 1.0;
236 set_default_cursor_shape(CURSOR_HSPLIT);
237 mode = MODE_DISABLED;
238 }
239 };
240
241 class AnimationKeyEdit : public Object {
242
243 OBJ_TYPE(AnimationKeyEdit, Object);
244
245 public:
246 bool setting;
247 bool hidden;
248
_bind_methods()249 static void _bind_methods() {
250
251 ObjectTypeDB::bind_method("_update_obj", &AnimationKeyEdit::_update_obj);
252 ObjectTypeDB::bind_method("_key_ofs_changed", &AnimationKeyEdit::_key_ofs_changed);
253 }
254
255 //PopupDialog *ke_dialog;
256
_fix_node_path(Variant & value)257 void _fix_node_path(Variant &value) {
258
259 NodePath np = value;
260
261 if (np == NodePath())
262 return;
263
264 Node *root = EditorNode::get_singleton()->get_tree()->get_root();
265
266 Node *np_node = root->get_node(np);
267 ERR_FAIL_COND(!np_node);
268
269 Node *edited_node = root->get_node(base);
270 ERR_FAIL_COND(!edited_node);
271
272 value = edited_node->get_path_to(np_node);
273 }
274
_update_obj(const Ref<Animation> & p_anim)275 void _update_obj(const Ref<Animation> &p_anim) {
276 if (setting)
277 return;
278 if (hidden)
279 return;
280 if (!(animation == p_anim))
281 return;
282 notify_change();
283 }
284
_key_ofs_changed(const Ref<Animation> & p_anim,float from,float to)285 void _key_ofs_changed(const Ref<Animation> &p_anim, float from, float to) {
286 if (hidden)
287 return;
288 if (!(animation == p_anim))
289 return;
290 if (from != key_ofs)
291 return;
292 key_ofs = to;
293 if (setting)
294 return;
295 notify_change();
296 }
297
_set(const StringName & p_name,const Variant & p_value)298 bool _set(const StringName &p_name, const Variant &p_value) {
299
300 int key = animation->track_find_key(track, key_ofs, true);
301 ERR_FAIL_COND_V(key == -1, false);
302
303 String name = p_name;
304 if (name == "time") {
305
306 float new_time = p_value;
307 if (new_time == key_ofs)
308 return true;
309
310 int existing = animation->track_find_key(track, new_time, true);
311
312 setting = true;
313 undo_redo->create_action(TTR("Move Add Key"), UndoRedo::MERGE_ENDS);
314
315 Variant val = animation->track_get_key_value(track, key);
316 float trans = animation->track_get_key_transition(track, key);
317
318 undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
319 undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
320 undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
321 undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_pos", track, new_time);
322 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
323 undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
324
325 if (existing != -1) {
326 Variant v = animation->track_get_key_value(track, existing);
327 float trans = animation->track_get_key_transition(track, existing);
328 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, new_time, v, trans);
329 }
330
331 undo_redo->commit_action();
332 setting = false;
333
334 return true;
335 } else if (name == "easing") {
336
337 float val = p_value;
338 float prev_val = animation->track_get_key_transition(track, key);
339 setting = true;
340 undo_redo->create_action(TTR("Anim Change Transition"), UndoRedo::MERGE_ENDS);
341 undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val);
342 undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val);
343 undo_redo->add_do_method(this, "_update_obj", animation);
344 undo_redo->add_undo_method(this, "_update_obj", animation);
345 undo_redo->commit_action();
346 setting = false;
347 return true;
348 }
349
350 switch (animation->track_get_type(track)) {
351
352 case Animation::TYPE_TRANSFORM: {
353
354 Dictionary d_old = animation->track_get_key_value(track, key);
355 Dictionary d_new = d_old;
356 d_new[p_name] = p_value;
357 setting = true;
358 undo_redo->create_action(TTR("Anim Change Transform"));
359 undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
360 undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
361 undo_redo->add_do_method(this, "_update_obj", animation);
362 undo_redo->add_undo_method(this, "_update_obj", animation);
363 undo_redo->commit_action();
364 setting = false;
365 return true;
366
367 } break;
368 case Animation::TYPE_VALUE: {
369
370 if (name == "value") {
371
372 Variant value = p_value;
373
374 if (value.get_type() == Variant::NODE_PATH) {
375
376 _fix_node_path(value);
377 }
378
379 setting = true;
380 undo_redo->create_action(TTR("Anim Change Value"), UndoRedo::MERGE_ENDS);
381 Variant prev = animation->track_get_key_value(track, key);
382 undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, value);
383 undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, prev);
384 undo_redo->add_do_method(this, "_update_obj", animation);
385 undo_redo->add_undo_method(this, "_update_obj", animation);
386 undo_redo->commit_action();
387 setting = false;
388 return true;
389 }
390
391 } break;
392 case Animation::TYPE_METHOD: {
393
394 Dictionary d_old = animation->track_get_key_value(track, key);
395 Dictionary d_new = d_old;
396
397 bool change_notify_deserved = false;
398 bool mergeable = false;
399
400 if (name == "name") {
401
402 d_new["method"] = p_value;
403 }
404
405 if (name == "arg_count") {
406
407 Vector<Variant> args = d_old["args"];
408 args.resize(p_value);
409 d_new["args"] = args;
410 change_notify_deserved = true;
411 }
412
413 if (name.begins_with("args/")) {
414
415 Vector<Variant> args = d_old["args"];
416 int idx = name.get_slice("/", 1).to_int();
417 ERR_FAIL_INDEX_V(idx, args.size(), false);
418
419 String what = name.get_slice("/", 2);
420 if (what == "type") {
421 Variant::Type t = Variant::Type(int(p_value));
422
423 if (t != args[idx].get_type()) {
424 Variant::CallError err;
425 if (Variant::can_convert(args[idx].get_type(), t)) {
426 Variant old = args[idx];
427 Variant *ptrs[1] = { &old };
428 args[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err);
429 } else {
430
431 args[idx] = Variant::construct(t, NULL, 0, err);
432 }
433 change_notify_deserved = true;
434 d_new["args"] = args;
435 }
436 }
437 if (what == "value") {
438
439 Variant value = p_value;
440 if (value.get_type() == Variant::NODE_PATH) {
441
442 _fix_node_path(value);
443 }
444
445 args[idx] = value;
446 d_new["args"] = args;
447 mergeable = true;
448 }
449 }
450
451 if (mergeable)
452 undo_redo->create_action(TTR("Anim Change Call"), UndoRedo::MERGE_ENDS);
453 else
454 undo_redo->create_action(TTR("Anim Change Call"));
455
456 Variant prev = animation->track_get_key_value(track, key);
457 setting = true;
458 undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
459 undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
460 undo_redo->add_do_method(this, "_update_obj", animation);
461 undo_redo->add_undo_method(this, "_update_obj", animation);
462 undo_redo->commit_action();
463 setting = false;
464 if (change_notify_deserved)
465 notify_change();
466 return true;
467 } break;
468 }
469
470 return false;
471 }
472
_get(const StringName & p_name,Variant & r_ret) const473 bool _get(const StringName &p_name, Variant &r_ret) const {
474
475 int key = animation->track_find_key(track, key_ofs, true);
476 ERR_FAIL_COND_V(key == -1, false);
477
478 String name = p_name;
479 if (name == "time") {
480 r_ret = key_ofs;
481 return true;
482 } else if (name == "easing") {
483 r_ret = animation->track_get_key_transition(track, key);
484 return true;
485 }
486
487 switch (animation->track_get_type(track)) {
488
489 case Animation::TYPE_TRANSFORM: {
490
491 Dictionary d = animation->track_get_key_value(track, key);
492 ERR_FAIL_COND_V(!d.has(name), false);
493 r_ret = d[p_name];
494 return true;
495
496 } break;
497 case Animation::TYPE_VALUE: {
498
499 if (name == "value") {
500 r_ret = animation->track_get_key_value(track, key);
501 return true;
502 }
503
504 } break;
505 case Animation::TYPE_METHOD: {
506
507 Dictionary d = animation->track_get_key_value(track, key);
508
509 if (name == "name") {
510
511 ERR_FAIL_COND_V(!d.has("method"), false);
512 r_ret = d["method"];
513 return true;
514 }
515
516 ERR_FAIL_COND_V(!d.has("args"), false);
517
518 Vector<Variant> args = d["args"];
519
520 if (name == "arg_count") {
521
522 r_ret = args.size();
523 return true;
524 }
525
526 if (name.begins_with("args/")) {
527
528 int idx = name.get_slice("/", 1).to_int();
529 ERR_FAIL_INDEX_V(idx, args.size(), false);
530
531 String what = name.get_slice("/", 2);
532 if (what == "type") {
533 r_ret = args[idx].get_type();
534 return true;
535 }
536 if (what == "value") {
537 r_ret = args[idx];
538 return true;
539 }
540 }
541
542 } break;
543 }
544
545 return false;
546 }
_get_property_list(List<PropertyInfo> * p_list) const547 void _get_property_list(List<PropertyInfo> *p_list) const {
548
549 if (animation.is_null())
550 return;
551
552 ERR_FAIL_INDEX(track, animation->get_track_count());
553 int key = animation->track_find_key(track, key_ofs, true);
554 ERR_FAIL_COND(key == -1);
555
556 p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
557
558 switch (animation->track_get_type(track)) {
559
560 case Animation::TYPE_TRANSFORM: {
561
562 p_list->push_back(PropertyInfo(Variant::VECTOR3, "loc"));
563 p_list->push_back(PropertyInfo(Variant::QUAT, "rot"));
564 p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
565
566 } break;
567 case Animation::TYPE_VALUE: {
568
569 Variant v = animation->track_get_key_value(track, key);
570
571 if (hint.type != Variant::NIL) {
572
573 PropertyInfo pi = hint;
574 pi.name = "value";
575 p_list->push_back(pi);
576 } else {
577
578 PropertyHint hint = PROPERTY_HINT_NONE;
579 String hint_string;
580
581 if (v.get_type() == Variant::OBJECT) {
582 //could actually check the object property if exists..? yes i will!
583 Ref<Resource> res = v;
584 if (res.is_valid()) {
585
586 hint = PROPERTY_HINT_RESOURCE_TYPE;
587 hint_string = res->get_type();
588 }
589 }
590
591 if (v.get_type() != Variant::NIL)
592 p_list->push_back(PropertyInfo(v.get_type(), "value", hint, hint_string));
593 }
594
595 } break;
596 case Animation::TYPE_METHOD: {
597
598 p_list->push_back(PropertyInfo(Variant::STRING, "name"));
599 p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,5,1"));
600
601 Dictionary d = animation->track_get_key_value(track, key);
602 ERR_FAIL_COND(!d.has("args"));
603 Vector<Variant> args = d["args"];
604 String vtypes;
605 for (int i = 0; i < Variant::VARIANT_MAX; i++) {
606
607 if (i > 0)
608 vtypes += ",";
609 vtypes += Variant::get_type_name(Variant::Type(i));
610 }
611
612 for (int i = 0; i < args.size(); i++) {
613
614 p_list->push_back(PropertyInfo(Variant::INT, "args/" + itos(i) + "/type", PROPERTY_HINT_ENUM, vtypes));
615 if (args[i].get_type() != Variant::NIL)
616 p_list->push_back(PropertyInfo(args[i].get_type(), "args/" + itos(i) + "/value"));
617 }
618
619 } break;
620 }
621
622 //if (animation->track_get_type(track)!=Animation::TYPE_METHOD)
623 // p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
624 }
625
626 UndoRedo *undo_redo;
627 Ref<Animation> animation;
628 int track;
629 float key_ofs;
630
631 PropertyInfo hint;
632 NodePath base;
633
notify_change()634 void notify_change() {
635
636 _change_notify();
637 }
638
AnimationKeyEdit()639 AnimationKeyEdit() {
640 hidden = true;
641 key_ofs = 0;
642 track = -1;
643 setting = false;
644 }
645 };
646
_menu_add_track(int p_type)647 void AnimationKeyEditor::_menu_add_track(int p_type) {
648
649 ERR_FAIL_COND(!animation.is_valid());
650
651 switch (p_type) {
652
653 case ADD_TRACK_MENU_ADD_CALL_TRACK: {
654 if (root) {
655 call_select->popup_centered_ratio();
656 break;
657 }
658 } break;
659 case ADD_TRACK_MENU_ADD_VALUE_TRACK:
660 case ADD_TRACK_MENU_ADD_TRANSFORM_TRACK: {
661
662 undo_redo->create_action(TTR("Anim Add Track"));
663 undo_redo->add_do_method(animation.ptr(), "add_track", p_type);
664 undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), ".");
665 undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
666 undo_redo->commit_action();
667
668 } break;
669 }
670 }
671
_anim_duplicate_keys(bool transpose)672 void AnimationKeyEditor::_anim_duplicate_keys(bool transpose) {
673 //duplicait!
674 if (selection.size() && animation.is_valid() && selected_track >= 0 && selected_track < animation->get_track_count()) {
675
676 int top_track = 0x7FFFFFFF;
677 float top_time = 1e10;
678 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
679
680 const SelectedKey &sk = E->key();
681
682 float t = animation->track_get_key_time(sk.track, sk.key);
683 if (t < top_time)
684 top_time = t;
685 if (sk.track < top_track)
686 top_track = sk.track;
687 }
688 ERR_FAIL_COND(top_track == 0x7FFFFFFF || top_time == 1e10);
689
690 //
691
692 int start_track = transpose ? selected_track : top_track;
693
694 undo_redo->create_action(TTR("Anim Duplicate Keys"));
695
696 List<Pair<int, float> > new_selection_values;
697
698 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
699
700 const SelectedKey &sk = E->key();
701
702 float t = animation->track_get_key_time(sk.track, sk.key);
703
704 float dst_time = t + (timeline_pos - top_time);
705 int dst_track = sk.track + (start_track - top_track);
706
707 if (dst_track < 0 || dst_track >= animation->get_track_count())
708 continue;
709
710 if (animation->track_get_type(dst_track) != animation->track_get_type(sk.track))
711 continue;
712
713 int existing_idx = animation->track_find_key(dst_track, dst_time, true);
714
715 undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
716 undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_pos", dst_track, dst_time);
717
718 Pair<int, float> p;
719 p.first = dst_track;
720 p.second = dst_time;
721 new_selection_values.push_back(p);
722
723 if (existing_idx != -1) {
724
725 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(dst_track, existing_idx), animation->track_get_key_transition(dst_track, existing_idx));
726 }
727 }
728
729 undo_redo->commit_action();
730
731 //reselect duplicated
732
733 Map<SelectedKey, KeyInfo> new_selection;
734 for (List<Pair<int, float> >::Element *E = new_selection_values.front(); E; E = E->next()) {
735
736 int track = E->get().first;
737 float time = E->get().second;
738
739 int existing_idx = animation->track_find_key(track, time, true);
740
741 if (existing_idx == -1)
742 continue;
743 SelectedKey sk2;
744 sk2.track = track;
745 sk2.key = existing_idx;
746
747 KeyInfo ki;
748 ki.pos = time;
749
750 new_selection[sk2] = ki;
751 }
752
753 selection = new_selection;
754 track_editor->update();
755 _edit_if_single_selection();
756 }
757 }
758
_menu_track(int p_type)759 void AnimationKeyEditor::_menu_track(int p_type) {
760
761 ERR_FAIL_COND(!animation.is_valid());
762
763 last_menu_track_opt = p_type;
764 switch (p_type) {
765
766 case TRACK_MENU_SCALE:
767 case TRACK_MENU_SCALE_PIVOT: {
768
769 scale_dialog->popup_centered(Size2(200, 100));
770 } break;
771 case TRACK_MENU_MOVE_UP: {
772
773 int idx = selected_track;
774 if (idx > 0 && idx < animation->get_track_count()) {
775 undo_redo->create_action(TTR("Move Anim Track Up"));
776 undo_redo->add_do_method(animation.ptr(), "track_move_down", idx);
777 undo_redo->add_undo_method(animation.ptr(), "track_move_up", idx - 1);
778 undo_redo->commit_action();
779 selected_track = idx - 1;
780 }
781
782 } break;
783 case TRACK_MENU_MOVE_DOWN: {
784
785 int idx = selected_track;
786 if (idx >= 0 && idx < animation->get_track_count() - 1) {
787 undo_redo->create_action(TTR("Move Anim Track Down"));
788 undo_redo->add_do_method(animation.ptr(), "track_move_up", idx);
789 undo_redo->add_undo_method(animation.ptr(), "track_move_down", idx + 1);
790 undo_redo->commit_action();
791 selected_track = idx + 1;
792 }
793
794 } break;
795 case TRACK_MENU_REMOVE: {
796
797 int idx = selected_track;
798 if (idx >= 0 && idx < animation->get_track_count()) {
799 undo_redo->create_action(TTR("Remove Anim Track"));
800 undo_redo->add_do_method(animation.ptr(), "remove_track", idx);
801 undo_redo->add_undo_method(animation.ptr(), "add_track", animation->track_get_type(idx), idx);
802 undo_redo->add_undo_method(animation.ptr(), "track_set_path", idx, animation->track_get_path(idx));
803 //todo interpolation
804 for (int i = 0; i < animation->track_get_key_count(idx); i++) {
805
806 Variant v = animation->track_get_key_value(idx, i);
807 float time = animation->track_get_key_time(idx, i);
808 float trans = animation->track_get_key_transition(idx, i);
809
810 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", idx, time, v);
811 undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", idx, i, trans);
812 }
813
814 undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", idx, animation->track_get_interpolation_type(idx));
815 if (animation->track_get_type(idx) == Animation::TYPE_VALUE) {
816 undo_redo->add_undo_method(animation.ptr(), "value_track_set_update_mode", idx, animation->value_track_get_update_mode(idx));
817 }
818
819 undo_redo->commit_action();
820 }
821
822 } break;
823 case TRACK_MENU_DUPLICATE:
824 case TRACK_MENU_DUPLICATE_TRANSPOSE: {
825
826 _anim_duplicate_keys(p_type == TRACK_MENU_DUPLICATE_TRANSPOSE);
827 } break;
828 case TRACK_MENU_SET_ALL_TRANS_LINEAR:
829 case TRACK_MENU_SET_ALL_TRANS_CONSTANT:
830 case TRACK_MENU_SET_ALL_TRANS_OUT:
831 case TRACK_MENU_SET_ALL_TRANS_IN:
832 case TRACK_MENU_SET_ALL_TRANS_INOUT:
833 case TRACK_MENU_SET_ALL_TRANS_OUTIN: {
834
835 if (!selection.size() || !animation.is_valid())
836 break;
837
838 float t = 0;
839 switch (p_type) {
840 case TRACK_MENU_SET_ALL_TRANS_LINEAR: t = 1.0; break;
841 case TRACK_MENU_SET_ALL_TRANS_CONSTANT: t = 0.0; break;
842 case TRACK_MENU_SET_ALL_TRANS_OUT: t = 0.5; break;
843 case TRACK_MENU_SET_ALL_TRANS_IN: t = 2.0; break;
844 case TRACK_MENU_SET_ALL_TRANS_INOUT: t = -0.5; break;
845 case TRACK_MENU_SET_ALL_TRANS_OUTIN: t = -2.0; break;
846 }
847
848 undo_redo->create_action(TTR("Set Transitions to:") + " " + rtos(t));
849
850 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
851
852 const SelectedKey &sk = E->key();
853
854 undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", sk.track, sk.key, t);
855 undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", sk.track, sk.key, animation->track_get_key_transition(sk.track, sk.key));
856 }
857
858 undo_redo->commit_action();
859
860 } break;
861 case TRACK_MENU_NEXT_STEP: {
862
863 if (animation.is_null())
864 break;
865 float step = animation->get_step();
866 if (step == 0)
867 step = 1;
868
869 float pos = timeline_pos;
870
871 pos = Math::stepify(pos + step, step);
872 if (pos > animation->get_length())
873 pos = animation->get_length();
874 timeline_pos = pos;
875 track_pos->update();
876 emit_signal("timeline_changed", pos, true);
877
878 } break;
879 case TRACK_MENU_PREV_STEP: {
880 if (animation.is_null())
881 break;
882 float step = animation->get_step();
883 if (step == 0)
884 step = 1;
885
886 float pos = timeline_pos;
887 pos = Math::stepify(pos - step, step);
888 if (pos < 0)
889 pos = 0;
890 timeline_pos = pos;
891 track_pos->update();
892 emit_signal("timeline_changed", pos, true);
893
894 } break;
895
896 case TRACK_MENU_OPTIMIZE: {
897
898 optimize_dialog->popup_centered(Size2(250, 180));
899 } break;
900 case TRACK_MENU_CLEAN_UP: {
901
902 cleanup_dialog->popup_centered_minsize(Size2(300, 0));
903 } break;
904 case TRACK_MENU_CLEAN_UP_CONFIRM: {
905
906 if (cleanup_all->is_pressed()) {
907 List<StringName> names;
908 AnimationPlayerEditor::singleton->get_player()->get_animation_list(&names);
909 for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
910 _cleanup_animation(AnimationPlayerEditor::singleton->get_player()->get_animation(E->get()));
911 }
912 } else {
913 _cleanup_animation(animation);
914 }
915 } break;
916 case CURVE_SET_LINEAR: {
917 curve_edit->force_transition(1.0);
918
919 } break;
920 case CURVE_SET_IN: {
921
922 curve_edit->force_transition(4.0);
923
924 } break;
925 case CURVE_SET_OUT: {
926
927 curve_edit->force_transition(0.25);
928 } break;
929 case CURVE_SET_INOUT: {
930 curve_edit->force_transition(-4);
931
932 } break;
933 case CURVE_SET_OUTIN: {
934
935 curve_edit->force_transition(-0.25);
936 } break;
937 case CURVE_SET_CONSTANT: {
938
939 curve_edit->force_transition(0);
940 } break;
941 }
942 }
943
_cleanup_animation(Ref<Animation> p_animation)944 void AnimationKeyEditor::_cleanup_animation(Ref<Animation> p_animation) {
945
946 for (int i = 0; i < p_animation->get_track_count(); i++) {
947
948 bool prop_exists = false;
949 Variant::Type valid_type = Variant::NIL;
950 Object *obj = NULL;
951
952 RES res;
953 Node *node = root->get_node_and_resource(p_animation->track_get_path(i), res);
954
955 if (res.is_valid()) {
956 obj = res.ptr();
957 } else if (node) {
958 obj = node;
959 }
960
961 if (obj && p_animation->track_get_type(i) == Animation::TYPE_VALUE) {
962 valid_type = obj->get_static_property_type(p_animation->track_get_path(i).get_property(), &prop_exists);
963 }
964
965 if (!obj && cleanup_tracks->is_pressed()) {
966
967 p_animation->remove_track(i);
968 i--;
969 continue;
970 }
971
972 if (!prop_exists || p_animation->track_get_type(i) != Animation::TYPE_VALUE || cleanup_keys->is_pressed() == false)
973 continue;
974
975 for (int j = 0; j < p_animation->track_get_key_count(i); j++) {
976
977 Variant v = p_animation->track_get_key_value(i, j);
978
979 if (!Variant::can_convert(v.get_type(), valid_type)) {
980 p_animation->track_remove_key(i, j);
981 j--;
982 }
983 }
984
985 if (p_animation->track_get_key_count(i) == 0 && cleanup_tracks->is_pressed()) {
986 p_animation->remove_track(i);
987 i--;
988 }
989 }
990
991 undo_redo->clear_history();
992 _update_paths();
993 }
994
_animation_optimize()995 void AnimationKeyEditor::_animation_optimize() {
996
997 animation->optimize(optimize_linear_error->get_val(), optimize_angular_error->get_val(), optimize_max_angle->get_val());
998 track_editor->update();
999 undo_redo->clear_history();
1000 }
1001
_get_zoom_scale() const1002 float AnimationKeyEditor::_get_zoom_scale() const {
1003
1004 float zv = zoom->get_val();
1005 if (zv < 1) {
1006 zv = 1.0 - zv;
1007 return Math::pow(1.0 + zv, 8.0) * 100;
1008 } else {
1009 return 1.0 / Math::pow(zv, 8.0) * 100;
1010 }
1011 }
1012
_track_pos_draw()1013 void AnimationKeyEditor::_track_pos_draw() {
1014
1015 if (!animation.is_valid()) {
1016 return;
1017 }
1018
1019 Ref<StyleBox> style = get_stylebox("normal", "TextEdit");
1020 Size2 size = track_editor->get_size() - style->get_minimum_size();
1021 Size2 ofs = style->get_offset();
1022
1023 int settings_limit = size.width - right_data_size_cache;
1024 int name_limit = settings_limit * name_column_ratio;
1025
1026 float keys_from = h_scroll->get_val();
1027 float zoom_scale = _get_zoom_scale();
1028 float keys_to = keys_from + (settings_limit - name_limit) / zoom_scale;
1029
1030 //will move to separate control! (for speedup)
1031 if (timeline_pos >= keys_from && timeline_pos < keys_to) {
1032 //draw position
1033 int pixel = (timeline_pos - h_scroll->get_val()) * zoom_scale;
1034 pixel += name_limit;
1035 track_pos->draw_line(ofs + Point2(pixel, 0), ofs + Point2(pixel, size.height), Color(1, 0.3, 0.3, 0.8));
1036 }
1037 }
1038
_track_editor_draw()1039 void AnimationKeyEditor::_track_editor_draw() {
1040
1041 VisualServer::get_singleton()->canvas_item_set_clip(track_editor->get_canvas_item(), true);
1042
1043 if (animation.is_valid() && animation->get_track_count()) {
1044 if (selected_track < 0)
1045 selected_track = 0;
1046 else if (selected_track >= animation->get_track_count())
1047 selected_track = animation->get_track_count() - 1;
1048 }
1049
1050 track_pos->update();
1051 Control *te = track_editor;
1052 Ref<StyleBox> style = get_stylebox("normal", "TextEdit");
1053 te->draw_style_box(style, Rect2(Point2(), track_editor->get_size()));
1054
1055 if (te->has_focus()) {
1056 te->draw_style_box(get_stylebox("bg_focus", "Tree"), Rect2(Point2(), track_editor->get_size()));
1057 }
1058
1059 if (!animation.is_valid()) {
1060 v_scroll->hide();
1061 h_scroll->hide();
1062 menu_add_track->set_disabled(true);
1063 menu_track->set_disabled(true);
1064 edit_button->set_disabled(true);
1065 key_editor_tab->hide();
1066 move_up_button->set_disabled(true);
1067 move_down_button->set_disabled(true);
1068 remove_button->set_disabled(true);
1069
1070 return;
1071 }
1072
1073 menu_add_track->set_disabled(false);
1074 menu_track->set_disabled(false);
1075 edit_button->set_disabled(false);
1076 move_up_button->set_disabled(false);
1077 move_down_button->set_disabled(false);
1078 remove_button->set_disabled(false);
1079 if (edit_button->is_pressed())
1080 key_editor_tab->show();
1081
1082 te_drawing = true;
1083
1084 Size2 size = te->get_size() - style->get_minimum_size();
1085 Size2 ofs = style->get_offset();
1086
1087 Ref<Font> font = te->get_font("font", "Tree");
1088 int sep = get_constant("vseparation", "Tree");
1089 int hsep = get_constant("hseparation", "Tree");
1090 Color color = get_color("font_color", "Tree");
1091 Color sepcolor = get_color("guide_color", "Tree");
1092 Color timecolor = get_color("prop_subsection", "Editor");
1093 timecolor = Color::html("ff4a414f");
1094 Color hover_color = Color(1, 1, 1, 0.05);
1095 Color select_color = Color(1, 1, 1, 0.1);
1096 Color invalid_path_color = Color(1, 0.6, 0.4, 0.5);
1097 Color track_select_color = Color::html("ffbd8e8e");
1098
1099 Ref<Texture> remove_icon = get_icon("Remove", "EditorIcons");
1100 Ref<Texture> move_up_icon = get_icon("MoveUp", "EditorIcons");
1101 Ref<Texture> move_down_icon = get_icon("MoveDown", "EditorIcons");
1102 Ref<Texture> remove_icon_hl = get_icon("RemoveHl", "EditorIcons");
1103 Ref<Texture> move_up_icon_hl = get_icon("MoveUpHl", "EditorIcons");
1104 Ref<Texture> move_down_icon_hl = get_icon("MoveDownHl", "EditorIcons");
1105 Ref<Texture> add_key_icon = get_icon("TrackAddKey", "EditorIcons");
1106 Ref<Texture> add_key_icon_hl = get_icon("TrackAddKeyHl", "EditorIcons");
1107 Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
1108 Ref<Texture> interp_icon[3] = {
1109 get_icon("InterpRaw", "EditorIcons"),
1110 get_icon("InterpLinear", "EditorIcons"),
1111 get_icon("InterpCubic", "EditorIcons")
1112 };
1113 Ref<Texture> cont_icon[3] = {
1114 get_icon("TrackContinuous", "EditorIcons"),
1115 get_icon("TrackDiscrete", "EditorIcons"),
1116 get_icon("TrackTrigger", "EditorIcons")
1117 };
1118 Ref<Texture> type_icon[3] = {
1119 get_icon("KeyValue", "EditorIcons"),
1120 get_icon("KeyXform", "EditorIcons"),
1121 get_icon("KeyCall", "EditorIcons")
1122 };
1123
1124 Ref<Texture> invalid_icon = get_icon("KeyInvalid", "EditorIcons");
1125 Ref<Texture> invalid_icon_hover = get_icon("KeyInvalidHover", "EditorIcons");
1126
1127 Ref<Texture> hsize_icon = get_icon("Hsize", "EditorIcons");
1128
1129 Ref<Texture> type_hover = get_icon("KeyHover", "EditorIcons");
1130 Ref<Texture> type_selected = get_icon("KeySelected", "EditorIcons");
1131
1132 int right_separator_ofs = down_icon->get_width() * 2 + add_key_icon->get_width() + interp_icon[0]->get_width() + cont_icon[0]->get_width() + hsep * 7;
1133
1134 int h = font->get_height() + sep;
1135
1136 int fit = (size.height / h) - 1;
1137 int total = animation->get_track_count();
1138 if (total < fit) {
1139 v_scroll->hide();
1140 v_scroll->set_max(total);
1141 v_scroll->set_page(fit);
1142 } else {
1143 v_scroll->show();
1144 v_scroll->set_max(total);
1145 v_scroll->set_page(fit);
1146 }
1147
1148 int settings_limit = size.width - right_separator_ofs;
1149 int name_limit = settings_limit * name_column_ratio;
1150
1151 te->draw_line(ofs + Point2(name_limit, 0), ofs + Point2(name_limit, size.height), color);
1152 te->draw_line(ofs + Point2(settings_limit, 0), ofs + Point2(settings_limit, size.height), color);
1153 te->draw_texture(hsize_icon, ofs + Point2(name_limit - hsize_icon->get_width() - hsep, (h - hsize_icon->get_height()) / 2));
1154
1155 te->draw_line(ofs + Point2(0, h), ofs + Point2(size.width, h), color);
1156 // draw time
1157
1158 float keys_from;
1159 float keys_to;
1160 float zoom_scale;
1161
1162 {
1163
1164 int zoomw = settings_limit - name_limit;
1165
1166 float scale = _get_zoom_scale();
1167 zoom_scale = scale;
1168
1169 float l = animation->get_length();
1170 if (l <= 0)
1171 l = 0.001; //avoid crashor
1172
1173 int end_px = (l - h_scroll->get_val()) * scale;
1174 int begin_px = -h_scroll->get_val() * scale;
1175 Color notimecol;
1176 notimecol.r = timecolor.gray();
1177 notimecol.g = notimecol.r;
1178 notimecol.b = notimecol.r;
1179 notimecol.a = timecolor.a;
1180
1181 {
1182
1183 te->draw_rect(Rect2(ofs + Point2(name_limit, 0), Point2(zoomw - 1, h)), notimecol);
1184
1185 if (begin_px < zoomw && end_px > 0) {
1186
1187 if (begin_px < 0)
1188 begin_px = 0;
1189 if (end_px > zoomw)
1190 end_px = zoomw;
1191
1192 te->draw_rect(Rect2(ofs + Point2(name_limit + begin_px, 0), Point2(end_px - begin_px - 1, h)), timecolor);
1193 }
1194 }
1195
1196 keys_from = h_scroll->get_val();
1197 keys_to = keys_from + zoomw / scale;
1198
1199 {
1200 float time_min = 0;
1201 float time_max = animation->get_length();
1202 for (int i = 0; i < animation->get_track_count(); i++) {
1203
1204 if (animation->track_get_key_count(i) > 0) {
1205
1206 float beg = animation->track_get_key_time(i, 0);
1207 if (beg < time_min)
1208 time_min = beg;
1209 float end = animation->track_get_key_time(i, animation->track_get_key_count(i) - 1);
1210 if (end > time_max)
1211 time_max = end;
1212 }
1213 }
1214
1215 float extra = (zoomw / scale) * 0.5;
1216
1217 if (time_min < -0.001)
1218 time_min -= extra;
1219 time_max += extra;
1220 h_scroll->set_min(time_min);
1221 h_scroll->set_max(time_max);
1222
1223 if (zoomw / scale < (time_max - time_min)) {
1224 h_scroll->show();
1225
1226 } else {
1227
1228 h_scroll->hide();
1229 }
1230 }
1231
1232 h_scroll->set_page(zoomw / scale);
1233
1234 Color color_time_sec = color;
1235 Color color_time_dec = color;
1236 color_time_dec.a *= 0.5;
1237 #define SC_ADJ 100
1238 int min = 30;
1239 int dec = 1;
1240 int step = 1;
1241 int decimals = 2;
1242 bool step_found = false;
1243
1244 while (!step_found) {
1245
1246 static const int _multp[3] = { 1, 2, 5 };
1247 for (int i = 0; i < 3; i++) {
1248
1249 step = (_multp[i] * dec);
1250 if (step * scale / SC_ADJ > min) {
1251 step_found = true;
1252 break;
1253 }
1254 }
1255 if (step_found)
1256 break;
1257 dec *= 10;
1258 decimals--;
1259 if (decimals < 0)
1260 decimals = 0;
1261 }
1262
1263 for (int i = 0; i < zoomw; i++) {
1264
1265 float pos = h_scroll->get_val() + double(i) / scale;
1266 float prev = h_scroll->get_val() + (double(i) - 1.0) / scale;
1267
1268 int sc = int(Math::floor(pos * SC_ADJ));
1269 int prev_sc = int(Math::floor(prev * SC_ADJ));
1270 bool sub = (sc % SC_ADJ);
1271
1272 if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) {
1273
1274 int scd = sc < 0 ? prev_sc : sc;
1275 te->draw_line(ofs + Point2(name_limit + i, 0), ofs + Point2(name_limit + i, h), color);
1276 te->draw_string(font, ofs + Point2(name_limit + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i);
1277 }
1278 }
1279 }
1280
1281 color.a *= 0.5;
1282
1283 for (int i = 0; i < fit; i++) {
1284
1285 //this code sucks, i always forget how it works
1286
1287 int idx = v_scroll->get_val() + i;
1288 if (idx >= animation->get_track_count())
1289 break;
1290 int y = h + i * h + sep;
1291
1292 bool prop_exists = false;
1293 Variant::Type valid_type = Variant::NIL;
1294 Object *obj = NULL;
1295
1296 RES res;
1297 Node *node = root ? root->get_node_and_resource(animation->track_get_path(idx), res) : (Node *)NULL;
1298
1299 if (res.is_valid()) {
1300 obj = res.ptr();
1301 } else if (node) {
1302 obj = node;
1303 }
1304
1305 if (obj && animation->track_get_type(idx) == Animation::TYPE_VALUE) {
1306 valid_type = obj->get_static_property_type(animation->track_get_path(idx).get_property(), &prop_exists);
1307 }
1308
1309 if (/*mouse_over.over!=MouseOver::OVER_NONE &&*/ idx == mouse_over.track) {
1310 Color sepc = hover_color;
1311 te->draw_rect(Rect2(ofs + Point2(0, y), Size2(size.width, h - 1)), sepc);
1312 }
1313
1314 if (selected_track == idx) {
1315 Color tc = select_color;
1316 //tc.a*=0.7;
1317 te->draw_rect(Rect2(ofs + Point2(0, y), Size2(size.width - 1, h - 1)), tc);
1318 }
1319
1320 te->draw_texture(type_icon[animation->track_get_type(idx)], ofs + Point2(0, y + (h - type_icon[0]->get_height()) / 2).floor());
1321 NodePath np = animation->track_get_path(idx);
1322 Node *n = root ? root->get_node(np) : (Node *)NULL;
1323 Color ncol = color;
1324 if (n && editor_selection->is_selected(n))
1325 ncol = track_select_color;
1326 te->draw_string(font, Point2(ofs + Point2(type_icon[0]->get_width() + sep, y + font->get_ascent() + (sep / 2))).floor(), np, ncol, name_limit - (type_icon[0]->get_width() + sep) - 5);
1327
1328 if (!obj)
1329 te->draw_line(ofs + Point2(0, y + h / 2), ofs + Point2(name_limit, y + h / 2), invalid_path_color);
1330
1331 te->draw_line(ofs + Point2(0, y + h), ofs + Point2(size.width, y + h), sepcolor);
1332
1333 Point2 icon_ofs = ofs + Point2(size.width, y + (h - remove_icon->get_height()) / 2).floor();
1334 icon_ofs.y += 4;
1335
1336 /* icon_ofs.x-=remove_icon->get_width();
1337
1338 te->draw_texture((mouse_over.over==MouseOver::OVER_REMOVE && mouse_over.track==idx)?remove_icon_hl:remove_icon,icon_ofs);
1339 icon_ofs.x-=hsep;
1340 icon_ofs.x-=move_down_icon->get_width();
1341 te->draw_texture((mouse_over.over==MouseOver::OVER_DOWN && mouse_over.track==idx)?move_down_icon_hl:move_down_icon,icon_ofs);
1342 icon_ofs.x-=hsep;
1343 icon_ofs.x-=move_up_icon->get_width();
1344 te->draw_texture((mouse_over.over==MouseOver::OVER_UP && mouse_over.track==idx)?move_up_icon_hl:move_up_icon,icon_ofs);
1345 icon_ofs.x-=hsep;
1346 te->draw_line(Point2(icon_ofs.x,ofs.y+y),Point2(icon_ofs.x,ofs.y+y+h),sepcolor);
1347
1348 icon_ofs.x-=hsep;
1349 */
1350 icon_ofs.x -= down_icon->get_width();
1351 te->draw_texture(down_icon, icon_ofs);
1352
1353 int interp_type = animation->track_get_interpolation_type(idx);
1354 ERR_CONTINUE(interp_type < 0 || interp_type >= 3);
1355 icon_ofs.x -= hsep;
1356 icon_ofs.x -= interp_icon[interp_type]->get_width();
1357 te->draw_texture(interp_icon[interp_type], icon_ofs);
1358
1359 icon_ofs.x -= hsep;
1360 te->draw_line(Point2(icon_ofs.x, ofs.y + y), Point2(icon_ofs.x, ofs.y + y + h), sepcolor);
1361
1362 if (animation->track_get_type(idx) == Animation::TYPE_VALUE) {
1363
1364 int umode = animation->value_track_get_update_mode(idx);
1365
1366 icon_ofs.x -= hsep;
1367 icon_ofs.x -= down_icon->get_width();
1368 te->draw_texture(down_icon, icon_ofs);
1369
1370 icon_ofs.x -= hsep;
1371 icon_ofs.x -= cont_icon[umode]->get_width();
1372 te->draw_texture(cont_icon[umode], icon_ofs);
1373 } else {
1374
1375 icon_ofs.x -= hsep * 2 + cont_icon[0]->get_width() + down_icon->get_width();
1376 }
1377
1378 icon_ofs.x -= hsep;
1379 te->draw_line(Point2(icon_ofs.x, ofs.y + y), Point2(icon_ofs.x, ofs.y + y + h), sepcolor);
1380
1381 icon_ofs.x -= hsep;
1382 icon_ofs.x -= add_key_icon->get_width();
1383 te->draw_texture((mouse_over.over == MouseOver::OVER_ADD_KEY && mouse_over.track == idx) ? add_key_icon_hl : add_key_icon, icon_ofs);
1384
1385 //draw the keys;
1386 int tt = animation->track_get_type(idx);
1387 float key_vofs = Math::floor((h - type_icon[tt]->get_height()) / 2);
1388 float key_hofs = -Math::floor(type_icon[tt]->get_height() / 2);
1389
1390 int kc = animation->track_get_key_count(idx);
1391 bool first = true;
1392
1393 for (int i = 0; i < kc; i++) {
1394
1395 float time = animation->track_get_key_time(idx, i);
1396 if (time < keys_from)
1397 continue;
1398 if (time > keys_to) {
1399
1400 if (first && i > 0 && animation->track_get_key_value(idx, i) == animation->track_get_key_value(idx, i - 1)) {
1401 //draw whole line
1402 te->draw_line(ofs + Vector2(name_limit, y + h / 2), ofs + Point2(settings_limit, y + h / 2), color);
1403 }
1404
1405 break;
1406 }
1407
1408 float x = key_hofs + name_limit + (time - keys_from) * zoom_scale;
1409
1410 Ref<Texture> tex = type_icon[tt];
1411
1412 SelectedKey sk;
1413 sk.key = i;
1414 sk.track = idx;
1415 if (selection.has(sk)) {
1416
1417 if (click.click == ClickOver::CLICK_MOVE_KEYS)
1418 continue;
1419 tex = type_selected;
1420 }
1421
1422 if (mouse_over.over == MouseOver::OVER_KEY && mouse_over.track == idx && mouse_over.over_key == i)
1423 tex = type_hover;
1424
1425 Variant value = animation->track_get_key_value(idx, i);
1426 if (first && i > 0 && value == animation->track_get_key_value(idx, i - 1)) {
1427
1428 te->draw_line(ofs + Vector2(name_limit, y + h / 2), ofs + Point2(x, y + h / 2), color);
1429 }
1430
1431 if (i < kc - 1 && value == animation->track_get_key_value(idx, i + 1)) {
1432 float x_n = key_hofs + name_limit + (animation->track_get_key_time(idx, i + 1) - keys_from) * zoom_scale;
1433
1434 x_n = MIN(x_n, settings_limit);
1435 te->draw_line(ofs + Point2(x_n, y + h / 2), ofs + Point2(x, y + h / 2), color);
1436 }
1437
1438 if (prop_exists && !Variant::can_convert(value.get_type(), valid_type)) {
1439 te->draw_texture(invalid_icon, ofs + Point2(x, y + key_vofs).floor());
1440 }
1441
1442 if (prop_exists && !Variant::can_convert(value.get_type(), valid_type)) {
1443 if (tex == type_hover)
1444 te->draw_texture(invalid_icon_hover, ofs + Point2(x, y + key_vofs).floor());
1445 else
1446 te->draw_texture(invalid_icon, ofs + Point2(x, y + key_vofs).floor());
1447 } else {
1448
1449 te->draw_texture(tex, ofs + Point2(x, y + key_vofs).floor());
1450 }
1451
1452 first = false;
1453 }
1454 }
1455
1456 switch (click.click) {
1457 case ClickOver::CLICK_SELECT_KEYS: {
1458
1459 te->draw_rect(Rect2(click.at, click.to - click.at), Color(0.7, 0.7, 1.0, 0.5));
1460
1461 } break;
1462 case ClickOver::CLICK_MOVE_KEYS: {
1463
1464 float from_t = 1e20;
1465
1466 for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
1467 float t = animation->track_get_key_time(E->key().track, E->key().key);
1468 if (t < from_t)
1469 from_t = t;
1470 }
1471
1472 float motion = from_t + (click.to.x - click.at.x) / zoom_scale;
1473 if (step->get_val())
1474 motion = Math::stepify(motion, step->get_val());
1475
1476 for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
1477
1478 int idx = E->key().track;
1479 int i = idx - v_scroll->get_val();
1480 if (i < 0 || i >= fit)
1481 continue;
1482 int y = h + i * h + sep;
1483
1484 float key_vofs = Math::floor((h - type_selected->get_height()) / 2);
1485 float key_hofs = -Math::floor(type_selected->get_height() / 2);
1486
1487 float time = animation->track_get_key_time(idx, E->key().key);
1488 float diff = time - from_t;
1489
1490 float t = motion + diff;
1491
1492 float x = (t - keys_from) * zoom_scale;
1493 //x+=click.to.x - click.at.x;
1494 if (x < 0 || x >= (settings_limit - name_limit))
1495 continue;
1496
1497 x += name_limit;
1498
1499 te->draw_texture(type_selected, ofs + Point2(x + key_hofs, y + key_vofs).floor());
1500 }
1501 } break;
1502 default: {};
1503 }
1504
1505 te_drawing = false;
1506 }
1507
_track_name_changed(const String & p_name)1508 void AnimationKeyEditor::_track_name_changed(const String &p_name) {
1509
1510 ERR_FAIL_COND(!animation.is_valid());
1511 undo_redo->create_action(TTR("Anim Track Rename"));
1512 undo_redo->add_do_method(animation.ptr(), "track_set_path", track_name_editing, p_name);
1513 undo_redo->add_undo_method(animation.ptr(), "track_set_path", track_name_editing, animation->track_get_path(track_name_editing));
1514 undo_redo->commit_action();
1515 track_name->hide();
1516 }
1517
_track_menu_selected(int p_idx)1518 void AnimationKeyEditor::_track_menu_selected(int p_idx) {
1519
1520 ERR_FAIL_COND(!animation.is_valid());
1521
1522 if (interp_editing != -1) {
1523
1524 ERR_FAIL_INDEX(interp_editing, animation->get_track_count());
1525 undo_redo->create_action(TTR("Anim Track Change Interpolation"));
1526 undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", interp_editing, p_idx);
1527 undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", interp_editing, animation->track_get_interpolation_type(interp_editing));
1528 undo_redo->commit_action();
1529 } else if (cont_editing != -1) {
1530
1531 ERR_FAIL_INDEX(cont_editing, animation->get_track_count());
1532
1533 undo_redo->create_action(TTR("Anim Track Change Value Mode"));
1534 undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", cont_editing, p_idx);
1535 undo_redo->add_undo_method(animation.ptr(), "value_track_set_update_mode", cont_editing, animation->value_track_get_update_mode(cont_editing));
1536 undo_redo->commit_action();
1537 } else {
1538 switch (p_idx) {
1539
1540 case RIGHT_MENU_DUPLICATE:
1541 _anim_duplicate_keys();
1542 break;
1543 case RIGHT_MENU_DUPLICATE_TRANSPOSE:
1544 _anim_duplicate_keys(true);
1545 break;
1546 case RIGHT_MENU_REMOVE:
1547 _anim_delete_keys();
1548 break;
1549 }
1550 }
1551 }
1552
1553 struct _AnimMoveRestore {
1554
1555 int track;
1556 float time;
1557 Variant key;
1558 float transition;
1559 };
1560
_clear_selection_for_anim(const Ref<Animation> & p_anim)1561 void AnimationKeyEditor::_clear_selection_for_anim(const Ref<Animation> &p_anim) {
1562
1563 if (!(animation == p_anim))
1564 return;
1565 //selection.clear();
1566 _clear_selection();
1567 }
1568
_select_at_anim(const Ref<Animation> & p_anim,int p_track,float p_pos)1569 void AnimationKeyEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos) {
1570
1571 if (!(animation == p_anim))
1572 return;
1573
1574 int idx = animation->track_find_key(p_track, p_pos, true);
1575 ERR_FAIL_COND(idx < 0);
1576
1577 SelectedKey sk;
1578 sk.track = p_track;
1579 sk.key = idx;
1580 KeyInfo ki;
1581 ki.pos = p_pos;
1582
1583 selection.insert(sk, ki);
1584 }
1585
_find_hint_for_track(int p_idx,NodePath & r_base_path)1586 PropertyInfo AnimationKeyEditor::_find_hint_for_track(int p_idx, NodePath &r_base_path) {
1587
1588 r_base_path = NodePath();
1589 ERR_FAIL_COND_V(!animation.is_valid(), PropertyInfo());
1590 ERR_FAIL_INDEX_V(p_idx, animation->get_track_count(), PropertyInfo());
1591
1592 if (!root)
1593 return PropertyInfo();
1594
1595 NodePath path = animation->track_get_path(p_idx);
1596
1597 if (!root->has_node_and_resource(path))
1598 return PropertyInfo();
1599
1600 RES res;
1601 Node *node = root->get_node_and_resource(path, res);
1602
1603 if (node) {
1604 r_base_path = node->get_path();
1605 }
1606
1607 String property = path.get_property();
1608 if (property == "")
1609 return PropertyInfo();
1610
1611 List<PropertyInfo> pinfo;
1612 if (res.is_valid())
1613 res->get_property_list(&pinfo);
1614 else
1615 node->get_property_list(&pinfo);
1616
1617 for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
1618
1619 if (E->get().name == property)
1620 return E->get();
1621 }
1622
1623 return PropertyInfo();
1624 }
1625
_curve_transition_changed(float p_what)1626 void AnimationKeyEditor::_curve_transition_changed(float p_what) {
1627
1628 if (selection.size() == 0)
1629 return;
1630 if (selection.size() == 1)
1631 undo_redo->create_action(TTR("Edit Node Curve"), UndoRedo::MERGE_ENDS);
1632 else
1633 undo_redo->create_action(TTR("Edit Selection Curve"), UndoRedo::MERGE_ENDS);
1634
1635 for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
1636
1637 int track = E->key().track;
1638 int key = E->key().key;
1639 float prev_val = animation->track_get_key_transition(track, key);
1640 undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, p_what);
1641 undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val);
1642 }
1643
1644 undo_redo->commit_action();
1645 }
1646
_toggle_edit_curves()1647 void AnimationKeyEditor::_toggle_edit_curves() {
1648
1649 if (edit_button->is_pressed())
1650 key_editor_tab->show();
1651 else
1652 key_editor_tab->hide();
1653 }
1654
_edit_if_single_selection()1655 bool AnimationKeyEditor::_edit_if_single_selection() {
1656
1657 if (selection.size() != 1) {
1658
1659 if (selection.size() == 0) {
1660 curve_edit->set_mode(AnimationCurveEdit::MODE_DISABLED);
1661 //print_line("disable");
1662 } else {
1663
1664 curve_edit->set_mode(AnimationCurveEdit::MODE_MULTIPLE);
1665 curve_edit->set_transition(1.0);
1666 curve_edit->clear_multiples();
1667 //add all
1668 for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
1669
1670 curve_edit->set_multiple(animation->track_get_key_transition(E->key().track, E->key().key));
1671 }
1672 //print_line("multiple");
1673 }
1674 return false;
1675 }
1676 curve_edit->set_mode(AnimationCurveEdit::MODE_SINGLE);
1677 //print_line("regular");
1678
1679 int idx = selection.front()->key().track;
1680 int key = selection.front()->key().key;
1681 {
1682
1683 key_edit->animation = animation;
1684 key_edit->track = idx;
1685 key_edit->key_ofs = animation->track_get_key_time(idx, key);
1686 key_edit->hint = _find_hint_for_track(idx, key_edit->base);
1687 key_edit->notify_change();
1688
1689 curve_edit->set_transition(animation->track_get_key_transition(idx, key));
1690
1691 /*key_edit_dialog->set_size( Size2( 200,200) );
1692 key_edit_dialog->set_pos( track_editor->get_global_pos() + ofs + mpos +Point2(-100,20));
1693 key_edit_dialog->popup();*/
1694 }
1695
1696 return true;
1697 }
1698
_anim_delete_keys()1699 void AnimationKeyEditor::_anim_delete_keys() {
1700 if (selection.size()) {
1701 undo_redo->create_action(TTR("Anim Delete Keys"));
1702
1703 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
1704
1705 undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
1706 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
1707 }
1708 undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
1709 undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
1710 undo_redo->commit_action();
1711 //selection.clear();
1712 accept_event();
1713 _edit_if_single_selection();
1714 }
1715 }
1716
_track_editor_input_event(const InputEvent & p_input)1717 void AnimationKeyEditor::_track_editor_input_event(const InputEvent &p_input) {
1718
1719 Control *te = track_editor;
1720 Ref<StyleBox> style = get_stylebox("normal", "TextEdit");
1721
1722 if (!animation.is_valid()) {
1723 return;
1724 }
1725
1726 Size2 size = te->get_size() - style->get_minimum_size();
1727 Size2 ofs = style->get_offset();
1728
1729 Ref<Font> font = te->get_font("font", "Tree");
1730 int sep = get_constant("vseparation", "Tree");
1731 int hsep = get_constant("hseparation", "Tree");
1732 Ref<Texture> remove_icon = get_icon("Remove", "EditorIcons");
1733 Ref<Texture> move_up_icon = get_icon("MoveUp", "EditorIcons");
1734 Ref<Texture> move_down_icon = get_icon("MoveDown", "EditorIcons");
1735 Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
1736 Ref<Texture> hsize_icon = get_icon("Hsize", "EditorIcons");
1737 Ref<Texture> add_key_icon = get_icon("TrackAddKey", "EditorIcons");
1738
1739 Ref<Texture> interp_icon[3] = {
1740 get_icon("InterpRaw", "EditorIcons"),
1741 get_icon("InterpLinear", "EditorIcons"),
1742 get_icon("InterpCubic", "EditorIcons")
1743 };
1744 Ref<Texture> cont_icon[3] = {
1745 get_icon("TrackContinuous", "EditorIcons"),
1746 get_icon("TrackDiscrete", "EditorIcons"),
1747 get_icon("TrackTrigger", "EditorIcons")
1748 };
1749 Ref<Texture> type_icon[3] = {
1750 get_icon("KeyValue", "EditorIcons"),
1751 get_icon("KeyXform", "EditorIcons"),
1752 get_icon("KeyCall", "EditorIcons")
1753 };
1754 int right_separator_ofs = down_icon->get_width() * 2 + add_key_icon->get_width() + interp_icon[0]->get_width() + cont_icon[0]->get_width() + hsep * 7;
1755
1756 int h = font->get_height() + sep;
1757
1758 int fit = (size.height / h) - 1;
1759 int total = animation->get_track_count();
1760 if (total < fit) {
1761 v_scroll->hide();
1762 } else {
1763 v_scroll->show();
1764 v_scroll->set_max(total);
1765 v_scroll->set_page(fit);
1766 }
1767
1768 int settings_limit = size.width - right_separator_ofs;
1769 int name_limit = settings_limit * name_column_ratio;
1770
1771 switch (p_input.type) {
1772
1773 case InputEvent::KEY: {
1774
1775 if (p_input.key.scancode == KEY_D && p_input.key.pressed && p_input.key.mod.command) {
1776
1777 if (p_input.key.mod.shift)
1778 _menu_track(TRACK_MENU_DUPLICATE_TRANSPOSE);
1779 else
1780 _menu_track(TRACK_MENU_DUPLICATE);
1781
1782 accept_event();
1783
1784 } else if (p_input.key.scancode == KEY_DELETE && p_input.key.pressed && click.click == ClickOver::CLICK_NONE) {
1785
1786 _anim_delete_keys();
1787 } else if (animation.is_valid() && animation->get_track_count() > 0) {
1788
1789 if (p_input.is_pressed() && (p_input.is_action("ui_up") || p_input.is_action("ui_page_up"))) {
1790
1791 if (p_input.is_action("ui_up"))
1792 selected_track--;
1793 if (v_scroll->is_visible() && p_input.is_action("ui_page_up"))
1794 selected_track--;
1795
1796 if (selected_track < 0)
1797 selected_track = 0;
1798
1799 if (v_scroll->is_visible()) {
1800 if (v_scroll->get_val() > selected_track)
1801 v_scroll->set_val(selected_track);
1802 }
1803
1804 track_editor->update();
1805 accept_event();
1806 }
1807
1808 if (p_input.is_pressed() && (p_input.is_action("ui_down") || p_input.is_action("ui_page_down"))) {
1809
1810 if (p_input.is_action("ui_down"))
1811 selected_track++;
1812 else if (v_scroll->is_visible() && p_input.is_action("ui_page_down"))
1813 selected_track += v_scroll->get_page();
1814
1815 if (selected_track >= animation->get_track_count())
1816 selected_track = animation->get_track_count() - 1;
1817
1818 if (v_scroll->is_visible() && v_scroll->get_page() + v_scroll->get_val() < selected_track + 1) {
1819 v_scroll->set_val(selected_track - v_scroll->get_page() + 1);
1820 }
1821
1822 track_editor->update();
1823 accept_event();
1824 }
1825 }
1826
1827 } break;
1828 case InputEvent::MOUSE_BUTTON: {
1829
1830 const InputEventMouseButton &mb = p_input.mouse_button;
1831
1832 if (mb.button_index == BUTTON_WHEEL_UP && mb.pressed) {
1833
1834 if (mb.mod.command) {
1835 zoom->set_val(zoom->get_val() + zoom->get_step());
1836 } else {
1837 v_scroll->set_val(v_scroll->get_val() - v_scroll->get_page() * mb.factor / 8);
1838 }
1839 }
1840
1841 if (mb.button_index == BUTTON_WHEEL_DOWN && mb.pressed) {
1842
1843 if (mb.mod.command) {
1844 zoom->set_val(zoom->get_val() - zoom->get_step());
1845 } else {
1846 v_scroll->set_val(v_scroll->get_val() + v_scroll->get_page() * mb.factor / 8);
1847 }
1848 }
1849
1850 if (mb.button_index == BUTTON_WHEEL_RIGHT && mb.pressed) {
1851 h_scroll->set_val(h_scroll->get_val() - h_scroll->get_page() * mb.factor / 8);
1852 }
1853
1854 if (mb.button_index == BUTTON_WHEEL_LEFT && mb.pressed) {
1855
1856 v_scroll->set_val(v_scroll->get_val() + v_scroll->get_page() * mb.factor / 8);
1857 }
1858
1859 if (mb.button_index == BUTTON_RIGHT && mb.pressed) {
1860
1861 Point2 mpos = Point2(mb.x, mb.y) - ofs;
1862
1863 if (selection.size() == 0) {
1864 // Auto-select on right-click if nothing is selected
1865 // Note: This code is pretty much duplicated from the left click code,
1866 // both codes could be moved into a function to avoid the duplicated code.
1867 Point2 mpos = Point2(mb.x, mb.y) - ofs;
1868
1869 if (mpos.y < h) {
1870 return;
1871 }
1872
1873 mpos.y -= h;
1874
1875 int idx = mpos.y / h;
1876 idx += v_scroll->get_val();
1877 if (idx < 0 || idx >= animation->get_track_count())
1878 break;
1879
1880 if (mpos.x < name_limit) {
1881 } else if (mpos.x < settings_limit) {
1882 float pos = mpos.x - name_limit;
1883 pos /= _get_zoom_scale();
1884 pos += h_scroll->get_val();
1885 float w_time = (type_icon[0]->get_width() / _get_zoom_scale()) / 2.0;
1886
1887 int kidx = animation->track_find_key(idx, pos);
1888 int kidx_n = kidx + 1;
1889 int key = -1;
1890
1891 if (kidx >= 0 && kidx < animation->track_get_key_count(idx)) {
1892
1893 float kpos = animation->track_get_key_time(idx, kidx);
1894 if (ABS(pos - kpos) <= w_time) {
1895
1896 key = kidx;
1897 }
1898 }
1899
1900 if (key == -1 && kidx_n >= 0 && kidx_n < animation->track_get_key_count(idx)) {
1901
1902 float kpos = animation->track_get_key_time(idx, kidx_n);
1903 if (ABS(pos - kpos) <= w_time) {
1904
1905 key = kidx_n;
1906 }
1907 }
1908
1909 if (key == -1) {
1910
1911 click.click = ClickOver::CLICK_SELECT_KEYS;
1912 click.at = Point2(mb.x, mb.y);
1913 click.to = click.at;
1914 click.shift = mb.mod.shift;
1915 selected_track = idx;
1916 track_editor->update();
1917 //drag select region
1918 return;
1919 }
1920
1921 SelectedKey sk;
1922 sk.track = idx;
1923 sk.key = key;
1924 KeyInfo ki;
1925 ki.pos = animation->track_get_key_time(idx, key);
1926 click.shift = mb.mod.shift;
1927 click.selk = sk;
1928
1929 if (!mb.mod.shift && !selection.has(sk))
1930 _clear_selection();
1931
1932 selection.insert(sk, ki);
1933
1934 click.click = ClickOver::CLICK_MOVE_KEYS;
1935 click.at = Point2(mb.x, mb.y);
1936 click.to = click.at;
1937 update();
1938 selected_track = idx;
1939 track_editor->update();
1940
1941 if (_edit_if_single_selection() && mb.mod.command) {
1942 edit_button->set_pressed(true);
1943 key_editor_tab->show();
1944 }
1945 }
1946 }
1947
1948 if (selection.size()) {
1949 // User has right clicked and we have a selection, show a popup menu with options
1950 track_menu->clear();
1951 track_menu->set_size(Point2(1, 1));
1952 track_menu->add_item(TTR("Duplicate Selection"), RIGHT_MENU_DUPLICATE);
1953 track_menu->add_item(TTR("Duplicate Transposed"), RIGHT_MENU_DUPLICATE_TRANSPOSE);
1954 track_menu->add_item(TTR("Remove Selection"), RIGHT_MENU_REMOVE);
1955
1956 track_menu->set_pos(te->get_global_pos() + mpos);
1957
1958 interp_editing = -1;
1959 cont_editing = -1;
1960
1961 track_menu->popup();
1962 }
1963 }
1964
1965 if (mb.button_index == BUTTON_LEFT && !(mb.button_mask & ~BUTTON_MASK_LEFT)) {
1966
1967 if (mb.pressed) {
1968
1969 Point2 mpos = Point2(mb.x, mb.y) - ofs;
1970
1971 if (mpos.y < h) {
1972
1973 if (mpos.x < name_limit && mpos.x > (name_limit - hsep - hsize_icon->get_width())) {
1974
1975 click.click = ClickOver::CLICK_RESIZE_NAMES;
1976 click.at = Point2(mb.x, mb.y);
1977 click.to = click.at;
1978 click.at.y = name_limit;
1979 }
1980
1981 if (mpos.x >= name_limit && mpos.x < settings_limit) {
1982 //seek
1983 //int zoomw = settings_limit-name_limit;
1984 float scale = _get_zoom_scale();
1985 float pos = h_scroll->get_val() + (mpos.x - name_limit) / scale;
1986 if (animation->get_step())
1987 pos = Math::stepify(pos, animation->get_step());
1988
1989 if (pos < 0)
1990 pos = 0;
1991 if (pos >= animation->get_length())
1992 pos = animation->get_length();
1993 timeline_pos = pos;
1994 click.click = ClickOver::CLICK_DRAG_TIMELINE;
1995 click.at = Point2(mb.x, mb.y);
1996 click.to = click.at;
1997 emit_signal("timeline_changed", pos, false);
1998 }
1999
2000 return;
2001 }
2002
2003 mpos.y -= h;
2004
2005 int idx = mpos.y / h;
2006 idx += v_scroll->get_val();
2007 if (idx < 0)
2008 break;
2009
2010 if (idx >= animation->get_track_count()) {
2011
2012 if (mpos.x >= name_limit && mpos.x < settings_limit) {
2013
2014 click.click = ClickOver::CLICK_SELECT_KEYS;
2015 click.at = Point2(mb.x, mb.y);
2016 click.to = click.at;
2017 //drag select region
2018 }
2019
2020 break;
2021 }
2022
2023 if (mpos.x < name_limit) {
2024 //name column
2025
2026 // area
2027 if (idx != selected_track) {
2028
2029 selected_track = idx;
2030 track_editor->update();
2031 break;
2032 }
2033
2034 Rect2 area(ofs.x, ofs.y + ((int(mpos.y) / h) + 1) * h, name_limit, h);
2035 track_name->set_text(animation->track_get_path(idx));
2036 track_name->set_pos(te->get_global_pos() + area.pos);
2037 track_name->set_size(area.size);
2038 track_name->show_modal();
2039 track_name->grab_focus();
2040 track_name->select_all();
2041 track_name_editing = idx;
2042
2043 } else if (mpos.x < settings_limit) {
2044
2045 float pos = mpos.x - name_limit;
2046 pos /= _get_zoom_scale();
2047 pos += h_scroll->get_val();
2048 float w_time = (type_icon[0]->get_width() / _get_zoom_scale()) / 2.0;
2049
2050 int kidx = animation->track_find_key(idx, pos);
2051 int kidx_n = kidx + 1;
2052 int key = -1;
2053
2054 if (kidx >= 0 && kidx < animation->track_get_key_count(idx)) {
2055
2056 float kpos = animation->track_get_key_time(idx, kidx);
2057 if (ABS(pos - kpos) <= w_time) {
2058
2059 key = kidx;
2060 }
2061 }
2062
2063 if (key == -1 && kidx_n >= 0 && kidx_n < animation->track_get_key_count(idx)) {
2064
2065 float kpos = animation->track_get_key_time(idx, kidx_n);
2066 if (ABS(pos - kpos) <= w_time) {
2067
2068 key = kidx_n;
2069 }
2070 }
2071
2072 if (key == -1) {
2073
2074 click.click = ClickOver::CLICK_SELECT_KEYS;
2075 click.at = Point2(mb.x, mb.y);
2076 click.to = click.at;
2077 click.shift = mb.mod.shift;
2078 selected_track = idx;
2079 track_editor->update();
2080 //drag select region
2081 return;
2082 }
2083
2084 SelectedKey sk;
2085 sk.track = idx;
2086 sk.key = key;
2087 KeyInfo ki;
2088 ki.pos = animation->track_get_key_time(idx, key);
2089 click.shift = mb.mod.shift;
2090 click.selk = sk;
2091
2092 if (!mb.mod.shift && !selection.has(sk))
2093 _clear_selection();
2094
2095 selection.insert(sk, ki);
2096
2097 click.click = ClickOver::CLICK_MOVE_KEYS;
2098 click.at = Point2(mb.x, mb.y);
2099 click.to = click.at;
2100 update();
2101 selected_track = idx;
2102 track_editor->update();
2103
2104 if (_edit_if_single_selection() && mb.mod.command) {
2105 edit_button->set_pressed(true);
2106 key_editor_tab->show();
2107 }
2108 } else {
2109 //button column
2110 int ofsx = size.width - mpos.x;
2111 if (ofsx < 0)
2112 break;
2113 /*
2114 if (ofsx < remove_icon->get_width()) {
2115
2116 undo_redo->create_action("Remove Anim Track");
2117 undo_redo->add_do_method(animation.ptr(),"remove_track",idx);
2118 undo_redo->add_undo_method(animation.ptr(),"add_track",animation->track_get_type(idx),idx);
2119 undo_redo->add_undo_method(animation.ptr(),"track_set_path",idx,animation->track_get_path(idx));
2120 //todo interpolation
2121 for(int i=0;i<animation->track_get_key_count(idx);i++) {
2122
2123 Variant v = animation->track_get_key_value(idx,i);
2124 float time = animation->track_get_key_time(idx,i);
2125 float trans = animation->track_get_key_transition(idx,i);
2126
2127 undo_redo->add_undo_method(animation.ptr(),"track_insert_key",idx,time,v);
2128 undo_redo->add_undo_method(animation.ptr(),"track_set_key_transition",idx,i,trans);
2129
2130 }
2131
2132 undo_redo->add_undo_method(animation.ptr(),"track_set_interpolation_type",idx,animation->track_get_interpolation_type(idx));
2133 if (animation->track_get_type(idx)==Animation::TYPE_VALUE) {
2134 undo_redo->add_undo_method(animation.ptr(),"value_track_set_continuous",idx,animation->value_track_is_continuous(idx));
2135
2136 }
2137
2138 undo_redo->commit_action();
2139
2140
2141 return;
2142 }
2143
2144 ofsx-=hsep+remove_icon->get_width();
2145
2146 if (ofsx < move_down_icon->get_width()) {
2147
2148 if (idx < animation->get_track_count() -1) {
2149 undo_redo->create_action("Move Anim Track Down");
2150 undo_redo->add_do_method(animation.ptr(),"track_move_up",idx);
2151 undo_redo->add_undo_method(animation.ptr(),"track_move_down",idx+1);
2152 undo_redo->commit_action();
2153 }
2154 return;
2155 }
2156
2157 ofsx-=hsep+move_down_icon->get_width();
2158
2159 if (ofsx < move_up_icon->get_width()) {
2160
2161 if (idx >0) {
2162 undo_redo->create_action("Move Anim Track Up");
2163 undo_redo->add_do_method(animation.ptr(),"track_move_down",idx);
2164 undo_redo->add_undo_method(animation.ptr(),"track_move_up",idx-1);
2165 undo_redo->commit_action();
2166 }
2167 return;
2168 }
2169
2170
2171 ofsx-=hsep*3+move_up_icon->get_width();
2172 */
2173
2174 if (ofsx < down_icon->get_width() + interp_icon[0]->get_width() + hsep * 2) {
2175
2176 track_menu->clear();
2177 track_menu->set_size(Point2(1, 1));
2178 static const char *interp_name[3] = { "Nearest", "Linear", "Cubic" };
2179 for (int i = 0; i < 3; i++) {
2180 track_menu->add_icon_item(interp_icon[i], interp_name[i]);
2181 }
2182
2183 int lofs = remove_icon->get_width() + move_up_icon->get_width() + move_down_icon->get_width() + down_icon->get_width() * 2 + hsep * 7; //interp_icon[0]->get_width() + cont_icon[0]->get_width() ;
2184 int popup_y = ofs.y + ((int(mpos.y) / h) + 2) * h;
2185 int popup_x = ofs.x + size.width - lofs;
2186
2187 track_menu->set_pos(te->get_global_pos() + Point2(popup_x, popup_y));
2188
2189 interp_editing = idx;
2190 cont_editing = -1;
2191
2192 track_menu->popup();
2193
2194 return;
2195 }
2196
2197 ofsx -= hsep * 2 + interp_icon[0]->get_width() + down_icon->get_width();
2198
2199 if (ofsx < down_icon->get_width() + cont_icon[0]->get_width()) {
2200
2201 track_menu->clear();
2202 track_menu->set_size(Point2(1, 1));
2203 String cont_name[3] = { TTR("Continuous"), TTR("Discrete"), TTR("Trigger") };
2204 for (int i = 0; i < 3; i++) {
2205 track_menu->add_icon_item(cont_icon[i], cont_name[i]);
2206 }
2207
2208 int lofs = settings_limit;
2209 int popup_y = ofs.y + ((int(mpos.y) / h) + 2) * h;
2210 int popup_x = ofs.x + lofs;
2211
2212 track_menu->set_pos(te->get_global_pos() + Point2(popup_x, popup_y));
2213
2214 interp_editing = -1;
2215 cont_editing = idx;
2216
2217 track_menu->popup();
2218
2219 return;
2220 }
2221
2222 ofsx -= hsep * 3 + cont_icon[0]->get_width() + down_icon->get_width();
2223
2224 if (ofsx < add_key_icon->get_width()) {
2225
2226 Animation::TrackType tt = animation->track_get_type(idx);
2227
2228 float pos = timeline_pos;
2229 int existing = animation->track_find_key(idx, pos, true);
2230
2231 Variant newval;
2232
2233 if (tt == Animation::TYPE_TRANSFORM) {
2234 Dictionary d;
2235 d["loc"] = Vector3();
2236 d["rot"] = Quat();
2237 d["scale"] = Vector3();
2238 newval = d;
2239
2240 } else if (tt == Animation::TYPE_METHOD) {
2241
2242 Dictionary d;
2243 d["method"] = "";
2244 d["args"] = Vector<Variant>();
2245
2246 newval = d;
2247 } else if (tt == Animation::TYPE_VALUE) {
2248
2249 NodePath np;
2250 PropertyInfo inf = _find_hint_for_track(idx, np);
2251 if (inf.type != Variant::NIL) {
2252
2253 Variant::CallError err;
2254 newval = Variant::construct(inf.type, NULL, 0, err);
2255 }
2256
2257 if (newval.get_type() == Variant::NIL) {
2258 //popup a new type
2259 cvi_track = idx;
2260 cvi_pos = pos;
2261
2262 type_menu->set_pos(get_global_pos() + mpos + ofs);
2263 type_menu->popup();
2264 return;
2265 }
2266 }
2267
2268 undo_redo->create_action(TTR("Anim Add Key"));
2269
2270 undo_redo->add_do_method(animation.ptr(), "track_insert_key", idx, pos, newval, 1);
2271 undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_pos", idx, pos);
2272
2273 if (existing != -1) {
2274 Variant v = animation->track_get_key_value(idx, existing);
2275 float trans = animation->track_get_key_transition(idx, existing);
2276 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", idx, pos, v, trans);
2277 }
2278
2279 undo_redo->commit_action();
2280
2281 return;
2282 }
2283 }
2284
2285 } else {
2286
2287 switch (click.click) {
2288 case ClickOver::CLICK_SELECT_KEYS: {
2289
2290 float zoom_scale = _get_zoom_scale();
2291 float keys_from = h_scroll->get_val();
2292 float keys_to = keys_from + (settings_limit - name_limit) / zoom_scale;
2293
2294 float from_time = keys_from + (click.at.x - (name_limit + ofs.x)) / zoom_scale;
2295 float to_time = keys_from + (click.to.x - (name_limit + ofs.x)) / zoom_scale;
2296
2297 if (to_time < from_time)
2298 SWAP(from_time, to_time);
2299
2300 if (from_time > keys_to || to_time < keys_from)
2301 break;
2302
2303 if (from_time < keys_from)
2304 from_time = keys_from;
2305
2306 if (to_time >= keys_to)
2307 to_time = keys_to;
2308
2309 int from_track = int(click.at.y - ofs.y - h - sep) / h + v_scroll->get_val();
2310 int to_track = int(click.to.y - ofs.y - h - sep) / h + v_scroll->get_val();
2311 int from_mod = int(click.at.y - ofs.y - sep) % h;
2312 int to_mod = int(click.to.y - ofs.y - sep) % h;
2313
2314 if (to_track < from_track) {
2315
2316 SWAP(from_track, to_track);
2317 SWAP(from_mod, to_mod);
2318 }
2319
2320 if ((from_mod > (h / 2)) && ((click.at.y - ofs.y) >= (h + sep))) {
2321 from_track++;
2322 }
2323
2324 if (to_mod < h / 2) {
2325 to_track--;
2326 }
2327
2328 if (from_track > to_track) {
2329 if (!click.shift)
2330 _clear_selection();
2331 _edit_if_single_selection();
2332 break;
2333 }
2334
2335 int tracks_from = v_scroll->get_val();
2336 int tracks_to = v_scroll->get_val() + fit - 1;
2337 if (tracks_to >= animation->get_track_count())
2338 tracks_to = animation->get_track_count() - 1;
2339
2340 tracks_from = 0;
2341 tracks_to = animation->get_track_count() - 1;
2342 if (to_track > tracks_to)
2343 to_track = tracks_to;
2344 if (from_track < tracks_from)
2345 from_track = tracks_from;
2346
2347 if (from_track > tracks_to || to_track < tracks_from) {
2348 if (!click.shift)
2349 _clear_selection();
2350 _edit_if_single_selection();
2351 break;
2352 }
2353
2354 if (!click.shift)
2355 _clear_selection();
2356
2357 int higher_track = 0x7FFFFFFF;
2358 for (int i = from_track; i <= to_track; i++) {
2359
2360 int kc = animation->track_get_key_count(i);
2361 for (int j = 0; j < kc; j++) {
2362
2363 float t = animation->track_get_key_time(i, j);
2364 if (t < from_time)
2365 continue;
2366 if (t > to_time)
2367 break;
2368
2369 if (i < higher_track)
2370 higher_track = i;
2371
2372 SelectedKey sk;
2373 sk.track = i;
2374 sk.key = j;
2375 KeyInfo ki;
2376 ki.pos = t;
2377 selection[sk] = ki;
2378 }
2379 }
2380
2381 if (higher_track != 0x7FFFFFFF) {
2382 selected_track = higher_track;
2383 track_editor->update();
2384 }
2385
2386 _edit_if_single_selection();
2387
2388 } break;
2389 case ClickOver::CLICK_MOVE_KEYS: {
2390
2391 if (selection.empty())
2392 break;
2393 if (click.at == click.to) {
2394
2395 if (!click.shift) {
2396
2397 KeyInfo ki = selection[click.selk];
2398 _clear_selection();
2399 selection[click.selk] = ki;
2400 _edit_if_single_selection();
2401 }
2402
2403 break;
2404 }
2405
2406 float from_t = 1e20;
2407
2408 for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
2409 float t = animation->track_get_key_time(E->key().track, E->key().key);
2410 if (t < from_t)
2411 from_t = t;
2412 }
2413
2414 float motion = from_t + (click.to.x - click.at.x) / _get_zoom_scale();
2415 if (step->get_val())
2416 motion = Math::stepify(motion, step->get_val());
2417
2418 undo_redo->create_action(TTR("Anim Move Keys"));
2419
2420 List<_AnimMoveRestore> to_restore;
2421
2422 // 1-remove the keys
2423 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
2424
2425 undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
2426 }
2427 // 2- remove overlapped keys
2428 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
2429
2430 float newtime = E->get().pos - from_t + motion;
2431 int idx = animation->track_find_key(E->key().track, newtime, true);
2432 if (idx == -1)
2433 continue;
2434 SelectedKey sk;
2435 sk.key = idx;
2436 sk.track = E->key().track;
2437 if (selection.has(sk))
2438 continue; //already in selection, don't save
2439
2440 undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_pos", E->key().track, newtime);
2441 _AnimMoveRestore amr;
2442
2443 amr.key = animation->track_get_key_value(E->key().track, idx);
2444 amr.track = E->key().track;
2445 amr.time = newtime;
2446 amr.transition = animation->track_get_key_transition(E->key().track, idx);
2447
2448 to_restore.push_back(amr);
2449 }
2450
2451 // 3-move the keys (re insert them)
2452 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
2453
2454 float newpos = E->get().pos - from_t + motion;
2455 //if (newpos<0)
2456 // continue; //no add at the begining
2457 undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
2458 }
2459
2460 // 4-(undo) remove inserted keys
2461 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
2462
2463 float newpos = E->get().pos + -from_t + motion;
2464 //if (newpos<0)
2465 // continue; //no remove what no inserted
2466 undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_pos", E->key().track, newpos);
2467 }
2468
2469 // 5-(undo) reinsert keys
2470 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
2471
2472 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
2473 }
2474
2475 // 6-(undo) reinsert overlapped keys
2476 for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
2477
2478 _AnimMoveRestore &amr = E->get();
2479 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
2480 }
2481
2482 // 6-(undo) reinsert overlapped keys
2483 for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
2484
2485 _AnimMoveRestore &amr = E->get();
2486 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
2487 }
2488
2489 undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
2490 undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
2491
2492 // 7-reselect
2493
2494 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
2495
2496 float oldpos = E->get().pos;
2497 float newpos = oldpos - from_t + motion;
2498 //if (newpos>=0)
2499 undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
2500 undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
2501 }
2502
2503 undo_redo->commit_action();
2504 _edit_if_single_selection();
2505
2506 } break;
2507 default: {}
2508 }
2509
2510 //button released
2511 click.click = ClickOver::CLICK_NONE;
2512 track_editor->update();
2513 }
2514 }
2515
2516 } break;
2517
2518 case InputEvent::MOUSE_MOTION: {
2519
2520 const InputEventMouseMotion &mb = p_input.mouse_motion;
2521
2522 mouse_over.over = MouseOver::OVER_NONE;
2523 mouse_over.track = -1;
2524 te->update();
2525 track_editor->set_tooltip("");
2526
2527 if (!track_editor->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
2528 track_editor->call_deferred("grab_focus");
2529
2530 if (click.click != ClickOver::CLICK_NONE) {
2531
2532 switch (click.click) {
2533 case ClickOver::CLICK_RESIZE_NAMES: {
2534
2535 float base = click.at.y;
2536 float clickp = click.at.x - ofs.x;
2537 float dif = base - clickp;
2538
2539 float target = mb.x + dif - ofs.x;
2540
2541 float ratio = target / settings_limit;
2542
2543 if (ratio > 0.9)
2544 ratio = 0.9;
2545 else if (ratio < 0.2)
2546 ratio = 0.2;
2547
2548 name_column_ratio = ratio;
2549
2550 } break;
2551 case ClickOver::CLICK_DRAG_TIMELINE: {
2552
2553 Point2 mpos = Point2(mb.x, mb.y) - ofs;
2554 /*
2555 if (mpos.x<name_limit)
2556 mpos.x=name_limit;
2557 if (mpos.x>settings_limit)
2558 mpos.x=settings_limit;
2559 */
2560
2561 //int zoomw = settings_limit-name_limit;
2562 float scale = _get_zoom_scale();
2563 float pos = h_scroll->get_val() + (mpos.x - name_limit) / scale;
2564 if (animation->get_step()) {
2565 pos = Math::stepify(pos, animation->get_step());
2566 }
2567 if (pos < 0)
2568 pos = 0;
2569 if (pos >= animation->get_length())
2570 pos = animation->get_length();
2571
2572 if (pos < h_scroll->get_val()) {
2573 h_scroll->set_val(pos);
2574 } else if (pos > h_scroll->get_val() + (settings_limit - name_limit) / scale) {
2575 h_scroll->set_val(pos - (settings_limit - name_limit) / scale);
2576 }
2577
2578 timeline_pos = pos;
2579 emit_signal("timeline_changed", pos, true);
2580
2581 } break;
2582 case ClickOver::CLICK_SELECT_KEYS: {
2583
2584 click.to = Point2(mb.x, mb.y);
2585 if (click.to.y < h && click.at.y > h && mb.relative_y < 0) {
2586
2587 float prev = v_scroll->get_val();
2588 v_scroll->set_val(v_scroll->get_val() - 1);
2589 if (prev != v_scroll->get_val())
2590 click.at.y += h;
2591 }
2592 if (click.to.y > size.height && click.at.y < size.height && mb.relative_y > 0) {
2593
2594 float prev = v_scroll->get_val();
2595 v_scroll->set_val(v_scroll->get_val() + 1);
2596 if (prev != v_scroll->get_val())
2597 click.at.y -= h;
2598 }
2599
2600 } break;
2601 case ClickOver::CLICK_MOVE_KEYS: {
2602
2603 click.to = Point2(mb.x, mb.y);
2604 } break;
2605 default: {}
2606 }
2607
2608 return;
2609 } else if (mb.button_mask & BUTTON_MASK_MIDDLE) {
2610
2611 int rel = mb.relative_x;
2612 float relf = rel / _get_zoom_scale();
2613 h_scroll->set_val(h_scroll->get_val() - relf);
2614 }
2615
2616 if (mb.button_mask == 0) {
2617
2618 Point2 mpos = Point2(mb.x, mb.y) - ofs;
2619
2620 if (mpos.y < h) {
2621 #if 0
2622 //seek
2623 //int zoomw = settings_limit-name_limit;
2624 float scale = _get_zoom_scale();
2625 float pos = h_scroll->get_val() + (mpos.y-name_limit) / scale;
2626 if (pos<0 )
2627 pos=0;
2628 if (pos>=animation->get_length())
2629 pos=animation->get_length();
2630 timeline->set_val(pos);
2631 #endif
2632 return;
2633 }
2634
2635 mpos.y -= h;
2636
2637 int idx = mpos.y / h;
2638 idx += v_scroll->get_val();
2639 if (idx < 0 || idx >= animation->get_track_count())
2640 break;
2641
2642 mouse_over.track = idx;
2643
2644 if (mpos.x < name_limit) {
2645 //name column
2646
2647 mouse_over.over = MouseOver::OVER_NAME;
2648
2649 } else if (mpos.x < settings_limit) {
2650
2651 float pos = mpos.x - name_limit;
2652 pos /= _get_zoom_scale();
2653 pos += h_scroll->get_val();
2654 float w_time = (type_icon[0]->get_width() / _get_zoom_scale()) / 2.0;
2655
2656 int kidx = animation->track_find_key(idx, pos);
2657 int kidx_n = kidx + 1;
2658
2659 bool found = false;
2660
2661 if (kidx >= 0 && kidx < animation->track_get_key_count(idx)) {
2662
2663 float kpos = animation->track_get_key_time(idx, kidx);
2664 if (ABS(pos - kpos) <= w_time) {
2665
2666 mouse_over.over = MouseOver::OVER_KEY;
2667 mouse_over.track = idx;
2668 mouse_over.over_key = kidx;
2669 found = true;
2670 }
2671 }
2672
2673 if (!found && kidx_n >= 0 && kidx_n < animation->track_get_key_count(idx)) {
2674
2675 float kpos = animation->track_get_key_time(idx, kidx_n);
2676 if (ABS(pos - kpos) <= w_time) {
2677
2678 mouse_over.over = MouseOver::OVER_KEY;
2679 mouse_over.track = idx;
2680 mouse_over.over_key = kidx_n;
2681 found = true;
2682 }
2683 }
2684
2685 if (found) {
2686
2687 String text;
2688 text = "time: " + rtos(animation->track_get_key_time(idx, mouse_over.over_key)) + "\n";
2689
2690 switch (animation->track_get_type(idx)) {
2691
2692 case Animation::TYPE_TRANSFORM: {
2693
2694 Dictionary d = animation->track_get_key_value(idx, mouse_over.over_key);
2695 if (d.has("loc"))
2696 text += "loc: " + String(d["loc"]) + "\n";
2697 if (d.has("rot"))
2698 text += "rot: " + String(d["rot"]) + "\n";
2699 if (d.has("scale"))
2700 text += "scale: " + String(d["scale"]) + "\n";
2701 } break;
2702 case Animation::TYPE_VALUE: {
2703
2704 Variant v = animation->track_get_key_value(idx, mouse_over.over_key);
2705 //text+="value: "+String(v)+"\n";
2706
2707 bool prop_exists = false;
2708 Variant::Type valid_type = Variant::NIL;
2709 Object *obj = NULL;
2710
2711 RES res;
2712 Node *node = root->get_node_and_resource(animation->track_get_path(idx), res);
2713
2714 if (res.is_valid()) {
2715 obj = res.ptr();
2716 } else if (node) {
2717 obj = node;
2718 }
2719
2720 if (obj) {
2721 valid_type = obj->get_static_property_type(animation->track_get_path(idx).get_property(), &prop_exists);
2722 }
2723
2724 text += "type: " + Variant::get_type_name(v.get_type()) + "\n";
2725 if (prop_exists && !Variant::can_convert(v.get_type(), valid_type)) {
2726 text += "value: " + String(v) + " (Invalid, expected type: " + Variant::get_type_name(valid_type) + ")\n";
2727 } else {
2728 text += "value: " + String(v) + "\n";
2729 }
2730
2731 } break;
2732 case Animation::TYPE_METHOD: {
2733
2734 Dictionary d = animation->track_get_key_value(idx, mouse_over.over_key);
2735 if (d.has("method"))
2736 text += String(d["method"]);
2737 text += "(";
2738 Vector<Variant> args;
2739 if (d.has("args"))
2740 args = d["args"];
2741 for (int i = 0; i < args.size(); i++) {
2742
2743 if (i > 0)
2744 text += ", ";
2745 text += String(args[i]);
2746 }
2747 text += ")\n";
2748
2749 } break;
2750 }
2751 text += "easing: " + rtos(animation->track_get_key_transition(idx, mouse_over.over_key));
2752
2753 track_editor->set_tooltip(text);
2754 return;
2755 }
2756
2757 } else {
2758 //button column
2759 int ofsx = size.width - mpos.x;
2760 if (ofsx < 0)
2761 break;
2762 /*
2763 if (ofsx < remove_icon->get_width()) {
2764
2765 mouse_over.over=MouseOver::OVER_REMOVE;
2766
2767 return;
2768 }
2769
2770 ofsx-=hsep+remove_icon->get_width();
2771
2772 if (ofsx < move_down_icon->get_width()) {
2773
2774 mouse_over.over=MouseOver::OVER_DOWN;
2775 return;
2776 }
2777
2778 ofsx-=hsep+move_down_icon->get_width();
2779
2780 if (ofsx < move_up_icon->get_width()) {
2781
2782 mouse_over.over=MouseOver::OVER_UP;
2783 return;
2784 }
2785
2786 ofsx-=hsep*3+move_up_icon->get_width();
2787
2788 */
2789
2790 if (ofsx < down_icon->get_width() + interp_icon[0]->get_width() + hsep * 2) {
2791
2792 mouse_over.over = MouseOver::OVER_INTERP;
2793 return;
2794 }
2795
2796 ofsx -= hsep * 2 + interp_icon[0]->get_width() + down_icon->get_width();
2797
2798 if (ofsx < down_icon->get_width() + cont_icon[0]->get_width() + hsep * 3) {
2799
2800 mouse_over.over = MouseOver::OVER_VALUE;
2801 return;
2802 }
2803
2804 ofsx -= hsep * 3 + cont_icon[0]->get_width() + down_icon->get_width();
2805
2806 if (ofsx < add_key_icon->get_width()) {
2807
2808 mouse_over.over = MouseOver::OVER_ADD_KEY;
2809 return;
2810 }
2811 }
2812 }
2813
2814 } break;
2815 }
2816 }
2817
_notification(int p_what)2818 void AnimationKeyEditor::_notification(int p_what) {
2819
2820 switch (p_what) {
2821 case NOTIFICATION_VISIBILITY_CHANGED: {
2822
2823 EditorNode::get_singleton()->update_keying();
2824 emit_signal("keying_changed");
2825 } break;
2826
2827 case NOTIFICATION_ENTER_TREE: {
2828
2829 key_editor->edit(key_edit);
2830
2831 zoomicon->set_texture(get_icon("Zoom", "EditorIcons"));
2832
2833 menu_add_track->set_icon(get_icon("AddTrack", "EditorIcons"));
2834 menu_add_track->get_popup()->add_icon_item(get_icon("KeyValue", "EditorIcons"), "Add Normal Track", ADD_TRACK_MENU_ADD_VALUE_TRACK);
2835 menu_add_track->get_popup()->add_icon_item(get_icon("KeyXform", "EditorIcons"), "Add Transform Track", ADD_TRACK_MENU_ADD_TRANSFORM_TRACK);
2836 menu_add_track->get_popup()->add_icon_item(get_icon("KeyCall", "EditorIcons"), "Add Call Func Track", ADD_TRACK_MENU_ADD_CALL_TRACK);
2837
2838 menu_track->set_icon(get_icon("Tools", "EditorIcons"));
2839 menu_track->get_popup()->add_item(TTR("Scale Selection"), TRACK_MENU_SCALE);
2840 menu_track->get_popup()->add_item(TTR("Scale From Cursor"), TRACK_MENU_SCALE_PIVOT);
2841 menu_track->get_popup()->add_separator();
2842 menu_track->get_popup()->add_item(TTR("Duplicate Selection"), TRACK_MENU_DUPLICATE);
2843 menu_track->get_popup()->add_item(TTR("Duplicate Transposed"), TRACK_MENU_DUPLICATE_TRANSPOSE);
2844 menu_track->get_popup()->add_separator();
2845 menu_track->get_popup()->add_item(TTR("Goto Next Step"), TRACK_MENU_NEXT_STEP, KEY_MASK_CMD | KEY_RIGHT);
2846 menu_track->get_popup()->add_item(TTR("Goto Prev Step"), TRACK_MENU_PREV_STEP, KEY_MASK_CMD | KEY_LEFT);
2847 menu_track->get_popup()->add_separator();
2848 PopupMenu *tpp = memnew(PopupMenu);
2849 tpp->add_item(TTR("Linear"), TRACK_MENU_SET_ALL_TRANS_LINEAR);
2850 tpp->add_item(TTR("Constant"), TRACK_MENU_SET_ALL_TRANS_CONSTANT);
2851 tpp->add_item(TTR("In"), TRACK_MENU_SET_ALL_TRANS_IN);
2852 tpp->add_item(TTR("Out"), TRACK_MENU_SET_ALL_TRANS_OUT);
2853 tpp->add_item(TTR("In-Out"), TRACK_MENU_SET_ALL_TRANS_INOUT);
2854 tpp->add_item(TTR("Out-In"), TRACK_MENU_SET_ALL_TRANS_OUTIN);
2855 tpp->set_name(TTR("Transitions"));
2856 tpp->connect("item_pressed", this, "_menu_track");
2857 optimize_dialog->connect("confirmed", this, "_animation_optimize");
2858
2859 menu_track->get_popup()->add_child(tpp);
2860 //menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
2861 //menu_track->get_popup()->add_separator();
2862 menu_track->get_popup()->add_item(TTR("Optimize Animation"), TRACK_MENU_OPTIMIZE);
2863 menu_track->get_popup()->add_item(TTR("Clean-Up Animation"), TRACK_MENU_CLEAN_UP);
2864
2865 curve_linear->set_icon(get_icon("CurveLinear", "EditorIcons"));
2866 curve_in->set_icon(get_icon("CurveIn", "EditorIcons"));
2867 curve_out->set_icon(get_icon("CurveOut", "EditorIcons"));
2868 curve_inout->set_icon(get_icon("CurveInOut", "EditorIcons"));
2869 curve_outin->set_icon(get_icon("CurveOutIn", "EditorIcons"));
2870 curve_constant->set_icon(get_icon("CurveConstant", "EditorIcons"));
2871
2872 curve_linear->connect("pressed", this, "_menu_track", varray(CURVE_SET_LINEAR));
2873 curve_in->connect("pressed", this, "_menu_track", varray(CURVE_SET_IN));
2874 curve_out->connect("pressed", this, "_menu_track", varray(CURVE_SET_OUT));
2875 curve_inout->connect("pressed", this, "_menu_track", varray(CURVE_SET_INOUT));
2876 curve_outin->connect("pressed", this, "_menu_track", varray(CURVE_SET_OUTIN));
2877 curve_constant->connect("pressed", this, "_menu_track", varray(CURVE_SET_CONSTANT));
2878
2879 move_up_button->set_icon(get_icon("MoveUp", "EditorIcons"));
2880 move_down_button->set_icon(get_icon("MoveDown", "EditorIcons"));
2881 remove_button->set_icon(get_icon("Remove", "EditorIcons"));
2882 edit_button->set_icon(get_icon("EditKey", "EditorIcons"));
2883 edit_button->connect("pressed", this, "_toggle_edit_curves");
2884
2885 loop->set_icon(get_icon("Loop", "EditorIcons"));
2886 curve_edit->connect("transition_changed", this, "_curve_transition_changed");
2887
2888 //edit_button->add_color_override("font_color",get_color("font_color","Tree"));
2889 //edit_button->add_color_override("font_color_hover",get_color("font_color","Tree"));
2890
2891 {
2892
2893 right_data_size_cache = 0;
2894 int hsep = get_constant("hseparation", "Tree");
2895 Ref<Texture> remove_icon = get_icon("Remove", "EditorIcons");
2896 Ref<Texture> move_up_icon = get_icon("MoveUp", "EditorIcons");
2897 Ref<Texture> move_down_icon = get_icon("MoveDown", "EditorIcons");
2898 Ref<Texture> down_icon = get_icon("select_arrow", "Tree");
2899 Ref<Texture> add_key_icon = get_icon("TrackAddKey", "EditorIcons");
2900 Ref<Texture> interp_icon[3] = {
2901 get_icon("InterpRaw", "EditorIcons"),
2902 get_icon("InterpLinear", "EditorIcons"),
2903 get_icon("InterpCubic", "EditorIcons")
2904 };
2905 Ref<Texture> cont_icon[3] = {
2906 get_icon("TrackContinuous", "EditorIcons"),
2907 get_icon("TrackDiscrete", "EditorIcons"),
2908 get_icon("TrackTrigger", "EditorIcons")
2909 };
2910
2911 //right_data_size_cache = remove_icon->get_width() + move_up_icon->get_width() + move_down_icon->get_width() + down_icon->get_width() *2 + interp_icon[0]->get_width() + cont_icon[0]->get_width() + add_key_icon->get_width() + hsep*11;
2912 right_data_size_cache = down_icon->get_width() * 2 + add_key_icon->get_width() + interp_icon[0]->get_width() + cont_icon[0]->get_width() + hsep * 7;
2913 }
2914 call_select->connect("selected", this, "_add_call_track");
2915 // rename_anim->set_icon( get_icon("Rename","EditorIcons") );
2916 /*
2917 edit_anim->set_icon( get_icon("Edit","EditorIcons") );
2918 blend_anim->set_icon( get_icon("Blend","EditorIcons") );
2919 play->set_icon( get_icon("Play","EditorIcons") );
2920 stop->set_icon( get_icon("Stop","EditorIcons") );
2921 pause->set_icon( get_icon("Pause","EditorIcons") );
2922 */
2923 // menu->set_icon(get_icon("Animation","EditorIcons"));
2924 // play->set_icon(get_icon("AnimationPlay","EditorIcons"));
2925 //menu->set_icon(get_icon("Animation","EditorIcons"));
2926 _update_menu();
2927
2928 } break;
2929 }
2930 }
2931
_scroll_changed(double)2932 void AnimationKeyEditor::_scroll_changed(double) {
2933
2934 if (te_drawing)
2935 return;
2936
2937 track_editor->update();
2938 }
2939
_update_paths()2940 void AnimationKeyEditor::_update_paths() {
2941
2942 if (animation.is_valid()) {
2943 //timeline->set_max(animation->get_length());
2944 //timeline->set_step(0.01);
2945 track_editor->update();
2946 length->set_val(animation->get_length());
2947 step->set_val(animation->get_step());
2948 }
2949 }
2950
_root_removed()2951 void AnimationKeyEditor::_root_removed() {
2952
2953 root = NULL;
2954 }
2955
_update_menu()2956 void AnimationKeyEditor::_update_menu() {
2957
2958 updating = true;
2959
2960 if (animation.is_valid()) {
2961
2962 length->set_val(animation->get_length());
2963 loop->set_pressed(animation->has_loop());
2964 step->set_val(animation->get_step());
2965 }
2966
2967 track_editor->update();
2968 updating = false;
2969 }
_clear_selection()2970 void AnimationKeyEditor::_clear_selection() {
2971
2972 selection.clear();
2973 key_edit->animation = Ref<Animation>();
2974 key_edit->track = 0;
2975 key_edit->key_ofs = 0;
2976 key_edit->hint = PropertyInfo();
2977 key_edit->base = NodePath();
2978 key_edit->notify_change();
2979 }
2980
set_animation(const Ref<Animation> & p_anim)2981 void AnimationKeyEditor::set_animation(const Ref<Animation> &p_anim) {
2982
2983 if (animation.is_valid())
2984 animation->disconnect("changed", this, "_update_paths");
2985 animation = p_anim;
2986 if (animation.is_valid())
2987 animation->connect("changed", this, "_update_paths");
2988
2989 timeline_pos = 0;
2990 _clear_selection();
2991 _update_paths();
2992
2993 _update_menu();
2994 selected_track = -1;
2995 _edit_if_single_selection();
2996
2997 EditorNode::get_singleton()->update_keying();
2998 }
2999
set_root(Node * p_root)3000 void AnimationKeyEditor::set_root(Node *p_root) {
3001
3002 if (root)
3003 root->disconnect("exit_tree", this, "_root_removed");
3004
3005 root = p_root;
3006
3007 if (root)
3008 root->connect("exit_tree", this, "_root_removed", make_binds(), CONNECT_ONESHOT);
3009 }
3010
get_root() const3011 Node *AnimationKeyEditor::get_root() const {
3012
3013 return root;
3014 }
3015
update_keying()3016 void AnimationKeyEditor::update_keying() {
3017
3018 bool keying_enabled = is_visible() && animation.is_valid();
3019
3020 if (keying_enabled == keying)
3021 return;
3022
3023 keying = keying_enabled;
3024 _update_menu();
3025 emit_signal("keying_changed");
3026 }
3027
has_keying() const3028 bool AnimationKeyEditor::has_keying() const {
3029
3030 return keying;
3031 }
3032
_query_insert(const InsertData & p_id)3033 void AnimationKeyEditor::_query_insert(const InsertData &p_id) {
3034
3035 if (insert_frame != OS::get_singleton()->get_frames_drawn()) {
3036 //clear insert list for the frame if frame changed
3037 if (insert_confirm->is_visible())
3038 return; //do nothing
3039 insert_data.clear();
3040 insert_query = false;
3041 }
3042 insert_frame = OS::get_singleton()->get_frames_drawn();
3043
3044 for (List<InsertData>::Element *E = insert_data.front(); E; E = E->next()) {
3045 //prevent insertion of multiple tracks
3046 if (E->get().path == p_id.path)
3047 return; //already inserted a track for this on this frame
3048 }
3049
3050 insert_data.push_back(p_id);
3051
3052 if (p_id.track_idx == -1) {
3053 if (bool(EDITOR_DEF("animation/confirm_insert_track", true))) {
3054 //potential new key, does not exist
3055 if (insert_data.size() == 1)
3056 insert_confirm->set_text(vformat(TTR("Create NEW track for %s and insert key?"), p_id.query));
3057 else
3058 insert_confirm->set_text(vformat(TTR("Create %d NEW tracks and insert keys?"), insert_data.size()));
3059
3060 insert_confirm->get_ok()->set_text(TTR("Create"));
3061 insert_confirm->popup_centered_minsize();
3062 insert_query = true;
3063 } else {
3064 call_deferred("_insert_delay");
3065 insert_queue = true;
3066 }
3067
3068 } else {
3069 if (!insert_query && !insert_queue) {
3070 call_deferred("_insert_delay");
3071 insert_queue = true;
3072 }
3073 }
3074 }
3075
insert_transform_key(Spatial * p_node,const String & p_sub,const Transform & p_xform)3076 void AnimationKeyEditor::insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform) {
3077
3078 if (!keying)
3079 return;
3080 if (!animation.is_valid())
3081 return;
3082
3083 ERR_FAIL_COND(!root);
3084 //let's build a node path
3085 String path = root->get_path_to(p_node);
3086 if (p_sub != "")
3087 path += ":" + p_sub;
3088
3089 NodePath np = path;
3090
3091 int track_idx = -1;
3092
3093 for (int i = 0; i < animation->get_track_count(); i++) {
3094
3095 if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM)
3096 continue;
3097 if (animation->track_get_path(i) != np)
3098 continue;
3099
3100 track_idx = i;
3101 break;
3102 }
3103
3104 InsertData id;
3105 Dictionary val;
3106
3107 id.path = np;
3108 id.track_idx = track_idx;
3109 id.value = p_xform;
3110 id.type = Animation::TYPE_TRANSFORM;
3111 id.query = "node '" + p_node->get_name() + "'";
3112 id.advance = false;
3113
3114 //dialog insert
3115
3116 _query_insert(id);
3117 }
3118
insert_node_value_key(Node * p_node,const String & p_property,const Variant & p_value,bool p_only_if_exists)3119 void AnimationKeyEditor::insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists) {
3120
3121 ERR_FAIL_COND(!root);
3122 //let's build a node path
3123
3124 Node *node = p_node;
3125
3126 String path = root->get_path_to(node);
3127
3128 for (int i = 1; i < history->get_path_size(); i++) {
3129
3130 String prop = history->get_path_property(i);
3131 ERR_FAIL_COND(prop == "");
3132 path += ":" + prop;
3133 }
3134
3135 path += ":" + p_property;
3136
3137 NodePath np = path;
3138
3139 //locate track
3140
3141 int track_idx = -1;
3142
3143 for (int i = 0; i < animation->get_track_count(); i++) {
3144
3145 if (animation->track_get_type(i) != Animation::TYPE_VALUE)
3146 continue;
3147 if (animation->track_get_path(i) != np)
3148 continue;
3149
3150 track_idx = i;
3151 break;
3152 }
3153
3154 if (p_only_if_exists && track_idx == -1)
3155 return;
3156 InsertData id;
3157 id.path = np;
3158 id.track_idx = track_idx;
3159 id.value = p_value;
3160 id.type = Animation::TYPE_VALUE;
3161 id.query = "property '" + p_property + "'";
3162 id.advance = false;
3163 //dialog insert
3164 _query_insert(id);
3165 }
3166
insert_value_key(const String & p_property,const Variant & p_value,bool p_advance)3167 void AnimationKeyEditor::insert_value_key(const String &p_property, const Variant &p_value, bool p_advance) {
3168
3169 ERR_FAIL_COND(!root);
3170 //let's build a node path
3171 ERR_FAIL_COND(history->get_path_size() == 0);
3172 Object *obj = ObjectDB::get_instance(history->get_path_object(0));
3173 ERR_FAIL_COND(!obj || !obj->cast_to<Node>());
3174
3175 Node *node = obj->cast_to<Node>();
3176
3177 String path = root->get_path_to(node);
3178
3179 for (int i = 1; i < history->get_path_size(); i++) {
3180
3181 String prop = history->get_path_property(i);
3182 ERR_FAIL_COND(prop == "");
3183 path += ":" + prop;
3184 }
3185
3186 path += ":" + p_property;
3187
3188 NodePath np = path;
3189
3190 //locate track
3191
3192 int track_idx = -1;
3193
3194 for (int i = 0; i < animation->get_track_count(); i++) {
3195
3196 if (animation->track_get_type(i) != Animation::TYPE_VALUE)
3197 continue;
3198 if (animation->track_get_path(i) != np)
3199 continue;
3200
3201 track_idx = i;
3202 break;
3203 }
3204
3205 InsertData id;
3206 id.path = np;
3207 id.track_idx = track_idx;
3208 id.value = p_value;
3209 id.type = Animation::TYPE_VALUE;
3210 id.query = "property '" + p_property + "'";
3211 id.advance = p_advance;
3212 //dialog insert
3213 _query_insert(id);
3214 }
3215
_confirm_insert_list()3216 void AnimationKeyEditor::_confirm_insert_list() {
3217
3218 undo_redo->create_action(TTR("Anim Create & Insert"));
3219
3220 int last_track = animation->get_track_count();
3221 while (insert_data.size()) {
3222
3223 last_track = _confirm_insert(insert_data.front()->get(), last_track);
3224 insert_data.pop_front();
3225 }
3226
3227 undo_redo->commit_action();
3228 }
3229
_confirm_insert(InsertData p_id,int p_last_track)3230 int AnimationKeyEditor::_confirm_insert(InsertData p_id, int p_last_track) {
3231
3232 if (p_last_track == -1)
3233 p_last_track = animation->get_track_count();
3234
3235 bool created = false;
3236 if (p_id.track_idx < 0) {
3237
3238 created = true;
3239 undo_redo->create_action(TTR("Anim Insert Track & Key"));
3240 Animation::UpdateMode update_mode = Animation::UPDATE_DISCRETE;
3241
3242 if (p_id.type == Animation::TYPE_VALUE) {
3243 //wants a new tack
3244
3245 {
3246 //shitty hack
3247 NodePath np;
3248 animation->add_track(p_id.type);
3249 animation->track_set_path(animation->get_track_count() - 1, p_id.path);
3250 PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
3251 animation->remove_track(animation->get_track_count() - 1); //hack
3252
3253 if (h.type == Variant::REAL ||
3254 h.type == Variant::VECTOR2 ||
3255 h.type == Variant::RECT2 ||
3256 h.type == Variant::VECTOR3 ||
3257 h.type == Variant::_AABB ||
3258 h.type == Variant::QUAT ||
3259 h.type == Variant::COLOR ||
3260 h.type == Variant::TRANSFORM) {
3261
3262 update_mode = Animation::UPDATE_CONTINUOUS;
3263 }
3264
3265 if (h.usage & PROPERTY_USAGE_ANIMATE_AS_TRIGGER) {
3266 update_mode = Animation::UPDATE_TRIGGER;
3267 }
3268 }
3269 }
3270
3271 p_id.track_idx = p_last_track;
3272
3273 undo_redo->add_do_method(animation.ptr(), "add_track", p_id.type);
3274 undo_redo->add_do_method(animation.ptr(), "track_set_path", p_id.track_idx, p_id.path);
3275 if (p_id.type == Animation::TYPE_VALUE)
3276 undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", p_id.track_idx, update_mode);
3277
3278 } else {
3279 undo_redo->create_action(TTR("Anim Insert Key"));
3280 }
3281
3282 float time = timeline_pos;
3283 Variant value;
3284
3285 switch (p_id.type) {
3286
3287 case Animation::TYPE_VALUE: {
3288
3289 value = p_id.value;
3290
3291 } break;
3292 case Animation::TYPE_TRANSFORM: {
3293
3294 Transform tr = p_id.value;
3295 Dictionary d;
3296 d["loc"] = tr.origin;
3297 d["scale"] = tr.basis.get_scale();
3298 d["rot"] = Quat(tr.basis); //.orthonormalized();
3299 value = d;
3300 } break;
3301 default: {}
3302 }
3303
3304 undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, value);
3305
3306 if (created) {
3307
3308 //just remove the track
3309 undo_redo->add_undo_method(animation.ptr(), "remove_track", p_last_track);
3310 p_last_track++;
3311 } else {
3312
3313 undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_pos", p_id.track_idx, time);
3314 int existing = animation->track_find_key(p_id.track_idx, time, true);
3315 if (existing != -1) {
3316 Variant v = animation->track_get_key_value(p_id.track_idx, existing);
3317 float trans = animation->track_get_key_transition(p_id.track_idx, existing);
3318 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, v, trans);
3319 }
3320 }
3321
3322 undo_redo->add_do_method(this, "update");
3323 undo_redo->add_undo_method(this, "update");
3324 undo_redo->add_do_method(track_editor, "update");
3325 undo_redo->add_undo_method(track_editor, "update");
3326 undo_redo->add_do_method(track_pos, "update");
3327 undo_redo->add_undo_method(track_pos, "update");
3328
3329 undo_redo->commit_action();
3330
3331 return p_last_track;
3332 }
3333
get_current_animation() const3334 Ref<Animation> AnimationKeyEditor::get_current_animation() const {
3335
3336 return animation;
3337 }
3338
_animation_len_changed(float p_len)3339 void AnimationKeyEditor::_animation_len_changed(float p_len) {
3340
3341 if (updating)
3342 return;
3343
3344 if (!animation.is_null()) {
3345
3346 undo_redo->create_action(TTR("Change Anim Len"));
3347 undo_redo->add_do_method(animation.ptr(), "set_length", p_len);
3348 undo_redo->add_undo_method(animation.ptr(), "set_length", animation->get_length());
3349 undo_redo->add_do_method(this, "_animation_len_update");
3350 undo_redo->add_undo_method(this, "_animation_len_update");
3351 undo_redo->commit_action();
3352 }
3353 }
3354
_animation_len_update()3355 void AnimationKeyEditor::_animation_len_update() {
3356
3357 if (!animation.is_null())
3358 emit_signal(alc, animation->get_length());
3359 }
3360
_animation_changed()3361 void AnimationKeyEditor::_animation_changed() {
3362 if (updating)
3363 return;
3364 _update_menu();
3365 }
3366
_animation_loop_changed()3367 void AnimationKeyEditor::_animation_loop_changed() {
3368
3369 if (updating)
3370 return;
3371
3372 if (!animation.is_null()) {
3373
3374 undo_redo->create_action(TTR("Change Anim Loop"));
3375 undo_redo->add_do_method(animation.ptr(), "set_loop", loop->is_pressed());
3376 undo_redo->add_undo_method(animation.ptr(), "set_loop", !loop->is_pressed());
3377 undo_redo->commit_action();
3378 }
3379 }
3380
_create_value_item(int p_type)3381 void AnimationKeyEditor::_create_value_item(int p_type) {
3382
3383 undo_redo->create_action(TTR("Anim Create Typed Value Key"));
3384
3385 Variant::CallError ce;
3386 Variant v = Variant::construct(Variant::Type(p_type), NULL, 0, ce);
3387 undo_redo->add_do_method(animation.ptr(), "track_insert_key", cvi_track, cvi_pos, v);
3388 undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_pos", cvi_track, cvi_pos);
3389
3390 int existing = animation->track_find_key(cvi_track, cvi_pos, true);
3391
3392 if (existing != -1) {
3393 Variant v = animation->track_get_key_value(cvi_track, existing);
3394 float trans = animation->track_get_key_transition(cvi_track, existing);
3395 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", cvi_track, cvi_pos, v, trans);
3396 }
3397
3398 undo_redo->commit_action();
3399 }
3400
set_anim_pos(float p_pos)3401 void AnimationKeyEditor::set_anim_pos(float p_pos) {
3402
3403 if (animation.is_null())
3404 return;
3405 timeline_pos = p_pos;
3406 update();
3407 track_pos->update();
3408 track_editor->update();
3409 }
3410
_pane_drag(const Point2 & p_delta)3411 void AnimationKeyEditor::_pane_drag(const Point2 &p_delta) {
3412
3413 Size2 ecs = ec->get_custom_minimum_size();
3414 ecs.y -= p_delta.y;
3415 if (ecs.y < 100)
3416 ecs.y = 100;
3417 ec->set_custom_minimum_size(ecs);
3418 }
3419
_insert_delay()3420 void AnimationKeyEditor::_insert_delay() {
3421
3422 if (insert_query) {
3423 //discard since it's entered into query mode
3424 insert_queue = false;
3425 return;
3426 }
3427
3428 undo_redo->create_action(TTR("Anim Insert"));
3429
3430 int last_track = animation->get_track_count();
3431 bool advance = false;
3432 while (insert_data.size()) {
3433
3434 if (insert_data.front()->get().advance)
3435 advance = true;
3436 last_track = _confirm_insert(insert_data.front()->get(), last_track);
3437 insert_data.pop_front();
3438 }
3439
3440 undo_redo->commit_action();
3441
3442 if (advance) {
3443 float step = animation->get_step();
3444 if (step == 0)
3445 step = 1;
3446
3447 float pos = timeline_pos;
3448
3449 pos = Math::stepify(pos + step, step);
3450 if (pos > animation->get_length())
3451 pos = animation->get_length();
3452 timeline_pos = pos;
3453 track_pos->update();
3454 emit_signal("timeline_changed", pos, true);
3455 }
3456 insert_queue = false;
3457 }
3458
_step_changed(float p_len)3459 void AnimationKeyEditor::_step_changed(float p_len) {
3460
3461 updating = true;
3462 if (!animation.is_null()) {
3463 animation->set_step(p_len);
3464 emit_signal("animation_step_changed", animation->get_step());
3465 }
3466 updating = false;
3467 }
3468
_scale()3469 void AnimationKeyEditor::_scale() {
3470
3471 if (selection.empty())
3472 return;
3473
3474 float from_t = 1e20;
3475 float to_t = -1e20;
3476 float len = -1e20;
3477 float pivot = 0;
3478
3479 for (Map<SelectedKey, KeyInfo>::Element *E = selection.front(); E; E = E->next()) {
3480 float t = animation->track_get_key_time(E->key().track, E->key().key);
3481 if (t < from_t)
3482 from_t = t;
3483 if (t > to_t)
3484 to_t = t;
3485 }
3486
3487 len = to_t - from_t;
3488 if (last_menu_track_opt == TRACK_MENU_SCALE_PIVOT) {
3489 pivot = timeline_pos;
3490
3491 } else {
3492
3493 pivot = from_t;
3494 }
3495
3496 float s = scale->get_val();
3497 if (s == 0) {
3498 ERR_PRINT("Can't scale to 0");
3499 }
3500
3501 undo_redo->create_action(TTR("Anim Scale Keys"));
3502
3503 List<_AnimMoveRestore> to_restore;
3504
3505 // 1-remove the keys
3506 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
3507
3508 undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
3509 }
3510 // 2- remove overlapped keys
3511 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
3512
3513 float newtime = (E->get().pos - from_t) * s + from_t;
3514 int idx = animation->track_find_key(E->key().track, newtime, true);
3515 if (idx == -1)
3516 continue;
3517 SelectedKey sk;
3518 sk.key = idx;
3519 sk.track = E->key().track;
3520 if (selection.has(sk))
3521 continue; //already in selection, don't save
3522
3523 undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_pos", E->key().track, newtime);
3524 _AnimMoveRestore amr;
3525
3526 amr.key = animation->track_get_key_value(E->key().track, idx);
3527 amr.track = E->key().track;
3528 amr.time = newtime;
3529 amr.transition = animation->track_get_key_transition(E->key().track, idx);
3530
3531 to_restore.push_back(amr);
3532 }
3533
3534 #define _NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * ABS(s) + from_t
3535 // 3-move the keys (re insert them)
3536 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
3537
3538 float newpos = _NEW_POS(E->get().pos);
3539 undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
3540 }
3541
3542 // 4-(undo) remove inserted keys
3543 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
3544
3545 float newpos = _NEW_POS(E->get().pos);
3546 undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_pos", E->key().track, newpos);
3547 }
3548
3549 // 5-(undo) reinsert keys
3550 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
3551
3552 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
3553 }
3554
3555 // 6-(undo) reinsert overlapped keys
3556 for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
3557
3558 _AnimMoveRestore &amr = E->get();
3559 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
3560 }
3561
3562 // 6-(undo) reinsert overlapped keys
3563 for (List<_AnimMoveRestore>::Element *E = to_restore.front(); E; E = E->next()) {
3564
3565 _AnimMoveRestore &amr = E->get();
3566 undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
3567 }
3568
3569 undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
3570 undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
3571
3572 // 7-reselect
3573
3574 for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
3575
3576 float oldpos = E->get().pos;
3577 float newpos = _NEW_POS(oldpos);
3578 if (newpos >= 0)
3579 undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
3580 undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
3581 }
3582 #undef _NEW_POS
3583 undo_redo->commit_action();
3584 }
3585
_add_call_track(const NodePath & p_base)3586 void AnimationKeyEditor::_add_call_track(const NodePath &p_base) {
3587
3588 Node *base = EditorNode::get_singleton()->get_edited_scene();
3589 if (!base)
3590 return;
3591 Node *from = base->get_node(p_base);
3592 if (!from || !root)
3593 return;
3594
3595 NodePath path = root->get_path_to(from);
3596
3597 //print_line("root: "+String(root->get_path()));
3598 //print_line("path: "+String(path));
3599
3600 undo_redo->create_action(TTR("Anim Add Call Track"));
3601 undo_redo->add_do_method(animation.ptr(), "add_track", Animation::TYPE_METHOD);
3602 undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path);
3603 undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
3604 undo_redo->commit_action();
3605 }
3606
cleanup()3607 void AnimationKeyEditor::cleanup() {
3608
3609 set_animation(Ref<Animation>());
3610 }
3611
_bind_methods()3612 void AnimationKeyEditor::_bind_methods() {
3613
3614 ObjectTypeDB::bind_method(_MD("_root_removed"), &AnimationKeyEditor::_root_removed);
3615 ObjectTypeDB::bind_method(_MD("_scale"), &AnimationKeyEditor::_scale);
3616 ObjectTypeDB::bind_method(_MD("set_root"), &AnimationKeyEditor::set_root);
3617
3618 // ObjectTypeDB::bind_method(_MD("_confirm_insert"),&AnimationKeyEditor::_confirm_insert);
3619 ObjectTypeDB::bind_method(_MD("_confirm_insert_list"), &AnimationKeyEditor::_confirm_insert_list);
3620
3621 ObjectTypeDB::bind_method(_MD("_update_paths"), &AnimationKeyEditor::_update_paths);
3622 ObjectTypeDB::bind_method(_MD("_track_editor_draw"), &AnimationKeyEditor::_track_editor_draw);
3623
3624 ObjectTypeDB::bind_method(_MD("_animation_changed"), &AnimationKeyEditor::_animation_changed);
3625 ObjectTypeDB::bind_method(_MD("_scroll_changed"), &AnimationKeyEditor::_scroll_changed);
3626 ObjectTypeDB::bind_method(_MD("_track_editor_input_event"), &AnimationKeyEditor::_track_editor_input_event);
3627 ObjectTypeDB::bind_method(_MD("_track_name_changed"), &AnimationKeyEditor::_track_name_changed);
3628 ObjectTypeDB::bind_method(_MD("_track_menu_selected"), &AnimationKeyEditor::_track_menu_selected);
3629 ObjectTypeDB::bind_method(_MD("_menu_add_track"), &AnimationKeyEditor::_menu_add_track);
3630 ObjectTypeDB::bind_method(_MD("_menu_track"), &AnimationKeyEditor::_menu_track);
3631 ObjectTypeDB::bind_method(_MD("_clear_selection_for_anim"), &AnimationKeyEditor::_clear_selection_for_anim);
3632 ObjectTypeDB::bind_method(_MD("_select_at_anim"), &AnimationKeyEditor::_select_at_anim);
3633 ObjectTypeDB::bind_method(_MD("_track_pos_draw"), &AnimationKeyEditor::_track_pos_draw);
3634 ObjectTypeDB::bind_method(_MD("_insert_delay"), &AnimationKeyEditor::_insert_delay);
3635 ObjectTypeDB::bind_method(_MD("_step_changed"), &AnimationKeyEditor::_step_changed);
3636
3637 ObjectTypeDB::bind_method(_MD("_animation_loop_changed"), &AnimationKeyEditor::_animation_loop_changed);
3638 ObjectTypeDB::bind_method(_MD("_animation_len_changed"), &AnimationKeyEditor::_animation_len_changed);
3639 ObjectTypeDB::bind_method(_MD("_create_value_item"), &AnimationKeyEditor::_create_value_item);
3640 ObjectTypeDB::bind_method(_MD("_pane_drag"), &AnimationKeyEditor::_pane_drag);
3641
3642 ObjectTypeDB::bind_method(_MD("_animation_len_update"), &AnimationKeyEditor::_animation_len_update);
3643
3644 ObjectTypeDB::bind_method(_MD("set_animation"), &AnimationKeyEditor::set_animation);
3645 ObjectTypeDB::bind_method(_MD("_animation_optimize"), &AnimationKeyEditor::_animation_optimize);
3646 ObjectTypeDB::bind_method(_MD("_curve_transition_changed"), &AnimationKeyEditor::_curve_transition_changed);
3647 ObjectTypeDB::bind_method(_MD("_toggle_edit_curves"), &AnimationKeyEditor::_toggle_edit_curves);
3648 ObjectTypeDB::bind_method(_MD("_add_call_track"), &AnimationKeyEditor::_add_call_track);
3649
3650 ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
3651 ADD_SIGNAL(MethodInfo("keying_changed"));
3652 ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "pos"), PropertyInfo(Variant::BOOL, "drag")));
3653 ADD_SIGNAL(MethodInfo("animation_len_changed", PropertyInfo(Variant::REAL, "len")));
3654 ADD_SIGNAL(MethodInfo("animation_step_changed", PropertyInfo(Variant::REAL, "step")));
3655 ADD_SIGNAL(MethodInfo("key_edited", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "key")));
3656 }
3657
AnimationKeyEditor()3658 AnimationKeyEditor::AnimationKeyEditor() {
3659
3660 alc = "animation_len_changed";
3661 editor_selection = EditorNode::get_singleton()->get_editor_selection();
3662
3663 selected_track = -1;
3664 updating = false;
3665 te_drawing = false;
3666 undo_redo = EditorNode::get_singleton()->get_undo_redo();
3667 history = EditorNode::get_singleton()->get_editor_history();
3668
3669 ec = memnew(Control);
3670 ec->set_custom_minimum_size(Size2(0, 150));
3671 add_child(ec);
3672 ec->set_v_size_flags(SIZE_EXPAND_FILL);
3673
3674 h_scroll = memnew(HScrollBar);
3675 h_scroll->connect("value_changed", this, "_scroll_changed");
3676 add_child(h_scroll);
3677 h_scroll->set_val(0);
3678
3679 HBoxContainer *hb = memnew(HBoxContainer);
3680 add_child(hb);
3681
3682 root = NULL;
3683 //menu = memnew( MenuButton );
3684 //menu->set_flat(true);
3685 //menu->set_pos(Point2());
3686 //add_child(menu);
3687
3688 zoomicon = memnew(TextureFrame);
3689 hb->add_child(zoomicon);
3690 zoomicon->set_tooltip(TTR("Animation zoom."));
3691
3692 zoom = memnew(HSlider);
3693 //hb->add_child(zoom);
3694 zoom->set_step(0.01);
3695 zoom->set_min(0.0);
3696 zoom->set_max(2.0);
3697 zoom->set_val(1.0);
3698 zoom->set_h_size_flags(SIZE_EXPAND_FILL);
3699 zoom->set_stretch_ratio(2);
3700 hb->add_child(zoom);
3701 zoom->connect("value_changed", this, "_scroll_changed");
3702 zoom->set_tooltip(TTR("Animation zoom."));
3703
3704 hb->add_child(memnew(VSeparator));
3705
3706 Label *l = memnew(Label);
3707 l->set_text(TTR("Length (s):"));
3708 hb->add_child(l);
3709
3710 length = memnew(SpinBox);
3711 length->set_min(0.01);
3712 length->set_max(10000);
3713 length->set_step(0.01);
3714 length->set_h_size_flags(SIZE_EXPAND_FILL);
3715 length->set_stretch_ratio(1);
3716 length->set_tooltip(TTR("Animation length (in seconds)."));
3717
3718 hb->add_child(length);
3719 length->connect("value_changed", this, "_animation_len_changed");
3720
3721 l = memnew(Label);
3722 l->set_text(TTR("Step (s):"));
3723 hb->add_child(l);
3724
3725 step = memnew(SpinBox);
3726 step->set_min(0.00);
3727 step->set_max(128);
3728 step->set_step(0.01);
3729 step->set_val(0.0);
3730 step->set_h_size_flags(SIZE_EXPAND_FILL);
3731 step->set_stretch_ratio(1);
3732 step->set_tooltip(TTR("Cursor step snap (in seconds)."));
3733
3734 hb->add_child(step);
3735 step->connect("value_changed", this, "_step_changed");
3736
3737 loop = memnew(ToolButton);
3738 loop->set_toggle_mode(true);
3739 loop->connect("pressed", this, "_animation_loop_changed");
3740 hb->add_child(loop);
3741 loop->set_tooltip(TTR("Enable/Disable looping in animation."));
3742
3743 hb->add_child(memnew(VSeparator));
3744
3745 menu_add_track = memnew(MenuButton);
3746 hb->add_child(menu_add_track);
3747 menu_add_track->get_popup()->connect("item_pressed", this, "_menu_add_track");
3748 menu_add_track->set_tooltip(TTR("Add new tracks."));
3749
3750 move_up_button = memnew(ToolButton);
3751 hb->add_child(move_up_button);
3752 move_up_button->connect("pressed", this, "_menu_track", make_binds(TRACK_MENU_MOVE_UP));
3753 move_up_button->set_focus_mode(FOCUS_NONE);
3754 move_up_button->set_disabled(true);
3755 move_up_button->set_tooltip(TTR("Move current track up."));
3756
3757 move_down_button = memnew(ToolButton);
3758 hb->add_child(move_down_button);
3759 move_down_button->connect("pressed", this, "_menu_track", make_binds(TRACK_MENU_MOVE_DOWN));
3760 move_down_button->set_focus_mode(FOCUS_NONE);
3761 move_down_button->set_disabled(true);
3762 move_down_button->set_tooltip(TTR("Move current track down."));
3763
3764 remove_button = memnew(ToolButton);
3765 hb->add_child(remove_button);
3766 remove_button->connect("pressed", this, "_menu_track", make_binds(TRACK_MENU_REMOVE));
3767 remove_button->set_focus_mode(FOCUS_NONE);
3768 remove_button->set_disabled(true);
3769 remove_button->set_tooltip(TTR("Remove selected track."));
3770
3771 hb->add_child(memnew(VSeparator));
3772
3773 menu_track = memnew(MenuButton);
3774 hb->add_child(menu_track);
3775 menu_track->get_popup()->connect("item_pressed", this, "_menu_track");
3776 menu_track->set_tooltip(TTR("Track tools"));
3777
3778 edit_button = memnew(ToolButton);
3779 edit_button->set_toggle_mode(true);
3780 edit_button->set_focus_mode(FOCUS_NONE);
3781 edit_button->set_disabled(true);
3782
3783 hb->add_child(edit_button);
3784 edit_button->set_tooltip(TTR("Enable editing of individual keys by clicking them."));
3785
3786 optimize_dialog = memnew(ConfirmationDialog);
3787 add_child(optimize_dialog);
3788 optimize_dialog->set_title(TTR("Anim. Optimizer"));
3789 VBoxContainer *optimize_vb = memnew(VBoxContainer);
3790 optimize_dialog->add_child(optimize_vb);
3791 optimize_dialog->set_child_rect(optimize_vb);
3792 optimize_linear_error = memnew(SpinBox);
3793 optimize_linear_error->set_max(1.0);
3794 optimize_linear_error->set_min(0.001);
3795 optimize_linear_error->set_step(0.001);
3796 optimize_linear_error->set_val(0.05);
3797 optimize_vb->add_margin_child(TTR("Max. Linear Error:"), optimize_linear_error);
3798 optimize_angular_error = memnew(SpinBox);
3799 optimize_angular_error->set_max(1.0);
3800 optimize_angular_error->set_min(0.001);
3801 optimize_angular_error->set_step(0.001);
3802 optimize_angular_error->set_val(0.01);
3803
3804 optimize_vb->add_margin_child(TTR("Max. Angular Error:"), optimize_angular_error);
3805 optimize_max_angle = memnew(SpinBox);
3806 optimize_vb->add_margin_child(TTR("Max Optimizable Angle:"), optimize_max_angle);
3807 optimize_max_angle->set_max(360.0);
3808 optimize_max_angle->set_min(0.0);
3809 optimize_max_angle->set_step(0.1);
3810 optimize_max_angle->set_val(22);
3811
3812 optimize_dialog->get_ok()->set_text(TTR("Optimize"));
3813
3814 /*keying = memnew( Button );
3815 keying->set_toggle_mode(true);
3816 //keying->set_text("Keys");
3817 keying->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,60);
3818 keying->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,10);
3819 keying->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,55);
3820 keying->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,10);
3821 //add_child(keying);
3822 keying->connect("pressed",this,"_keying_toggled");
3823 */
3824
3825 /* l = memnew( Label );
3826 l->set_text("Base: ");
3827 l->set_pos(Point2(0,3));
3828 // dr_panel->add_child(l);*/
3829
3830 // menu->get_popup()->connect("item_pressed",this,"_menu_callback");
3831
3832 hb = memnew(HBoxContainer);
3833 hb->set_area_as_parent_rect();
3834 ec->add_child(hb);
3835 hb->set_v_size_flags(SIZE_EXPAND_FILL);
3836
3837 track_editor = memnew(Control);
3838 track_editor->connect("draw", this, "_track_editor_draw");
3839 hb->add_child(track_editor);
3840 track_editor->connect("input_event", this, "_track_editor_input_event");
3841 track_editor->set_focus_mode(Control::FOCUS_ALL);
3842 track_editor->set_h_size_flags(SIZE_EXPAND_FILL);
3843
3844 track_pos = memnew(Control);
3845 track_pos->set_area_as_parent_rect();
3846 track_pos->set_ignore_mouse(true);
3847 track_editor->add_child(track_pos);
3848 track_pos->connect("draw", this, "_track_pos_draw");
3849
3850 select_anim_warning = memnew(Label);
3851 track_editor->add_child(select_anim_warning);
3852 select_anim_warning->set_area_as_parent_rect();
3853 select_anim_warning->set_text(TTR("Select an AnimationPlayer from the Scene Tree to edit animations."));
3854 select_anim_warning->set_autowrap(true);
3855 select_anim_warning->set_align(Label::ALIGN_CENTER);
3856 select_anim_warning->set_valign(Label::VALIGN_CENTER);
3857
3858 v_scroll = memnew(VScrollBar);
3859 hb->add_child(v_scroll);
3860 v_scroll->connect("value_changed", this, "_scroll_changed");
3861 v_scroll->set_val(0);
3862
3863 key_editor_tab = memnew(TabContainer);
3864 hb->add_child(key_editor_tab);
3865 key_editor_tab->set_custom_minimum_size(Size2(200, 0));
3866
3867 key_editor = memnew(PropertyEditor);
3868 key_editor->set_area_as_parent_rect();
3869 key_editor->hide_top_label();
3870 key_editor->set_name(TTR("Key"));
3871 key_editor_tab->add_child(key_editor);
3872
3873 key_edit = memnew(AnimationKeyEdit);
3874 key_edit->undo_redo = undo_redo;
3875 //key_edit->ke_dialog=key_edit_dialog;
3876
3877 type_menu = memnew(PopupMenu);
3878 add_child(type_menu);
3879 for (int i = 0; i < Variant::VARIANT_MAX; i++)
3880 type_menu->add_item(Variant::get_type_name(Variant::Type(i)), i);
3881 type_menu->connect("item_pressed", this, "_create_value_item");
3882
3883 VBoxContainer *curve_vb = memnew(VBoxContainer);
3884 curve_vb->set_name(TTR("Transition"));
3885 HBoxContainer *curve_hb = memnew(HBoxContainer);
3886 curve_vb->add_child(curve_hb);
3887
3888 curve_linear = memnew(ToolButton);
3889 curve_linear->set_focus_mode(FOCUS_NONE);
3890 curve_hb->add_child(curve_linear);
3891 curve_in = memnew(ToolButton);
3892 curve_in->set_focus_mode(FOCUS_NONE);
3893 curve_hb->add_child(curve_in);
3894 curve_out = memnew(ToolButton);
3895 curve_out->set_focus_mode(FOCUS_NONE);
3896 curve_hb->add_child(curve_out);
3897 curve_inout = memnew(ToolButton);
3898 curve_inout->set_focus_mode(FOCUS_NONE);
3899 curve_hb->add_child(curve_inout);
3900 curve_outin = memnew(ToolButton);
3901 curve_outin->set_focus_mode(FOCUS_NONE);
3902 curve_hb->add_child(curve_outin);
3903 curve_constant = memnew(ToolButton);
3904 curve_constant->set_focus_mode(FOCUS_NONE);
3905 curve_hb->add_child(curve_constant);
3906
3907 curve_edit = memnew(AnimationCurveEdit);
3908 curve_vb->add_child(curve_edit);
3909 curve_edit->set_v_size_flags(SIZE_EXPAND_FILL);
3910 key_editor_tab->add_child(curve_vb);
3911
3912 track_name = memnew(LineEdit);
3913 track_name->set_as_toplevel(true);
3914 track_name->hide();
3915 add_child(track_name);
3916 track_name->connect("text_entered", this, "_track_name_changed");
3917 track_menu = memnew(PopupMenu);
3918 add_child(track_menu);
3919 track_menu->connect("item_pressed", this, "_track_menu_selected");
3920
3921 key_editor_tab->hide();
3922
3923 last_idx = 1;
3924
3925 _update_menu();
3926
3927 insert_confirm = memnew(ConfirmationDialog);
3928 add_child(insert_confirm);
3929 insert_confirm->connect("confirmed", this, "_confirm_insert_list");
3930
3931 click.click = ClickOver::CLICK_NONE;
3932
3933 name_column_ratio = 0.3;
3934 timeline_pos = 0;
3935
3936 keying = false;
3937 insert_frame = 0;
3938 insert_query = false;
3939 insert_queue = false;
3940
3941 editor_selection->connect("selection_changed", track_editor, "update");
3942
3943 scale_dialog = memnew(ConfirmationDialog);
3944 VBoxContainer *vbc = memnew(VBoxContainer);
3945 scale_dialog->add_child(vbc);
3946 scale_dialog->set_child_rect(vbc);
3947 scale = memnew(SpinBox);
3948 scale->set_min(-99999);
3949 scale->set_max(99999);
3950 scale->set_step(0.001);
3951 vbc->add_margin_child(TTR("Scale Ratio:"), scale);
3952 scale_dialog->connect("confirmed", this, "_scale");
3953 add_child(scale_dialog);
3954
3955 call_select = memnew(SceneTreeDialog);
3956 add_child(call_select);
3957 call_select->set_title(TTR("Call Functions in Which Node?"));
3958
3959 cleanup_dialog = memnew(ConfirmationDialog);
3960 add_child(cleanup_dialog);
3961 VBoxContainer *cleanup_vb = memnew(VBoxContainer);
3962 cleanup_dialog->add_child(cleanup_vb);
3963 cleanup_dialog->set_child_rect(cleanup_vb);
3964 cleanup_keys = memnew(CheckButton);
3965 cleanup_keys->set_text(TTR("Remove invalid keys"));
3966 cleanup_keys->set_pressed(true);
3967 cleanup_vb->add_child(cleanup_keys);
3968
3969 cleanup_tracks = memnew(CheckButton);
3970 cleanup_tracks->set_text(TTR("Remove unresolved and empty tracks"));
3971 cleanup_tracks->set_pressed(true);
3972 cleanup_vb->add_child(cleanup_tracks);
3973
3974 cleanup_all = memnew(CheckButton);
3975 cleanup_all->set_text(TTR("Clean-up all animations"));
3976 cleanup_vb->add_child(cleanup_all);
3977
3978 cleanup_dialog->set_title(TTR("Clean-Up Animation(s) (NO UNDO!)"));
3979 cleanup_dialog->get_ok()->set_text(TTR("Clean-Up"));
3980
3981 cleanup_dialog->connect("confirmed", this, "_menu_track", varray(TRACK_MENU_CLEAN_UP_CONFIRM));
3982
3983 add_constant_override("separation", get_constant("separation", "VBoxContainer"));
3984 }
3985
~AnimationKeyEditor()3986 AnimationKeyEditor::~AnimationKeyEditor() {
3987
3988 memdelete(key_edit);
3989 }
3990