Browse Source

Improve drag and drop on 2D viewport

volzhs 9 years ago
parent
commit
eed9179ea3

+ 403 - 2
tools/editor/plugins/canvas_item_editor_plugin.cpp

@@ -32,13 +32,20 @@
 #include "os/keyboard.h"
 #include "scene/main/viewport.h"
 #include "scene/main/canvas_layer.h"
-#include "scene/2d/node_2d.h"
+#include "scene/2d/sprite.h"
+#include "scene/2d/light_2d.h"
+#include "scene/2d/particles_2d.h"
+#include "scene/2d/polygon_2d.h"
+#include "scene/2d/screen_button.h"
 #include "globals.h"
 #include "os/input.h"
 #include "tools/editor/editor_settings.h"
 #include "scene/gui/grid_container.h"
+#include "scene/gui/patch_9_frame.h"
 #include "tools/editor/animation_editor.h"
 #include "tools/editor/plugins/animation_player_editor_plugin.h"
+#include "tools/editor/script_editor_debugger.h"
+#include "tools/editor/plugins/script_editor_plugin.h"
 #include "scene/resources/packed_scene.h"
 
 
@@ -3326,7 +3333,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	vp->add_child(p_editor->get_scene_root());
 
 
-	viewport = memnew( Control );
+	viewport = memnew( CanvasItemEditorViewport(p_editor, this) );
 	vp_base->add_child(viewport);
 	viewport->set_area_as_parent_rect();
 
@@ -3642,3 +3649,397 @@ CanvasItemEditorPlugin::~CanvasItemEditorPlugin()
 }
 
 
