Browse Source

improved animation editor

-same-value link keys
-new layout
-forward, backwards playback
-integrated curve/property editor
-auto increment sprite frame after insert
-copy & paste animation resoucres
Juan Linietsky 10 years ago
parent
commit
f36e7dcb40

+ 33 - 0
core/object.cpp

@@ -995,12 +995,44 @@ Variant Object::get_meta(const String& p_name) const {
 	return metadata[p_name];
 }
 
+
 Array Object::_get_property_list_bind() const {
 
 	List<PropertyInfo> lpi;
 	get_property_list(&lpi);
 	return convert_property_list(&lpi);
 }
+
+Array Object::_get_method_list_bind() const {
+
+	List<MethodInfo> ml;
+	get_method_list(&ml);
+	Array ret;
+
+	for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
+
+		Dictionary d;
+		d["name"]=E->get().name;
+		d["args"]=convert_property_list(&E->get().arguments);
+		Array da;
+		for(int i=0;i<E->get().default_arguments.size();i++)
+			da.push_back(E->get().default_arguments[i]);
+		d["default_args"]=da;
+		d["flags"]=E->get().flags;
+		d["id"]=E->get().id;
+		Dictionary r;
+		r["type"]=E->get().return_val.type;
+		r["hint"]=E->get().return_val.hint;
+		r["hint_string"]=E->get().return_val.hint_string;
+		d["return_type"]=r;
+		//va.push_back(d);
+		ret.push_back(d);
+	}
+
+	return ret;
+
+}
+
 DVector<String> Object::_get_meta_list_bind() const {
 
 	DVector<String> _metaret;
@@ -1439,6 +1471,7 @@ void Object::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set","property","value"),&Object::_set_bind);
 	ObjectTypeDB::bind_method(_MD("get","property"),&Object::_get_bind);
 	ObjectTypeDB::bind_method(_MD("get_property_list"),&Object::_get_property_list_bind);
+	ObjectTypeDB::bind_method(_MD("get_method_list"),&Object::_get_method_list_bind);
 	ObjectTypeDB::bind_method(_MD("notification","what"),&Object::notification,DEFVAL(false));
 	ObjectTypeDB::bind_method(_MD("get_instance_ID"),&Object::get_instance_ID);
 

+ 2 - 0
core/object.h

@@ -54,6 +54,7 @@ enum PropertyHint {
 	PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
 	PROPERTY_HINT_EXP_EASING, /// exponential easing funciton (Math::ease)
 	PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
+	PROPERTY_HINT_SPRITE_FRAME,
 	PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
 	PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
 	PROPERTY_HINT_ALL_FLAGS,
@@ -448,6 +449,7 @@ protected:
 
 	DVector<String> _get_meta_list_bind() const;
 	Array _get_property_list_bind() const;
+	Array _get_method_list_bind() const;
 
 public: //should be protected, but bug in clang++
 	static void initialize_type();

+ 21 - 6
core/os/main_loop.cpp

@@ -31,7 +31,20 @@
 
 void MainLoop::_bind_methods() {
 
-	ObjectTypeDB::bind_method("input_event",&MainLoop::input_event);
+	ObjectTypeDB::bind_method(_MD("input_event","ev"),&MainLoop::input_event);
+	ObjectTypeDB::bind_method(_MD("input_text","text"),&MainLoop::input_text);
+	ObjectTypeDB::bind_method(_MD("init"),&MainLoop::init);
+	ObjectTypeDB::bind_method(_MD("iteration","delta"),&MainLoop::iteration);
+	ObjectTypeDB::bind_method(_MD("idle","delta"),&MainLoop::idle);
+	ObjectTypeDB::bind_method(_MD("finish"),&MainLoop::finish);
+
+	BIND_VMETHOD( MethodInfo("_input_event",PropertyInfo(Variant::INPUT_EVENT,"ev")) );
+	BIND_VMETHOD( MethodInfo("_input_text",PropertyInfo(Variant::STRING,"text")) );
+	BIND_VMETHOD( MethodInfo("_initialize") );
+	BIND_VMETHOD( MethodInfo("_iteration",PropertyInfo(Variant::REAL,"delta")) );
+	BIND_VMETHOD( MethodInfo("_idle",PropertyInfo(Variant::REAL,"delta")) );
+	BIND_VMETHOD( MethodInfo("_finalize") );
+
 
 	BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN);
 	BIND_CONSTANT(NOTIFICATION_WM_FOCUS_OUT);
@@ -58,13 +71,15 @@ MainLoop::~MainLoop()
 
 void MainLoop::input_text( const String& p_text ) {
 
+	if (get_script_instance())
+		get_script_instance()->call("_input_text",p_text);
 
 }
 
 void MainLoop::input_event( const InputEvent& p_event ) {
 
 	if (get_script_instance())
-		get_script_instance()->call("input_event",p_event);
+		get_script_instance()->call("_input_event",p_event);
 
 }
 
@@ -74,13 +89,13 @@ void MainLoop::init() {
 		set_script(init_script.get_ref_ptr());
 
 	if (get_script_instance())
-		get_script_instance()->call("init");
+		get_script_instance()->call("_initialize");
 
 }
 bool MainLoop::iteration(float p_time) {
 
 	if (get_script_instance())
-		return get_script_instance()->call("iteration",p_time);
+		return get_script_instance()->call("_iteration",p_time);
 
 	return false;
 
@@ -88,14 +103,14 @@ bool MainLoop::iteration(float p_time) {
 bool MainLoop::idle(float p_time) {
 
 	if (get_script_instance())
-		return get_script_instance()->call("idle",p_time);
+		return get_script_instance()->call("_idle",p_time);
 
 	return false;
 }
 void MainLoop::finish() {
 
 	if (get_script_instance()) {
-		get_script_instance()->call("finish");
+		get_script_instance()->call("_finalize");
 		set_script(RefPtr()); //clear script
 	}
 

+ 1 - 1
scene/2d/animated_sprite.cpp

@@ -329,7 +329,7 @@ void AnimatedSprite::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("frame_changed"));
 
 	ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "frames",PROPERTY_HINT_RESOURCE_TYPE,"SpriteFrames"), _SCS("set_sprite_frames"),_SCS("get_sprite_frames"));
-	ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame"));
+	ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "frame",PROPERTY_HINT_SPRITE_FRAME), _SCS("set_frame"),_SCS("get_frame"));
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "centered"), _SCS("set_centered"),_SCS("is_centered"));
 	ADD_PROPERTYNZ( PropertyInfo( Variant::VECTOR2, "offset"), _SCS("set_offset"),_SCS("get_offset"));
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_h"), _SCS("set_flip_h"),_SCS("is_flipped_h"));

+ 1 - 1
scene/2d/sprite.cpp

@@ -320,7 +320,7 @@ void Sprite::_bind_methods() {
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_v"), _SCS("set_flip_v"),_SCS("is_flipped_v"));
 	ADD_PROPERTY( PropertyInfo( Variant::INT, "vframes"), _SCS("set_vframes"),_SCS("get_vframes"));
 	ADD_PROPERTY( PropertyInfo( Variant::INT, "hframes"), _SCS("set_hframes"),_SCS("get_hframes"));
-	ADD_PROPERTY( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame"));
+	ADD_PROPERTY( PropertyInfo( Variant::INT, "frame",PROPERTY_HINT_SPRITE_FRAME), _SCS("set_frame"),_SCS("get_frame"));
 	ADD_PROPERTY( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate"));
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "region"), _SCS("set_region"),_SCS("is_region"));
 	ADD_PROPERTY( PropertyInfo( Variant::RECT2, "region_rect"), _SCS("set_region_rect"),_SCS("get_region_rect"));

+ 2 - 2
scene/3d/sprite_3d.cpp

@@ -580,7 +580,7 @@ void Sprite3D::_bind_methods() {
 	ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_texture"),_SCS("get_texture"));
 	ADD_PROPERTY( PropertyInfo( Variant::INT, "vframes"), _SCS("set_vframes"),_SCS("get_vframes"));
 	ADD_PROPERTY( PropertyInfo( Variant::INT, "hframes"), _SCS("set_hframes"),_SCS("get_hframes"));
-	ADD_PROPERTY( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame"));
+	ADD_PROPERTY( PropertyInfo( Variant::INT, "frame",PROPERTY_HINT_SPRITE_FRAME), _SCS("set_frame"),_SCS("get_frame"));
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "region"), _SCS("set_region"),_SCS("is_region"));
 	ADD_PROPERTY( PropertyInfo( Variant::RECT2, "region_rect"), _SCS("set_region_rect"),_SCS("get_region_rect"));
 
@@ -727,7 +727,7 @@ void AnimatedSprite3D::_bind_methods(){
 	ObjectTypeDB::bind_method(_MD("get_frame"),&AnimatedSprite3D::get_frame);
 
 	ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE,"SpriteFrames"), _SCS("set_sprite_frames"),_SCS("get_sprite_frames"));
-	ADD_PROPERTY( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame"));
+	ADD_PROPERTY( PropertyInfo( Variant::INT, "frame",PROPERTY_HINT_SPRITE_FRAME), _SCS("set_frame"),_SCS("get_frame"));
 
 	ADD_SIGNAL(MethodInfo("frame_changed"));
 

+ 6 - 4
scene/animation/animation_player.cpp

@@ -967,14 +967,16 @@ String AnimationPlayer::get_current_animation() const {
 
 }
 
-void AnimationPlayer::stop() {
+void AnimationPlayer::stop(bool p_reset) {
 	
 	Playback &c=playback;
 	c.blend.clear();
-	c.current.from=NULL;
+	if (p_reset) {
+		c.current.from=NULL;
+	}
 	_set_process(false);
 	queued.clear();
-    playing = false;
+	playing = false;
 }
 
 void AnimationPlayer::stop_all() {
@@ -1211,7 +1213,7 @@ void AnimationPlayer::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_default_blend_time"),&AnimationPlayer::get_default_blend_time);
 
 	ObjectTypeDB::bind_method(_MD("play","name","custom_blend","custom_speed","from_end"),&AnimationPlayer::play,DEFVAL(""),DEFVAL(-1),DEFVAL(1.0),DEFVAL(false));
-	ObjectTypeDB::bind_method(_MD("stop"),&AnimationPlayer::stop);
+	ObjectTypeDB::bind_method(_MD("stop","reset"),&AnimationPlayer::stop,DEFVAL(true));
 	ObjectTypeDB::bind_method(_MD("stop_all"),&AnimationPlayer::stop_all);
 	ObjectTypeDB::bind_method(_MD("is_playing"),&AnimationPlayer::is_playing);
 	ObjectTypeDB::bind_method(_MD("set_current_animation","anim"),&AnimationPlayer::set_current_animation);

+ 1 - 1
scene/animation/animation_player.h

@@ -260,7 +260,7 @@ public:
 	void play(const StringName& p_name=StringName(),float p_custom_blend=-1,float p_custom_scale=1.0,bool p_from_end=false);
 	void queue(const StringName& p_name);
 	void clear_queue();
-	void stop();
+	void stop(bool p_reset=true);
 	bool is_playing() const;
 	String get_current_animation() const;
 	void set_current_animation(const String& p_anim);

+ 467 - 58
tools/editor/animation_editor.cpp

@@ -44,11 +44,207 @@
 */
 
 
+class AnimationCurveEdit : public Control {
+	OBJ_TYPE( AnimationCurveEdit, Control );
+public:
+	enum Mode {
+		MODE_DISABLED,
+		MODE_SINGLE,
+		MODE_MULTIPLE
+	};
+private:
+
+	Set<float> multiples;
+	float transition;
+	Mode mode;
+
+	void _notification(int p_what) {
+
+		if (p_what==NOTIFICATION_DRAW) {
+
+
+			RID ci = get_canvas_item();
+
+			Size2 s = get_size();
+			Rect2 r(Point2(),s);
+
+			//r=r.grow(3);
+			Ref<StyleBox> sb = get_stylebox("normal","LineEdit");
+			sb->draw(ci,r);
+			r.size-=sb->get_minimum_size();
+			r.pos+=sb->get_offset();
+			//VisualServer::get_singleton()->canvas_item_add
+
+			Ref<Font> f = get_font("font","Label");
+			r=r.grow(-2);
+			Color color = get_color("font_color","Label");
+
+			int points = 48;
+			if (mode==MODE_MULTIPLE) {
+
+				int max_draw = 16;
+				Color mcolor=color;
+				mcolor.a*=0.3;
+
+				Set<float>::Element *E=multiples.front();
+				for(int j=0;j<16;j++) {
+
+					if (!E)
+						break;
+
+					float prev=1.0;
+					float exp=E->get();
+					bool flip=false;//hint_text=="attenuation";
+
+
+					for(int i=1;i<=points;i++) {
+
+						float ifl = i/float(points);
+						float iflp = (i-1)/float(points);
+
+						float h = 1.0-Math::ease(ifl,exp);
+
+						if (flip) {
+							ifl=1.0-ifl;
+							iflp=1.0-iflp;
+						}
+
+						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);
+						prev=h;
+					}
+
+					E=E->next();
+				}
+			}
+
+			float exp=transition;
+			if (mode!=MODE_DISABLED) {
+
+
+				float prev=1.0;
+
+				bool flip=false;//hint_text=="attenuation";
+
+
+				for(int i=1;i<=points;i++) {
+
+					float ifl = i/float(points);
+					float iflp = (i-1)/float(points);
+
+					float h = 1.0-Math::ease(ifl,exp);
+
+					if (flip) {
+						ifl=1.0-ifl;
+						iflp=1.0-iflp;
+					}
+
+					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);
+					prev=h;
+				}
+			}
+
+			String txt=String::num(exp,2);
+			if (mode==MODE_DISABLED) {
+				txt="Disabled";
+			} else if (mode==MODE_MULTIPLE) {
+				txt+=" - All Selection";
+			}
+
+			f->draw(ci,Point2(10,10+f->get_ascent()),txt,color);
+
+		}
+	}
+
+	void _input_event(const InputEvent& p_ev) {
+		if (p_ev.type==InputEvent::MOUSE_MOTION && p_ev.mouse_motion.button_mask&BUTTON_MASK_LEFT) {
+
+			if (mode==MODE_DISABLED)
+				return;
+
+			float rel = p_ev.mouse_motion.relative_x;
+			if (rel==0)
+			    return;
+
+			bool flip=false;
+
+			if (flip)
+				rel=-rel;
+
+			float val = transition;
+			if (val==0)
+				return;
+			bool sg = val < 0;
+			val = Math::absf(val);
+
+			val = Math::log(val)/Math::log(2);
+			//logspace
+			val+=rel*0.05;
+			//
+
+			val = Math::pow(2,val);
+			if (sg)
+				val=-val;
+
+			transition=val;
+			update();
+			//emit_signal("variant_changed");
+			emit_signal("transition_changed",transition);
+		}
+	}
+
+public:
+
+	static void _bind_methods() {
+
+	//	ObjectTypeDB::bind_method("_update_obj",&AnimationKeyEdit::_update_obj);
+		ObjectTypeDB::bind_method("_input_event",&AnimationCurveEdit::_input_event);
+		ADD_SIGNAL(MethodInfo("transition_changed"));
+	}
+
+	void set_mode(Mode p_mode) {
+
+		mode=p_mode;
+		update();
+	}
+
+	void clear_multiples() { multiples.clear(); update();}
+	void set_multiple(float p_transition) {
+
+		multiples.insert(p_transition);
+	}
+
+	void set_transition(float p_transition) {
+		transition=p_transition;
+		update();
+	}
+
+	float get_transition() const {
+		return transition;
+	}
+
+	void force_transition(float p_value) {
+		if (mode==MODE_DISABLED)
+			return;
+		transition=p_value;
+		emit_signal("transition_changed",p_value);
+		update();
+	}
+
+	AnimationCurveEdit() {
+
+		transition=1.0;
+		set_default_cursor_shape(CURSOR_HSPLIT);
+		mode=MODE_DISABLED;
+	}
+
+};
+
 class AnimationKeyEdit : public Object {
 
 	OBJ_TYPE(AnimationKeyEdit,Object);
 public:
 	bool setting;
+	bool hidden;
 
 	static void _bind_methods() {
 
@@ -56,12 +252,12 @@ public:
 		ObjectTypeDB::bind_method("_key_ofs_changed",&AnimationKeyEdit::_key_ofs_changed);
 	}
 
-	PopupDialog *ke_dialog;
+	//PopupDialog *ke_dialog;
 
 	void _update_obj(const Ref<Animation> &p_anim) {
 		if (setting)
 			return;
-		if (!ke_dialog->is_visible())
+		if (hidden)
 			return;
 		if (!(animation==p_anim))
 			return;
@@ -69,7 +265,7 @@ public:
 	}
 
 	void _key_ofs_changed(const Ref<Animation> &p_anim,float from, float to) {
-		if (!ke_dialog->is_visible())
+		if (hidden)
 			return;
 		if (!(animation==p_anim))
 			return;
@@ -408,8 +604,8 @@ public:
 			} break;
 		}
 
-		if (animation->track_get_type(track)!=Animation::TYPE_METHOD)
-			p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
+		//if (animation->track_get_type(track)!=Animation::TYPE_METHOD)
+		//	p_list->push_back( PropertyInfo( Variant::REAL, "easing", PROPERTY_HINT_EXP_EASING));
 	}
 
 	UndoRedo *undo_redo;