+void CanvasItemEditorViewport::_on_mouse_exit() {
+	if (selector->is_hidden()){
+		_remove_preview();
+	}
+}
+
+void CanvasItemEditorViewport::_on_select_type(Object* selected) {
+	CheckBox* check = selected->cast_to<CheckBox>();
+	String type = check->get_text();
+	selector_label->set_text(vformat(TTR("Add %s"),type));
+	label->set_text(vformat(TTR("Adding %s..."),type));
+}
+
+void CanvasItemEditorViewport::_on_change_type() {
+	CheckBox* check=btn_group->get_pressed_button()->cast_to<CheckBox>();
+	default_type=check->get_text();
+	_perform_drop_data();
+	selector->hide();
+}
+
+void CanvasItemEditorViewport::_create_preview(const Vector<String>& files) const {
+	label->set_pos(get_global_pos()+Point2(14,14));
+	label_desc->set_pos(label->get_pos()+Point2(0,label->get_size().height));
+	for (int i=0;i<files.size();i++) {
+		String path=files[i];
+		RES res=ResourceLoader::load(path);
+		String type=res->get_type();
+		if (type=="ImageTexture" || type=="PackedScene") {
+			if (type=="ImageTexture") {
+				Ref<ImageTexture> texture=Ref<ImageTexture> ( ResourceCache::get(path)->cast_to<ImageTexture>() );
+				Sprite* sprite=memnew(Sprite);
+				sprite->set_texture(texture);
+				sprite->set_opacity(0.7f);
+				preview->add_child(sprite);
+				label->show();
+				label_desc->show();
+			} else if (type=="PackedScene") {
+				Ref<PackedScene> scn=ResourceLoader::load(path);
+				if (scn.is_valid()){
+					Node* instance=scn->instance();
+					if (instance){
+						preview->add_child(instance);
+					}
+				}
+			}
+			editor->get_scene_root()->add_child(preview);
+		}
+	}
+}
+
+void CanvasItemEditorViewport::_remove_preview() {
+	if (preview->get_parent()){
+		editor->get_scene_root()->remove_child(preview);
+		for (int i=preview->get_child_count()-1;i>=0;i--){
+			Node* node=preview->get_child(i);
+			memdelete(node);
+		}
+		label->hide();
+		label_desc->hide();
+	}
+}
+
+bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
+	if (p_desired_node->get_filename()==p_target_scene_path) {
+		return true;
+	}
+
+	int childCount=p_desired_node->get_child_count();
+	for (int i=0;i<childCount;i++) {
+		Node* child=p_desired_node->get_child(i);
+		if(_cyclical_dependency_exists(p_target_scene_path,child)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+void CanvasItemEditorViewport::_create_nodes(Node* parent, Node* child, String& path, const Point2& p_point) {
+	child->set_name(path.get_file().basename());
+	Ref<ImageTexture> texture=Ref<ImageTexture> ( ResourceCache::get(path)->cast_to<ImageTexture>() );
+	Size2 texture_size = texture->get_size();
+
+	editor_data->get_undo_redo().add_do_method(parent,"add_child",child);
+	editor_data->get_undo_redo().add_do_method(child,"set_owner",editor->get_edited_scene());
+	editor_data->get_undo_redo().add_do_reference(child);
+	editor_data->get_undo_redo().add_undo_method(parent,"remove_child",child);
+
+	String new_name=parent->validate_child_name(child);
+	ScriptEditorDebugger *sed=ScriptEditor::get_singleton()->get_debugger();
+	editor_data->get_undo_redo().add_do_method(sed,"live_debug_create_node",editor->get_edited_scene()->get_path_to(parent),child->get_type(),new_name);
+	editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(editor->get_edited_scene()->get_path_to(parent))+"/"+new_name));
+
+	// handle with different property for texture
+	String property = "texture";
+	List<PropertyInfo> props;
+	child->get_property_list(&props);
+	for(const List<PropertyInfo>::Element *E=props.front();E;E=E->next() ) {
+		if (E->get().name=="config/texture") { // Particles2D
+			property="config/texture";
+			break;
+		} else if (E->get().name=="texture/texture") { // Polygon2D
+			property="texture/texture";
+			break;
+		} else if (E->get().name=="normal") { // TouchScreenButton
+			property="normal";
+			break;
+		}
+	}
+	editor_data->get_undo_redo().add_do_property(child,property,texture);
+
+	// make visible for certain node type
+	if (default_type=="Patch9Frame") {
+		editor_data->get_undo_redo().add_do_property(child,"rect/size",texture_size);
+	} else if (default_type=="Polygon2D") {
+		DVector<Vector2> list;
+		list.push_back(Vector2(0,0));
+		list.push_back(Vector2(texture_size.width,0));
+		list.push_back(Vector2(texture_size.width,texture_size.height));
+		list.push_back(Vector2(0,texture_size.height));
+		editor_data->get_undo_redo().add_do_property(child,"polygon",list);
+	}
+
+	// locate at preview position
+	Point2 pos;
+	if (parent->has_method("get_global_pos")) {
+		pos=parent->call("get_global_pos");
+	}
+	Matrix32 trans=canvas->get_canvas_transform();
+	Point2 target_pos = (p_point-trans.get_origin())/trans.get_scale().x-pos;
+	if (default_type=="Polygon2D" || default_type=="TouchScreenButton" || default_type=="TextureFrame" || default_type=="Patch9Frame") {
+		target_pos -= texture_size/2;
+	}
+	editor_data->get_undo_redo().add_do_method(child,"set_pos",target_pos);
+}
+
+bool CanvasItemEditorViewport::_create_instance(Node* parent, String& path, const Point2& p_point) {
+	Ref<PackedScene> sdata=ResourceLoader::load(path);
+	if (!sdata.is_valid()) { // invalid scene
+		return false;
+	}
+
+	Node* instanced_scene=sdata->instance(true);
+	if (!instanced_scene) { // error on instancing
+		return false;
+	}
+
+	if (editor->get_edited_scene()->get_filename()!="") { // cyclical instancing
+		if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instanced_scene)) {
+			return false;
+		}
+	}
+
+	instanced_scene->set_filename( Globals::get_singleton()->localize_path(path) );
+
+	editor_data->get_undo_redo().add_do_method(parent,"add_child",instanced_scene);
+	editor_data->get_undo_redo().add_do_method(instanced_scene,"set_owner",editor->get_edited_scene());
+	editor_data->get_undo_redo().add_do_reference(instanced_scene);
+	editor_data->get_undo_redo().add_undo_method(parent,"remove_child",instanced_scene);
+
+	String new_name=parent->validate_child_name(instanced_scene);
+	ScriptEditorDebugger *sed=ScriptEditor::get_singleton()->get_debugger();
+	editor_data->get_undo_redo().add_do_method(sed,"live_debug_instance_node",editor->get_edited_scene()->get_path_to(parent),path,new_name);
+	editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(editor->get_edited_scene()->get_path_to(parent))+"/"+new_name));
+
+	Point2 pos;
+	Node2D* parent_node2d=parent->cast_to<Node2D>();
+	if (parent_node2d) {
+		pos=parent_node2d->get_global_pos();
+	} else {
+		Control* parent_control=parent->cast_to<Control>();
+		if (parent_control) {
+			pos=parent_control->get_global_pos();
+		}
+	}
+	Matrix32 trans=canvas->get_canvas_transform();
+	editor_data->get_undo_redo().add_do_method(instanced_scene,"set_pos",(p_point-trans.get_origin())/trans.get_scale().x-pos);
+
+	return true;
+}
+
+void CanvasItemEditorViewport::_perform_drop_data(){
+	_remove_preview();
+
+	Vector<String> error_files;
+
+	editor_data->get_undo_redo().create_action(TTR("Create Node"));
+
+	for (int i=0;i<selected_files.size();i++) {
+		String path=selected_files[i];
+		RES res=ResourceLoader::load(path);
+		if (res.is_null()) {
+			continue;
+		}
+		String type=res->get_type();
+		if (type=="ImageTexture") {
+			Node* child;
+			if      (default_type=="Light2D")           child=memnew(Light2D);
+			else if (default_type=="Particles2D")       child=memnew(Particles2D);
+			else if (default_type=="Polygon2D")         child=memnew(Polygon2D);
+			else if (default_type=="TouchScreenButton") child=memnew(TouchScreenButton);
+			else if (default_type=="TextureFrame")      child=memnew(TextureFrame);
+			else if (default_type=="Patch9Frame")       child=memnew(Patch9Frame);
+			else child=memnew(Sprite); // default
+
+			_create_nodes(target_node, child, path, drop_pos);
+		} else if (type=="PackedScene") {
+			bool success=_create_instance(target_node, path, drop_pos);
+			if (!success) {
+				error_files.push_back(path);
+			}
+		}
+	}
+
+	editor_data->get_undo_redo().commit_action();
+
+	if (error_files.size()>0) {
+		String files_str;
+		for (int i=0;i<error_files.size();i++) {
+			files_str += error_files[i].get_file().basename() + ",";
+		}
+		files_str=files_str.substr(0,files_str.length()-1);
+		accept->get_ok()->set_text(TTR("Ugh"));
+		accept->set_text(vformat(TTR("Error instancing scene from %s"),files_str.c_str()));
+		accept->popup_centered_minsize();
+	}
+}
+
+bool CanvasItemEditorViewport::can_drop_data(const Point2& p_point,const Variant& p_data) const {
+	Dictionary d=p_data;
+	if (d.has("type")) {
+		if (String(d["type"])=="files") {
+			Vector<String> files=d["files"];
+			bool can_instance=false;
+			for (int i=0;i<files.size();i++) { // check if dragged files contain resource or scene can be created at least one
+				RES res=ResourceLoader::load(files[i]);
+				if (res.is_null()) {
+					continue;
+				}
+				String type=res->get_type();
+				if (type=="PackedScene") {
+					Ref<PackedScene> sdata=ResourceLoader::load(files[i]);
+					Node* instanced_scene=sdata->instance(true);
+					if (!instanced_scene) {
+						continue;
+					}
+					memdelete(instanced_scene);
+				}
+				can_instance=true;
+				break;
+			}
+			if (can_instance) {
+				if (!preview->get_parent()){ // create preview only once
+					_create_preview(files);
+				}
+				Matrix32 trans=canvas->get_canvas_transform();
+				preview->set_pos((p_point-trans.get_origin())/trans.get_scale().x);
+				label->set_text(vformat(TTR("Adding %s..."),default_type));
+			}
+			return can_instance;
+		}
+	}
+	label->hide();
+	return false;
+}
+
+void CanvasItemEditorViewport::drop_data(const Point2& p_point,const Variant& p_data) {
+	bool is_shift=Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+	bool is_alt=Input::get_singleton()->is_key_pressed(KEY_ALT);
+
+	selected_files.clear();
+	Dictionary d=p_data;
+	if (d.has("type") && String(d["type"])=="files"){
+		selected_files=d["files"];
+	}
+
+	List<Node*> list=editor->get_editor_selection()->get_selected_node_list();
+	if (list.size()==0) {
+		accept->get_ok()->set_text(TTR("OK :("));
+		accept->set_text(TTR("No parent to instance a child at."));
+		accept->popup_centered_minsize();
+		_remove_preview();
+		return;
+	}
+	if (list.size()!=1) {
+		accept->get_ok()->set_text(TTR("I see.."));
+		accept->set_text(TTR("This operation requires a single selected node."));
+		accept->popup_centered_minsize();
+		_remove_preview();
+		return;
+	}
+
+	target_node=list[0];
+	if (is_shift && target_node!=editor->get_edited_scene()) {
+		target_node=target_node->get_parent();
+	}
+	drop_pos=p_point;
+
+	if (is_alt) {
+		List<BaseButton*> btn_list;
+		btn_group->get_button_list(&btn_list);
+		for (int i=0;i<btn_list.size();i++) {
+			CheckBox* check=btn_list[i]->cast_to<CheckBox>();
+			check->set_pressed(check->get_text()==default_type);
+		}
+		selector_label->set_text(vformat(TTR("Add %s"),default_type));
+		selector->popup_centered_minsize();
+	} else {
+		_perform_drop_data();
+	}
+}
+
+void CanvasItemEditorViewport::_notification(int p_what) {
+	if (p_what==NOTIFICATION_ENTER_TREE) {
+		connect("mouse_exit",this,"_on_mouse_exit");
+	} else if (p_what==NOTIFICATION_EXIT_TREE) {
+		disconnect("mouse_exit",this,"_on_mouse_exit");
+	}
+}
+
+void CanvasItemEditorViewport::_bind_methods() {
+	ObjectTypeDB::bind_method(_MD("_on_select_type"),&CanvasItemEditorViewport::_on_select_type);
+	ObjectTypeDB::bind_method(_MD("_on_change_type"),&CanvasItemEditorViewport::_on_change_type);
+	ObjectTypeDB::bind_method(_MD("_on_mouse_exit"),&CanvasItemEditorViewport::_on_mouse_exit);
+}
+
+CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor* p_canvas) {
+	default_type="Sprite";
+	// Node2D
+	types.push_back("Sprite");
+	types.push_back("Light2D");
+	types.push_back("Particles2D");
+	types.push_back("Polygon2D");
+	types.push_back("TouchScreenButton");
+	// Control
+	types.push_back("TextureFrame");
+	types.push_back("Patch9Frame");
+
+	target_node=NULL;
+	editor=p_node;
+	editor_data=editor->get_scene_tree_dock()->get_editor_data();
+	canvas=p_canvas;
+	preview=memnew( Node2D );
+	accept=memnew( AcceptDialog );
+	editor->get_gui_base()->add_child(accept);
+
+	selector=memnew( WindowDialog );
+	selector->set_title(TTR("Change default type"));
+
+	VBoxContainer* vbc=memnew(VBoxContainer);
+	vbc->add_constant_override("separation",10*EDSCALE);
+	vbc->set_custom_minimum_size(Size2(200,260)*EDSCALE);
+
+	selector_label=memnew(Label);
+	selector_label->set_align(Label::ALIGN_CENTER);
+	selector_label->set_valign(Label::VALIGN_BOTTOM);
+	selector_label->set_custom_minimum_size(Size2(0,30)*EDSCALE);
+	vbc->add_child(selector_label);
+
+	btn_group=memnew( ButtonGroup );
+	btn_group->set_h_size_flags(0);
+	btn_group->connect("button_selected", this, "_on_select_type");
+
+	for (int i=0;i<types.size();i++) {
+		CheckBox* check=memnew(CheckBox);
+		check->set_text(types[i]);
+		btn_group->add_child(check);
+	}
+	vbc->add_child(btn_group);
+
+	Button* ok=memnew(Button);
+	ok->set_text(TTR("OK"));
+	ok->set_h_size_flags(0);
+	vbc->add_child(ok);
+	ok->connect("pressed", this, "_on_change_type");
+
+	selector->add_child(vbc);
+	editor->get_gui_base()->add_child(selector);
+
+	label=memnew(Label);
+	label->add_color_override("font_color", Color(1,1,0,1));
+	label->add_color_override("font_color_shadow", Color(0,0,0,1));
+	label->add_constant_override("shadow_as_outline", 1*EDSCALE);
+	label->hide();
+	editor->get_gui_base()->add_child(label);
+
+	label_desc=memnew(Label);
+	label_desc->set_text(TTR("Drag & drop + Shift : Add node as sibling\nDrag & drop + Alt : Change node type"));
+	label_desc->add_color_override("font_color", Color(0.6,0.6,0.6,1));
+	label_desc->add_color_override("font_color_shadow", Color(0.2,0.2,0.2,1));
+	label_desc->add_constant_override("shadow_as_outline", 1*EDSCALE);
+	label_desc->add_constant_override("line_spacing", 0);
+	label_desc->hide();
+	editor->get_gui_base()->add_child(label_desc);
+}