@@ -425,7 +621,7 @@ public:
 		_change_notify();
 	}
 
-	AnimationKeyEdit() { key_ofs=0; track=-1; setting=false; }
+	AnimationKeyEdit() { hidden=true; key_ofs=0; track=-1; setting=false; }
 
 };
 
@@ -610,6 +806,8 @@ void AnimationKeyEditor::_menu_track(int p_type) {
 
 				selection=new_selection;
 				track_editor->update();
+				_edit_if_single_selection();
+
 
 			}
 
@@ -689,8 +887,31 @@ void AnimationKeyEditor::_menu_track(int p_type) {
 
 			optimize_dialog->popup_centered(Size2(250,180));
 		} break;
+		case CURVE_SET_LINEAR: {
+			curve_edit->force_transition(1.0);
 
+		} break;
+		case CURVE_SET_IN: {
 
+			curve_edit->force_transition(4.0);
+
+		} break;
+		case CURVE_SET_OUT: {
+
+			curve_edit->force_transition(0.25);
+		} break;
+		case CURVE_SET_INOUT: {
+			curve_edit->force_transition(-4);
+
+		} break;
+		case CURVE_SET_OUTIN: {
+
+			curve_edit->force_transition(-0.25);
+		} break;
+		case CURVE_SET_CONSTANT: {
+
+			curve_edit->force_transition(0);
+		} break;
 
 	}
 
@@ -774,6 +995,7 @@ void AnimationKeyEditor::_track_editor_draw() {
 		h_scroll->hide();
 		menu_track->set_disabled(true);
 		edit_button->set_disabled(true);
+		key_editor_tab->hide();
 		move_up_button->set_disabled(true);
 		move_down_button->set_disabled(true);
 		remove_button->set_disabled(true);
@@ -785,6 +1007,8 @@ void AnimationKeyEditor::_track_editor_draw() {
 	move_up_button->set_disabled(false);
 	move_down_button->set_disabled(false);
 	remove_button->set_disabled(false);
+	if (edit_button->is_pressed())
+		key_editor_tab->show();
 
 	te_drawing=true;
 
@@ -1000,8 +1224,13 @@ void AnimationKeyEditor::_track_editor_draw() {
 		}
 	}
 
+	Color sep_color=color;
+	color.a*=0.5;
+
 	for(int i=0;i<fit;i++) {
 
+		//this code sucks, i always forget how it works
+
 		int idx = v_scroll->get_val() + i;
 		if (idx>=animation->get_track_count())
 			break;
@@ -1090,6 +1319,7 @@ void AnimationKeyEditor::_track_editor_draw() {
 		float key_hofs = -Math::floor(type_icon[tt]->get_height()/2);
 
 		int kc=animation->track_get_key_count(idx);
+		bool first=true;
 
 		for(int i=0;i<kc;i++) {
 
@@ -1116,7 +1346,22 @@ void AnimationKeyEditor::_track_editor_draw() {
 			if (mouse_over.over==MouseOver::OVER_KEY && mouse_over.track==idx && mouse_over.over_key==i)
 				tex=type_hover;
 
+			Variant value = animation->track_get_key_value(idx,i);
+			if (first && i>0 && value==animation->track_get_key_value(idx,i-1)) {
+
+				te->draw_line(ofs+Vector2(name_limit,y+h/2),ofs+Point2(x,y+h/2),color);
+			}
+
+			if (i<kc-1 && value==animation->track_get_key_value(idx,i+1)) {
+				float x_n =  key_hofs + name_limit + (animation->track_get_key_time(idx,i+1)-keys_from)*zoom_scale;
+
+				x_n = MIN( x_n, settings_limit);
+				te->draw_line(ofs+Point2(x_n,y+h/2),ofs+Point2(x,y+h/2),color);
+
+			}
+
 			te->draw_texture(tex,ofs+Point2(x,y+key_vofs).floor());
+			first=false;
 		}
 
 	}
@@ -1226,7 +1471,9 @@ void AnimationKeyEditor::_clear_selection_for_anim(const Ref<Animation>& p_anim)
 
 	if (!(animation==p_anim))
 		return;
-	selection.clear();
+	//selection.clear();
+	_clear_selection();
+
 }
 
 void AnimationKeyEditor::_select_at_anim(const Ref<Animation>& p_anim,int p_track,float p_pos){
@@ -1244,6 +1491,7 @@ void AnimationKeyEditor::_select_at_anim(const Ref<Animation>& p_anim,int p_trac
 	ki.pos=p_pos;
 
 	selection.insert(sk,ki);
+
 }
 
 
@@ -1283,6 +1531,83 @@ PropertyInfo AnimationKeyEditor::_find_hint_for_track(int p_idx) {
 }
 
 
+void AnimationKeyEditor::_curve_transition_changed(float p_what) {
+
+	if (selection.size()==0)
+		return;
+	if (selection.size()==1)
+		undo_redo->create_action("Edit Node Curve",true);
+	else
+		undo_redo->create_action("Edit Selection Curve",true);
+
+	for(Map<SelectedKey,KeyInfo>::Element *E=selection.front();E;E=E->next()) {
+
+		int track = E->key().track;
+		int key = E->key().key;
+		float prev_val = animation->track_get_key_transition(track,key);
+		undo_redo->add_do_method(animation.ptr(),"track_set_key_transition",track,key,p_what);
+		undo_redo->add_undo_method(animation.ptr(),"track_set_key_transition",track,key,prev_val);
+	}
+
+	undo_redo->commit_action();
+
+}
+
+void AnimationKeyEditor::_toggle_edit_curves() {
+
+	if (edit_button->is_pressed())
+		key_editor_tab->show();
+	else
+		key_editor_tab->hide();
+}
+
+
+bool AnimationKeyEditor::_edit_if_single_selection() {
+
+	if (selection.size()!=1) {
+
+		if (selection.size()==0) {
+			curve_edit->set_mode(AnimationCurveEdit::MODE_DISABLED);
+			print_line("disable");
+		} else {
+
+			curve_edit->set_mode(AnimationCurveEdit::MODE_MULTIPLE);
+			curve_edit->set_transition(1.0);
+			curve_edit->clear_multiples();
+			//add all
+			for(Map<SelectedKey,KeyInfo>::Element *E=selection.front();E;E=E->next()) {
+
+				curve_edit->set_multiple(animation->track_get_key_transition(E->key().track,E->key().key));
+			}
+			print_line("multiple");
+
+		}
+		return false;
+	}
+	curve_edit->set_mode(AnimationCurveEdit::MODE_SINGLE);
+	print_line("regular");
+
+	int idx = selection.front()->key().track;
+	int key = selection.front()->key().key;
+	{
+
+		key_edit->animation=animation;
+		key_edit->track=idx;
+		key_edit->key_ofs=animation->track_get_key_time(idx,key);
+		key_edit->hint=_find_hint_for_track(idx);
+		key_edit->notify_change();
+
+		curve_edit->set_transition(animation->track_get_key_transition(idx,key));
+
+		/*key_edit_dialog->set_size( Size2( 200,200) );
+		key_edit_dialog->set_pos(  track_editor->get_global_pos() + ofs + mpos +Point2(-100,20));
+		key_edit_dialog->popup();*/
+
+	}
+
+	return true;
+
+}
 
 void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 
@@ -1364,9 +1689,12 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 						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));
 
 					}
+					undo_redo->add_do_method(this,"_clear_selection_for_anim",animation);
+					undo_redo->add_undo_method(this,"_clear_selection_for_anim",animation);
 					undo_redo->commit_action();
-					selection.clear();
+					//selection.clear();
 					accept_event();
+					_edit_if_single_selection();
 				}
 			} else if (animation.is_valid() && animation->get_track_count()>0) {
 
@@ -1552,20 +1880,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 
 						}
 
-						if (mb.mod.command || edit_button->is_pressed()) {
-
-							key_edit->animation=animation;
-							key_edit->track=idx;
-							key_edit->key_ofs=animation->track_get_key_time(idx,key);
-							key_edit->hint=_find_hint_for_track(idx);
-
-							key_edit->notify_change();							
 
-							key_edit_dialog->set_size( Size2( 200,200) );
-							key_edit_dialog->set_pos(  track_editor->get_global_pos() + ofs + mpos +Point2(-100,20));
-							key_edit_dialog->popup();
-
-						}
 
 						SelectedKey sk;
 						sk.track=idx;
@@ -1577,7 +1892,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 
 
 						if (!mb.mod.shift && !selection.has(sk))
-							selection.clear();
+							_clear_selection();
 
 						selection.insert(sk,ki);
 
@@ -1588,7 +1903,10 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 						selected_track=idx;
 						track_editor->update();
 
-
+						if (_edit_if_single_selection() && mb.mod.command) {
+							edit_button->set_pressed(true);
+							key_editor_tab->show();
+						}
 					} else {
 						//button column
 						int ofsx = size.width - mpos.x;
@@ -1824,7 +2142,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 
 							if (from_track>to_track) {
 								if (!click.shift)
-									selection.clear();
+									_clear_selection();
+								_edit_if_single_selection();
 								break;
 							}
 
@@ -1842,12 +2161,13 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 
 							if (from_track > tracks_to  || to_track < tracks_from) {
 								if (!click.shift)
-									selection.clear();
+									_clear_selection();
+								_edit_if_single_selection();
 								break;
 							}
 
 							if (!click.shift)
-								selection.clear();
+								_clear_selection();
 
 
 							int higher_track=0x7FFFFFFF;
@@ -1880,6 +2200,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 							}
 
 
+							_edit_if_single_selection();
+
 
 						} break;
 						case ClickOver::CLICK_MOVE_KEYS: {
@@ -1891,8 +2213,9 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 								if (!click.shift) {
 
 									KeyInfo ki=selection[click.selk];
-									selection.clear();
+									_clear_selection();
 									selection[click.selk]=ki;
+									_edit_if_single_selection();
 								}
 
 								break;
@@ -2007,6 +2330,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
 							}
 
 							undo_redo->commit_action();
+							_edit_if_single_selection();
 
 						} break;
 						default: {}
@@ -2348,20 +2672,33 @@ void AnimationKeyEditor::_notification(int p_what) {
 				optimize_dialog->connect("confirmed",this,"_animation_optimize");
 
 				menu_track->get_popup()->add_child(tpp);
-				menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
-				menu_track->get_popup()->add_separator();
+				//menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
+				//menu_track->get_popup()->add_separator();
 				menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE);
 
+				curve_linear->set_icon(get_icon("CurveLinear","EditorIcons"));
+				curve_in->set_icon(get_icon("CurveIn","EditorIcons"));
+				curve_out->set_icon(get_icon("CurveOut","EditorIcons"));
+				curve_inout->set_icon(get_icon("CurveInOut","EditorIcons"));
+				curve_outin->set_icon(get_icon("CurveOutIn","EditorIcons"));
+				curve_constant->set_icon(get_icon("CurveConstant","EditorIcons"));
 
-
+				curve_linear->connect("pressed",this,"_menu_track",varray(CURVE_SET_LINEAR));
+				curve_in->connect("pressed",this,"_menu_track",varray(CURVE_SET_IN));
+				curve_out->connect("pressed",this,"_menu_track",varray(CURVE_SET_OUT));
+				curve_inout->connect("pressed",this,"_menu_track",varray(CURVE_SET_INOUT));
+				curve_outin->connect("pressed",this,"_menu_track",varray(CURVE_SET_OUTIN));
+				curve_constant->connect("pressed",this,"_menu_track",varray(CURVE_SET_CONSTANT));
 
 
 				move_up_button->set_icon(get_icon("MoveUp","EditorIcons"));
 				move_down_button->set_icon(get_icon("MoveDown","EditorIcons"));
 				remove_button->set_icon(get_icon("Remove","EditorIcons"));
 				edit_button->set_icon(get_icon("EditKey","EditorIcons"));
+				edit_button->connect("pressed",this,"_toggle_edit_curves");
 
 				loop->set_icon(get_icon("Loop","EditorIcons"));
+				curve_edit->connect("transition_changed",this,"_curve_transition_changed");
 
 				//edit_button->add_color_override("font_color",get_color("font_color","Tree"));
 				//edit_button->add_color_override("font_color_hover",get_color("font_color","Tree"));
@@ -2456,6 +2793,17 @@ void AnimationKeyEditor::_update_menu() {
 	updating=false;
 
 }