+ 49 - 3
tools/editor/plugins/canvas_item_editor_plugin.h

@@ -31,17 +31,18 @@
 
 #include "tools/editor/editor_plugin.h"
 #include "tools/editor/editor_node.h"
+#include "scene/gui/button_group.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/label.h"
 #include "scene/gui/spin_box.h"
 #include "scene/gui/panel_container.h"
 #include "scene/gui/box_container.h"
 #include "scene/2d/canvas_item.h"
-
 /**
 	@author Juan Linietsky <[email protected]>
 */
 
-
-
+class CanvasItemEditorViewport;
 
 class CanvasItemEditorSelectedItem : public Object {
 
@@ -453,4 +454,49 @@ public:
 
 };
 
+class CanvasItemEditorViewport : public VBoxContainer {
+	OBJ_TYPE( CanvasItemEditorViewport, VBoxContainer );
+
+	String default_type;
+	Vector<String> types;
+
+	Vector<String> selected_files;
+	Node* target_node;
+	Point2 drop_pos;
+
+	EditorNode* editor;
+	EditorData* editor_data;
+	CanvasItemEditor* canvas;
+	Node2D* preview;
+	AcceptDialog* accept;
+	WindowDialog* selector;
+	Label* selector_label;
+	Label* label;
+	Label* label_desc;
+	ButtonGroup* btn_group;
+
+	void _on_mouse_exit();
+	void _on_select_type(Object* selected);
+	void _on_change_type();
+
+	void _create_preview(const Vector<String>& files) const;
+	void _remove_preview();
+
+	bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node);
+	void _create_nodes(Node* parent, Node* child, String& path, const Point2& p_point);
+	bool _create_instance(Node* parent, String& path, const Point2& p_point);
+	void _perform_drop_data();
+
+	static void _bind_methods();
+
+protected:
+	void _notification(int p_what);
+
+public:
+	virtual bool can_drop_data(const Point2& p_point,const Variant& p_data) const;
+	virtual void drop_data(const Point2& p_point,const Variant& p_data);
+
+	CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor* p_canvas);
+};
+
 #endif

+ 1 - 1
tools/editor/scene_tree_dock.h

@@ -174,7 +174,7 @@ public:
 	void fill_path_renames(Node* p_node, Node *p_new_parent, List<Pair<NodePath,NodePath> > *p_renames);
 	void perform_node_renames(Node* p_base,List<Pair<NodePath,NodePath> > *p_renames, Map<Ref<Animation>, Set<int> > *r_rem_anims=NULL);
 	SceneTreeEditor *get_tree_editor() { return scene_tree; }
-
+	EditorData *get_editor_data() { return editor_data; }
 
 	void open_script_dialog(Node* p_for_node);
 	SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelection *p_editor_selection,EditorData &p_editor_data);