+void AnimationKeyEditor::_clear_selection() {
+
+	selection.clear();
+	key_edit->animation=Ref<Animation>();
+	key_edit->track=0;
+	key_edit->key_ofs=0;
+	key_edit->hint=PropertyInfo();
+	key_edit->notify_change();
+
+}
+
 
 void AnimationKeyEditor::set_animation(const Ref<Animation>& p_anim) {
 
@@ -2466,11 +2814,12 @@ void AnimationKeyEditor::set_animation(const Ref<Animation>& p_anim) {
 		animation->connect("changed",this,"_update_paths");
 
 	timeline_pos=0;
-	selection.clear();
+	_clear_selection();
 	_update_paths();
 
 	_update_menu();
 	selected_track=-1;
+	_edit_if_single_selection();
 }
 
 void AnimationKeyEditor::set_root(Node *p_root) {
@@ -2591,6 +2940,7 @@ void AnimationKeyEditor::insert_transform_key(Spatial *p_node,const String& p_su
 	id.value=p_xform;
 	id.type=Animation::TYPE_TRANSFORM;
 	id.query="node '"+p_node->get_name()+"'";
+	id.advance=false;
 
 	//dialog insert
 
@@ -2643,6 +2993,7 @@ void AnimationKeyEditor::insert_node_value_key(Node* p_node, const String& p_pro
 	id.value=p_value;
 	id.type=Animation::TYPE_VALUE;
 	id.query="property '"+p_property+"'";
+	id.advance=false;
 	//dialog insert
 	_query_insert(id);
 
@@ -2650,7 +3001,7 @@ void AnimationKeyEditor::insert_node_value_key(Node* p_node, const String& p_pro
 
 }
 
-void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant& p_value) {
+void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant& p_value,bool p_advance) {
 
 	ERR_FAIL_COND(!root);
 	//let's build a node path
@@ -2696,6 +3047,7 @@ void AnimationKeyEditor::insert_value_key(const String& p_property,const Variant
 	id.value=p_value;
 	id.type=Animation::TYPE_VALUE;
 	id.query="property '"+p_property+"'";
+	id.advance=p_advance;
 	//dialog insert
 	_query_insert(id);
 
@@ -2928,13 +3280,31 @@ void AnimationKeyEditor::_insert_delay() {
 	undo_redo->create_action("Anim  Insert");
 
 	int last_track = animation->get_track_count();
+	bool advance=false;
 	while(insert_data.size()) {
 
+		if (insert_data.front()->get().advance)
+			advance=true;
 		last_track=_confirm_insert(insert_data.front()->get(),last_track);
 		insert_data.pop_front();
 	}
 
 	undo_redo->commit_action();
+
+	if (advance) {
+		float step = animation->get_step();
+		if (step==0)
+			step=1;
+
+		float pos=timeline_pos;
+
+		pos=Math::stepify(pos+step,step);
+		if (pos>animation->get_length())
+			pos=animation->get_length();
+		timeline_pos=pos;
+		track_pos->update();
+		emit_signal("timeline_changed",pos);
+	}
 	insert_queue=false;
 }
 
@@ -3122,12 +3492,15 @@ void AnimationKeyEditor::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("set_animation"),&AnimationKeyEditor::set_animation);
 	ObjectTypeDB::bind_method(_MD("_animation_optimize"),&AnimationKeyEditor::_animation_optimize);
+	ObjectTypeDB::bind_method(_MD("_curve_transition_changed"),&AnimationKeyEditor::_curve_transition_changed);
+	ObjectTypeDB::bind_method(_MD("_toggle_edit_curves"),&AnimationKeyEditor::_toggle_edit_curves);
 
 
 	ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) );
 	ADD_SIGNAL( MethodInfo("keying_changed" ) );
 	ADD_SIGNAL( MethodInfo("timeline_changed", PropertyInfo(Variant::REAL,"pos") ) );
 	ADD_SIGNAL( MethodInfo("animation_len_changed", PropertyInfo(Variant::REAL,"len") ) );
+	ADD_SIGNAL( MethodInfo("key_edited", PropertyInfo(Variant::INT,"track"), PropertyInfo(Variant::INT,"key") ) );
 
 }
 
@@ -3219,12 +3592,6 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 
 	hb->add_child( memnew( VSeparator ) );
 
-	edit_button = memnew( ToolButton );
-	edit_button->set_toggle_mode(true);
-	edit_button->set_focus_mode(FOCUS_NONE);
-	edit_button->set_disabled(true);
-	hb->add_child(edit_button);
-	edit_button->set_tooltip("Enable editing of individual keys by clicking them.");
 
 	move_up_button = memnew( ToolButton );
 	hb->add_child(move_up_button);
@@ -3247,6 +3614,15 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 	remove_button->set_disabled(true);
 	remove_button->set_tooltip("Remove selected track.");
 
+	hb->add_child(memnew( VSeparator ));
+
+	edit_button = memnew( ToolButton );
+	edit_button->set_toggle_mode(true);
+	edit_button->set_focus_mode(FOCUS_NONE);
+	edit_button->set_disabled(true);
+
+	hb->add_child(edit_button);
+	edit_button->set_tooltip("Enable editing of individual keys by clicking them.");
 
 	optimize_dialog = memnew( ConfirmationDialog );
 	add_child(optimize_dialog);
@@ -3297,7 +3673,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 //	menu->get_popup()->connect("item_pressed",this,"_menu_callback");
 
 	ec = memnew (Control);
-	ec->set_custom_minimum_size(Size2(0,50));
+	ec->set_custom_minimum_size(Size2(0,150));
 	add_child(ec);
 	ec->set_v_size_flags(SIZE_EXPAND_FILL);
 
@@ -3313,6 +3689,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 	track_editor->set_focus_mode(Control::FOCUS_ALL);
 	track_editor->set_h_size_flags(SIZE_EXPAND_FILL);
 
+
 	track_pos = memnew( Control );
 	track_pos->set_area_as_parent_rect();
 	track_pos->set_ignore_mouse(true);
@@ -3325,6 +3702,56 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 	v_scroll->connect("value_changed",this,"_scroll_changed");
 	v_scroll->set_val(0);
 
+	key_editor_tab = memnew(TabContainer);
+	hb->add_child(key_editor_tab);
+	key_editor_tab->set_custom_minimum_size(Size2(200,0));
+
+	key_editor = memnew( PropertyEditor );
+	key_editor->set_area_as_parent_rect();
+	key_editor->hide_top_label();
+	key_editor->set_name("Key");
+	key_editor_tab->add_child(key_editor);
+
+	key_edit = memnew( AnimationKeyEdit );
+	key_edit->undo_redo=undo_redo;
+	//key_edit->ke_dialog=key_edit_dialog;
+	key_editor->edit(key_edit);
+	type_menu = memnew( PopupMenu );
+	add_child(type_menu);
+	for(int i=0;i<Variant::VARIANT_MAX;i++)
+		type_menu->add_item(Variant::get_type_name(Variant::Type(i)),i);
+	type_menu->connect("item_pressed",this,"_create_value_item");
+
+	VBoxContainer *curve_vb = memnew( VBoxContainer );
+	curve_vb->set_name("Transition");
+	HBoxContainer *curve_hb = memnew( HBoxContainer );
+	curve_vb->add_child(curve_hb);
+
+	curve_linear = memnew( ToolButton );
+	curve_linear->set_focus_mode(FOCUS_NONE);
+	curve_hb->add_child(curve_linear);
+	curve_in = memnew( ToolButton );
+	curve_in->set_focus_mode(FOCUS_NONE);
+	curve_hb->add_child(curve_in);
+	curve_out = memnew( ToolButton );
+	curve_out->set_focus_mode(FOCUS_NONE);
+	curve_hb->add_child(curve_out);
+	curve_inout = memnew( ToolButton );
+	curve_inout->set_focus_mode(FOCUS_NONE);
+	curve_hb->add_child(curve_inout);
+	curve_outin = memnew( ToolButton );
+	curve_outin->set_focus_mode(FOCUS_NONE);
+	curve_hb->add_child(curve_outin);
+	curve_constant = memnew( ToolButton );
+	curve_constant->set_focus_mode(FOCUS_NONE);
+	curve_hb->add_child(curve_constant);
+
+
+	curve_edit = memnew( AnimationCurveEdit );
+	curve_vb->add_child(curve_edit);
+	curve_edit->set_v_size_flags(SIZE_EXPAND_FILL);
+	key_editor_tab->add_child(curve_vb);
+
 	h_scroll = memnew( HScrollBar );
 	h_scroll->connect("value_changed",this,"_scroll_changed");
 	add_child(h_scroll);
@@ -3340,7 +3767,7 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 	add_child(track_menu);
 	track_menu->connect("item_pressed",this,"_track_menu_selected");
 
-
+	key_editor_tab->hide();
 
 	last_idx =1;
 
@@ -3359,24 +3786,6 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 	timeline_pos=0;
 
 
-	key_edit_dialog = memnew( PopupDialog );
-	key_editor = memnew( PropertyEditor );
-	add_child(key_edit_dialog);
-	key_editor->set_area_as_parent_rect();
-	key_editor->hide_top_label();
-	for(int i=0;i<4;i++)
-		key_editor->set_margin(Margin(i),5);
-	key_edit_dialog->add_child(key_editor);
-
-	key_edit = memnew( AnimationKeyEdit );
-	key_edit->undo_redo=undo_redo;
-	key_edit->ke_dialog=key_edit_dialog;
-	key_editor->edit(key_edit);
-	type_menu = memnew( PopupMenu );
-	add_child(type_menu);
-	for(int i=0;i<Variant::VARIANT_MAX;i++)
-		type_menu->add_item(Variant::get_type_name(Variant::Type(i)),i);
-	type_menu->connect("item_pressed",this,"_create_value_item");
 	keying=false;
 	insert_frame=0;
 	insert_query=false;

+ 25 - 4
tools/editor/animation_editor.h

@@ -37,6 +37,7 @@
 #include "scene/gui/scroll_bar.h"
 #include "scene/gui/tool_button.h"
 #include "scene/gui/file_dialog.h"
+#include "scene/gui/tab_container.h"
 
 #include "scene/resources/animation.h"
 #include "scene/animation/animation_cache.h"
@@ -46,6 +47,7 @@
 
 
 class AnimationKeyEdit;
+class AnimationCurveEdit;
 
 class AnimationKeyEditor : public VBoxContainer  {
 
@@ -86,7 +88,13 @@ class AnimationKeyEditor : public VBoxContainer  {
 		TRACK_MENU_SET_ALL_TRANS_OUTIN,
 		TRACK_MENU_NEXT_STEP,
 		TRACK_MENU_PREV_STEP,
-		TRACK_MENU_OPTIMIZE
+		TRACK_MENU_OPTIMIZE,
+		CURVE_SET_LINEAR,
+		CURVE_SET_IN,
+		CURVE_SET_OUT,
+		CURVE_SET_INOUT,
+		CURVE_SET_OUTIN,
+		CURVE_SET_CONSTANT
 	};
 
 	struct MouseOver {
@@ -169,6 +177,14 @@ class AnimationKeyEditor : public VBoxContainer  {
 	ToolButton *move_down_button;
 	ToolButton *remove_button;
 
+	ToolButton *curve_linear;
+	ToolButton *curve_in;
+	ToolButton *curve_out;
+	ToolButton *curve_inout;
+	ToolButton *curve_outin;
+	ToolButton *curve_constant;
+
+
 	ConfirmationDialog *optimize_dialog;
 	SpinBox *optimize_linear_error;
 	SpinBox *optimize_angular_error;
@@ -183,11 +199,11 @@ class AnimationKeyEditor : public VBoxContainer  {
 
 	Control *track_editor;
 	Control *track_pos;
+	TabContainer *key_editor_tab;
 
 	ConfirmationDialog *scale_dialog;
 	SpinBox *scale;
 
-	PopupDialog *key_edit_dialog;
 	PropertyEditor *key_editor;	
 
 	Ref<Animation> animation;
@@ -203,6 +219,7 @@ class AnimationKeyEditor : public VBoxContainer  {
 
 
 	AnimationKeyEdit *key_edit;
+	AnimationCurveEdit *curve_edit;
 
 	bool inserting;
 
@@ -220,6 +237,7 @@ class AnimationKeyEditor : public VBoxContainer  {
 		int track_idx;
 		Variant value;
 		String query;
+		bool advance;
 	};/* insert_data;*/
 
 	bool insert_query;
@@ -254,7 +272,7 @@ class AnimationKeyEditor : public VBoxContainer  {
 	void _scale();
 
 
-
+	void _clear_selection();
 
 	//void _browse_path();
 
@@ -270,12 +288,15 @@ class AnimationKeyEditor : public VBoxContainer  {
 
 	void _clear_selection_for_anim(const Ref<Animation>& p_anim);
 	void _select_at_anim(const Ref<Animation>& p_anim,int p_track,float p_pos);
+	void _curve_transition_changed(float p_what);
 
 	PropertyInfo _find_hint_for_track(int p_idx);
 
 	void _create_value_item(int p_type);
 	void _pane_drag(const Point2& p_delta);
+	bool _edit_if_single_selection();
 
+	void _toggle_edit_curves();
 	void _animation_len_update();
 
 	void _root_removed();
@@ -296,7 +317,7 @@ public:
 
 	void set_anim_pos(float p_pos);
 	void insert_node_value_key(Node* p_node, const String& p_property,const Variant& p_value,bool p_only_if_exists=false);
-	void insert_value_key(const String& p_property,const Variant& p_value);
+	void insert_value_key(const String& p_property, const Variant& p_value, bool p_advance);
 	void insert_transform_key(Spatial *p_node,const String& p_sub,const Transform& p_xform);
 
 	AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_history, EditorSelection *p_selection);

+ 1 - 0
tools/editor/editor_file_system.cpp

@@ -989,6 +989,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_path(const String& p_path) {
 
 void EditorFileSystem::_resource_saved(const String& p_path){
 
+	print_line("resource saved: "+p_path);
 	EditorFileSystem::get_singleton()->update_file(p_path);
 }
 

+ 2 - 2
tools/editor/editor_node.cpp

@@ -2838,9 +2838,9 @@ void EditorNode::_instance_request(const String& p_path){
 	request_instance_scene(p_path);
 }
 
-void EditorNode::_property_keyed(const String& p_keyed,const Variant& p_value) {
+void EditorNode::_property_keyed(const String& p_keyed,const Variant& p_value,bool p_advance) {
 
-	animation_editor->insert_value_key(p_keyed,p_value);
+	animation_editor->insert_value_key(p_keyed,p_value,p_advance);
 }
 
 void EditorNode::_transform_keyed(Object *sp,const String& p_sub,const Transform& p_key) {

+ 1 - 1
tools/editor/editor_node.h

@@ -348,7 +348,7 @@ class EditorNode : public Node {
 
 	void _instance_request(const String& p_path);
 
-	void _property_keyed(const String& p_keyed,const Variant& p_value);
+	void _property_keyed(const String& p_keyed, const Variant& p_value, bool p_advance);
 	void _transform_keyed(Object *sp,const String& p_sub,const Transform& p_key);
 
 	void _update_keying();

BIN
tools/editor/icons/icon_curve_constant.png


BIN
tools/editor/icons/icon_curve_in.png


BIN
tools/editor/icons/icon_curve_in_out.png


BIN
tools/editor/icons/icon_curve_linear.png


BIN
tools/editor/icons/icon_curve_out.png


BIN
tools/editor/icons/icon_curve_out_in.png


BIN
tools/editor/icons/icon_key_next.png


BIN
tools/editor/icons/icon_pause.png


BIN
tools/editor/icons/icon_play.png


BIN
tools/editor/icons/icon_play_backwards.png


BIN
tools/editor/icons/icon_play_start.png


BIN
tools/editor/icons/icon_play_start_backwards.png


BIN
tools/editor/icons/icon_stop.png


+ 235 - 24
tools/editor/plugins/animation_player_editor_plugin.cpp

@@ -28,7 +28,8 @@
 /*************************************************************************/
 #include "animation_player_editor_plugin.h"
 #include "io/resource_loader.h"
-
+#include "os/keyboard.h"
+#include "tools/editor/editor_settings.h"
 
 void AnimationPlayerEditor::_node_removed(Node *p_node) {
 
@@ -100,12 +101,18 @@ void AnimationPlayerEditor::_notification(int p_what) {
 		remove_anim->set_icon( get_icon("Del","EditorIcons") );
 		edit_anim->set_icon( get_icon("Edit","EditorIcons") );
 		blend_anim->set_icon( get_icon("Blend","EditorIcons") );
-		play->set_icon( get_icon("Play","EditorIcons") );
+		play->set_icon( get_icon("PlayStart","EditorIcons") );
+		play_from->set_icon( get_icon("Play","EditorIcons") );
+		play_bw->set_icon( get_icon("PlayStartBackwards","EditorIcons") );
+		play_bw_from->set_icon( get_icon("PlayBackwards","EditorIcons") );
+
 		autoplay_icon=get_icon("AutoPlay","EditorIcons");
 		stop->set_icon( get_icon("Stop","EditorIcons") );
 		resource_edit_anim->set_icon( get_icon("EditResource","EditorIcons") );
 		pin->set_normal_texture(get_icon("Pin","EditorIcons") );
 		pin->set_pressed_texture( get_icon("PinPressed","EditorIcons") );
+		tool_anim->set_icon(get_icon("Tools","EditorIcons"));
+		tool_anim->get_popup()->connect("item_pressed",this,"_animation_tool_menu");
 
 		blend_editor.next->connect("text_changed",this,"_blend_editor_next_changed");
 
@@ -180,9 +187,82 @@ void AnimationPlayerEditor::_play_pressed() {
 	//unpause
 	//pause->set_pressed(false);
 }
+
+void AnimationPlayerEditor::_play_from_pressed() {
+
+	String current;
+	if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) {
+
+		current = animation->get_item_text( animation->get_selected() );
+	}
+
+	if (current!="") {
+
+		float time = player->get_current_animation_pos();
+
+		if (current==player->get_current_animation() && player->is_playing()) {
+
+			player->stop(); //so it wont blend with itself
+		}
+
+		player->play( current );
+		player->seek(time);
+	}
+
+	//unstop
+	stop->set_pressed(false);
+	//unpause
+	//pause->set_pressed(false);
+}
+
+
+void AnimationPlayerEditor::_play_bw_pressed() {
+
+	String current;
+	if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) {
+
+		current = animation->get_item_text( animation->get_selected() );
+	}
+
+	if (current!="") {
+
+		if (current==player->get_current_animation())
+			player->stop(); //so it wont blend with itself
+		player->play(current,-1,-1,true);
+	}
+
+	//unstop
+	stop->set_pressed(false);
+	//unpause
+	//pause->set_pressed(false);
+}
+
+void AnimationPlayerEditor::_play_bw_from_pressed() {
+
+	String current;
+	if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) {
+
+		current = animation->get_item_text( animation->get_selected() );
+	}
+
+	if (current!="") {
+
+		float time = player->get_current_animation_pos();
+		if (current==player->get_current_animation())
+			player->stop(); //so it wont blend with itself
+
+		player->play(current,-1,-1,true);
+		player->seek(time);
+	}
+
+	//unstop
+	stop->set_pressed(false);
+	//unpause
+	//pause->set_pressed(false);
+}
 void AnimationPlayerEditor::_stop_pressed() {
 
-	player->stop();
+	player->stop(false);
 	play->set_pressed(false);
 	stop->set_pressed(true);
 	//pause->set_pressed(false);
@@ -598,6 +678,9 @@ void AnimationPlayerEditor::_update_player() {
 
 	stop->set_disabled(animlist.size()==0);
 	play->set_disabled(animlist.size()==0);
+	play_bw->set_disabled(animlist.size()==0);
+	play_bw_from->set_disabled(animlist.size()==0);
+	play_from->set_disabled(animlist.size()==0);
 	autoplay->set_disabled(animlist.size()==0);
 	duplicate_anim->set_disabled(animlist.size()==0);
 	rename_anim->set_disabled(animlist.size()==0);
@@ -877,11 +960,108 @@ void AnimationPlayerEditor::_hide_anim_editors() {
 	}
 }
 
+
+void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
+
+	switch(p_option) {
+
+		case TOOL_COPY_ANIM: {
+
+			if (!animation->get_item_count()) {
+				error_dialog->set_text("ERROR: No animation to copy!");
+				error_dialog->popup_centered_minsize();
+				return;
+			}
+
+			String current = animation->get_item_text(animation->get_selected());
+			Ref<Animation> anim =  player->get_animation(current);
+			//editor->edit_resource(anim);
+			EditorSettings::get_singleton()->set_resource_clipboard(anim);
+
+		} break;
+		case TOOL_PASTE_ANIM: {
+
+			Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard();
+			if (!anim.is_valid()) {
+				error_dialog->set_text("ERROR: No animation resource on clipboard!");
+				error_dialog->popup_centered_minsize();
+				return;
+			}
+
+			String name = anim->get_name();
+			if (name=="") {
+				name="Pasted Animation";
+			}
+
+			int idx=1;
+			String base = name;
+			while (player->has_animation(name)) {
+
+				idx++;
+				name=base+" "+itos(idx);
+			}
+
+			undo_redo->create_action("Paste Animation");
+			undo_redo->add_do_method(player,"add_animation",name,anim);
+			undo_redo->add_undo_method(player,"remove_animation",name);
+			undo_redo->add_do_method(this,"_animation_player_changed",player);
+			undo_redo->add_undo_method(this,"_animation_player_changed",player);
+			undo_redo->commit_action();
+
+			_select_anim_by_name(name);
+
+
+		} break;
+		case TOOL_EDIT_RESOURCE: {
+
+			if (!animation->get_item_count()) {
+				error_dialog->set_text("ERROR: No animation to edit!");
+				error_dialog->popup_centered_minsize();
+				return;
+			}
+
+			String current = animation->get_item_text(animation->get_selected());
+			Ref<Animation> anim =  player->get_animation(current);
+			editor->edit_resource(anim);
+
+		} break;
+
+	}
+}
+
+void AnimationPlayerEditor::_unhandled_key_input(const InputEvent& p_ev) {
+
+	if (is_visible() && p_ev.type==InputEvent::KEY && p_ev.key.pressed && !p_ev.key.echo && !p_ev.key.mod.alt && !p_ev.key.mod.control && !p_ev.key.mod.meta) {
+
+		switch(p_ev.key.scancode) {
+
+			case KEY_A: {
+				if (!p_ev.key.mod.shift)
+					_play_bw_from_pressed();
+				else
+					_play_bw_pressed();
+			} break;
+			case KEY_S: {
+				_stop_pressed();
+			} break;
+			case KEY_D: {
+				if (!p_ev.key.mod.shift)
+					_play_from_pressed();
+				else
+					_play_pressed();
+			} break;
+		}
+	}
+}
+
 void AnimationPlayerEditor::_bind_methods() {
 
 	ObjectTypeDB::bind_method(_MD("_input_event"),&AnimationPlayerEditor::_input_event);
 	ObjectTypeDB::bind_method(_MD("_node_removed"),&AnimationPlayerEditor::_node_removed);
 	ObjectTypeDB::bind_method(_MD("_play_pressed"),&AnimationPlayerEditor::_play_pressed);
+	ObjectTypeDB::bind_method(_MD("_play_from_pressed"),&AnimationPlayerEditor::_play_from_pressed);
+	ObjectTypeDB::bind_method(_MD("_play_bw_pressed"),&AnimationPlayerEditor::_play_bw_pressed);
+	ObjectTypeDB::bind_method(_MD("_play_bw_from_pressed"),&AnimationPlayerEditor::_play_bw_from_pressed);
 	ObjectTypeDB::bind_method(_MD("_stop_pressed"),&AnimationPlayerEditor::_stop_pressed);
 	ObjectTypeDB::bind_method(_MD("_autoplay_pressed"),&AnimationPlayerEditor::_autoplay_pressed);
 	ObjectTypeDB::bind_method(_MD("_pause_pressed"),&AnimationPlayerEditor::_pause_pressed);
@@ -908,6 +1088,8 @@ void AnimationPlayerEditor::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("_hide_anim_editors"),&AnimationPlayerEditor::_hide_anim_editors);
 	ObjectTypeDB::bind_method(_MD("_animation_duplicate"),&AnimationPlayerEditor::_animation_duplicate);
 	ObjectTypeDB::bind_method(_MD("_blend_editor_next_changed"),&AnimationPlayerEditor::_blend_editor_next_changed);
+	ObjectTypeDB::bind_method(_MD("_unhandled_key_input"),&AnimationPlayerEditor::_unhandled_key_input);
+	ObjectTypeDB::bind_method(_MD("_animation_tool_menu"),&AnimationPlayerEditor::_animation_tool_menu);
 
 
 
@@ -935,47 +1117,56 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
 	add_child(hb);
 
 
-	add_anim = memnew( Button );
+	add_anim = memnew( ToolButton );
 	add_anim->set_tooltip("Create new animation in player.");
 
 	hb->add_child(add_anim);
 
 
-
-	load_anim = memnew( Button );
+	load_anim = memnew( ToolButton );
 	load_anim->set_tooltip("Load an animation from disk.");
 	hb->add_child(load_anim);
 
-	duplicate_anim = memnew( Button );
+	duplicate_anim = memnew( ToolButton );
 	hb->add_child(duplicate_anim);
 	duplicate_anim->set_tooltip("Duplicate Animation");
 
+	rename_anim = memnew( ToolButton );
+	hb->add_child(rename_anim);
+	rename_anim->set_tooltip("Rename Animation");
+
+	remove_anim = memnew( ToolButton );
+
+	hb->add_child(remove_anim);
+	remove_anim->set_tooltip("Remove Animation");
+
+
 	animation = memnew( OptionButton );
 	hb->add_child(animation);
 	animation->set_h_size_flags(SIZE_EXPAND_FILL);
 	animation->set_tooltip("Display list of animations in player.");
 
-	autoplay = memnew( Button );
+	autoplay = memnew( ToolButton );
 	hb->add_child(autoplay);
 	autoplay->set_tooltip("Autoplay On Load");
 
 
-	rename_anim = memnew( Button );
-	hb->add_child(rename_anim);
-	rename_anim->set_tooltip("Rename Animation");
-
-	remove_anim = memnew( Button );
-
-	hb->add_child(remove_anim);
-	remove_anim->set_tooltip("Remove Animation");
 
-	blend_anim = memnew( Button );
+	blend_anim = memnew( ToolButton );
 	hb->add_child(blend_anim);
 	blend_anim->set_tooltip("Edit Target Blend Times");
 
+	tool_anim = memnew( MenuButton);
+	//tool_anim->set_flat(false);
+	tool_anim->set_tooltip("Animation Tools");
+	tool_anim->get_popup()->add_item("Copy Animation",TOOL_COPY_ANIM);
+	tool_anim->get_popup()->add_item("Paste Animation",TOOL_PASTE_ANIM);
+	//tool_anim->get_popup()->add_separator();
+	//tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM);
+	hb->add_child(tool_anim);
 
 
-	edit_anim = memnew( Button );
+	edit_anim = memnew( ToolButton );
 	edit_anim->set_toggle_mode(true);
 	hb->add_child(edit_anim);
 	edit_anim->set_tooltip("Open animation editor.\nProperty editor will displays all editable keys too.");
@@ -984,15 +1175,29 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
 	hb = memnew (HBoxContainer);
 	add_child(hb);
 
-	play = memnew( Button );
-	play->set_tooltip("Play selected animation.");
+	play_bw_from = memnew( ToolButton );
+	play_bw_from->set_tooltip("Play backwards selected animation from current pos. (A)");
+	hb->add_child(play_bw_from);
 
-	hb->add_child(play);
+	play_bw = memnew( ToolButton );
+	play_bw->set_tooltip("Play backwards selected animation from end. (Shift+A)");
+	hb->add_child(play_bw);
 
-	stop = memnew( Button );
+	stop = memnew( ToolButton );
 	stop->set_toggle_mode(true);
 	hb->add_child(stop);
-	play->set_tooltip("Stop animation playback.");
+	stop->set_tooltip("Stop animation playback. (S)");
+
+	play = memnew( ToolButton );
+	play->set_tooltip("Play selected animation from start. (Shift+D)");
+	hb->add_child(play);
+
+
+	play_from = memnew( ToolButton );
+	play_from->set_tooltip("Play selected animation from current pos. (D)");
+	hb->add_child(play_from);
+
+
 
 	//pause = memnew( Button );
 	//pause->set_toggle_mode(true);
@@ -1024,6 +1229,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
 
 	resource_edit_anim= memnew( Button );
 	hb->add_child(resource_edit_anim);
+	resource_edit_anim->hide();
 
 
 	file = memnew(FileDialog);
@@ -1074,7 +1280,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
 
 	autoplay->connect("pressed", this,"_autoplay_pressed");
 	autoplay->set_toggle_mode(true);
-	play->connect("pressed", this,"_play_pressed");	
+	play->connect("pressed", this,"_play_pressed");
+	play_from->connect("pressed", this,"_play_from_pressed");
+	play_bw->connect("pressed", this,"_play_bw_pressed");
+	play_bw_from->connect("pressed", this,"_play_bw_from_pressed");
 	stop->connect("pressed", this,"_stop_pressed");
 	//pause->connect("pressed", this,"_pause_pressed");
 	add_anim->connect("pressed", this,"_animation_new");
@@ -1104,6 +1313,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
 
 	renaming=false;
 	last_active=false;
+
+	set_process_unhandled_key_input(true);
 }
 
 

+ 15 - 0
tools/editor/plugins/animation_player_editor_plugin.h

@@ -49,10 +49,19 @@ class AnimationPlayerEditor : public VBoxContainer {
 	EditorNode *editor;
 	AnimationPlayer *player;
 
+	enum {
+		TOOL_COPY_ANIM,
+		TOOL_PASTE_ANIM,
+		TOOL_EDIT_RESOURCE
+	};
 
 	OptionButton *animation;
 	Button *stop;
 	Button *play;
+	Button *play_from;
+	Button *play_bw;
+	Button *play_bw_from;
+
 //	Button *pause;
 	Button *add_anim;
 	Button *autoplay;
@@ -63,6 +72,7 @@ class AnimationPlayerEditor : public VBoxContainer {
 	Button *load_anim;
 	Button *blend_anim;
 	Button *remove_anim;
+	MenuButton *tool_anim;
 	TextureButton *pin;
 	Label *nodename;
 	SpinBox *frame;
@@ -95,6 +105,9 @@ class AnimationPlayerEditor : public VBoxContainer {
 
 	void _select_anim_by_name(const String& p_anim);
 	void _play_pressed();
+	void _play_from_pressed();
+	void _play_bw_pressed();
+	void _play_bw_from_pressed();
 	void _autoplay_pressed();
 	void _stop_pressed();
 	void _pause_pressed();
@@ -126,6 +139,8 @@ class AnimationPlayerEditor : public VBoxContainer {
 
 	void _animation_key_editor_seek(float p_pos);
 	void _animation_key_editor_anim_len_changed(float p_new);
+	void _unhandled_key_input(const InputEvent& p_ev);
+	void _animation_tool_menu(int p_option);
 
 	AnimationPlayerEditor();
 protected:

+ 26 - 2
tools/editor/property_editor.cpp

@@ -2629,7 +2629,12 @@ void PropertyEditor::update_tree() {
 
 		if (keying) {
 
-			item->add_button(1,get_icon("Key","EditorIcons"),2);
+			if (p.hint==PROPERTY_HINT_SPRITE_FRAME) {
+
+				item->add_button(1,get_icon("KeyNext","EditorIcons"),5);
+			} else {
+				item->add_button(1,get_icon("Key","EditorIcons"),2);
+			}
 		}
 
 		if (get_instanced_node()) {
@@ -2904,6 +2909,16 @@ void PropertyEditor::edit(Object* p_object) {
 	
 }
 
+void PropertyEditor::_set_range_def(Object *p_item, String prop,float p_frame) {
+
+	TreeItem *ti = p_item->cast_to<TreeItem>();
+	ERR_FAIL_COND(!ti);
+
+	ti->call_deferred("set_range",1, p_frame);
+	obj->call_deferred("set",prop,p_frame);
+
+}
+
 void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
 	TreeItem *ti = p_item->cast_to<TreeItem>();
 	ERR_FAIL_COND(!ti);
@@ -2915,7 +2930,15 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
 		if (!d.has("name"))
 			return;
 		String prop=d["name"];
-		emit_signal("property_keyed",prop,obj->get(prop));
+		emit_signal("property_keyed",prop,obj->get(prop),false);
+	} else if (p_button==5) {
+		print_line("PB5");
+		if (!d.has("name"))
+			return;
+		String prop=d["name"];
+		emit_signal("property_keyed",prop,obj->get(prop),true);
+		//set_range(p_column, ti->get_range(p_column)+1.0 );
+		call_deferred("_set_range_def",ti,prop,ti->get_range(p_column)+1.0);
 	} else if (p_button==3) {
 
 		if (!get_instanced_node())
@@ -3046,6 +3069,7 @@ void PropertyEditor::_bind_methods() {
 	ObjectTypeDB::bind_method( "_edit_button",&PropertyEditor::_edit_button);
 	ObjectTypeDB::bind_method( "_changed_callback",&PropertyEditor::_changed_callbacks);
 	ObjectTypeDB::bind_method( "_draw_flags",&PropertyEditor::_draw_flags);
+	ObjectTypeDB::bind_method( "_set_range_def",&PropertyEditor::_set_range_def);
 
 	ADD_SIGNAL( MethodInfo("property_toggled",PropertyInfo( Variant::STRING, "property"),PropertyInfo( Variant::BOOL, "value")));
 	ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) );

+ 1 - 0
tools/editor/property_editor.h

@@ -187,6 +187,7 @@ class PropertyEditor : public Control {
 
 	Node *get_instanced_node();
 	void _refresh_item(TreeItem *p_item);
+	void _set_range_def(Object *p_item, String prop, float p_frame);
 
 	UndoRedo *undo_redo;
 protected: