Pārlūkot izejas kodu

Merge pull request #2786 from neikeq/node_select_menu

Added selection menu to spatial and canvas editors
Juan Linietsky 9 gadi atpakaļ
vecāks
revīzija
3118c192bd

+ 214 - 75
tools/editor/plugins/canvas_item_editor_plugin.cpp

@@ -447,6 +447,47 @@ CanvasItem* CanvasItemEditor::_select_canvas_item_at_pos(const Point2& p_pos,Nod
 	return NULL;
 }
 
+void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items) {
+	if (!p_node)
+		return;
+	if (p_node->cast_to<Viewport>())
+		return;
+
+	CanvasItem *c=p_node->cast_to<CanvasItem>();
+
+	for (int i=p_node->get_child_count()-1;i>=0;i--) {
+
+		if (c && !c->is_set_as_toplevel())
+			_find_canvas_items_at_pos(p_pos,p_node->get_child(i),p_parent_xform * c->get_transform(),p_canvas_xform, r_items);
+		else {
+			CanvasLayer *cl = p_node->cast_to<CanvasLayer>();
+			if (cl)
+				return;
+			_find_canvas_items_at_pos(p_pos,p_node->get_child(i),transform ,cl ? cl->get_transform() : p_canvas_xform, r_items); //use base transform
+		}
+	}
+
+
+	if (c && c->is_visible() && !c->has_meta("_edit_lock_")) {
+
+		Rect2 rect = c->get_item_rect();
+		Point2 local_pos = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse().xform(p_pos);
+
+
+		if (rect.has_point(local_pos)) {
+			Node2D *node=c->cast_to<Node2D>();
+
+			_SelectResult res;
+			res.item=c;
+			res.z=node?node->get_z():0;
+			res.has_z=node;
+			r_items.push_back(res);
+		}
+
+	}
+
+	return;
+}
 
 void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items) {
 
@@ -489,6 +530,96 @@ void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_no
 
 }
 
+bool CanvasItemEditor::_select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag) {
+
+	if (p_append) {
+		//additive selection
+
+		if (!item) {
+
+			if (p_drag) {
+				drag_from=transform.affine_inverse().xform(p_click_pos);
+
+				box_selecting=true;
+				box_selecting_to=drag_from;
+			}
+
+			return false; //nothing to add
+		}
+
+		if (editor_selection->is_selected(item)) {
+			//already in here, erase it
+			editor_selection->remove_node(item);
+			//_remove_canvas_item(c);
+
+			viewport->update();
+			return false;
+
+		}
+		_append_canvas_item(item);
+		viewport->update();
+
+	} else {
+		//regular selection
+
+		if (!item) {
+			//clear because nothing clicked
+			editor_selection->clear();;
+
+			if (p_drag) {
+				drag_from=transform.affine_inverse().xform(p_click_pos);
+
+				box_selecting=true;
+				box_selecting_to=drag_from;
+			}
+
+			viewport->update();
+			return false;
+		}
+
+		if (!editor_selection->is_selected(item)) {
+			//select a new one and clear previous selection
+			editor_selection->clear();
+			editor_selection->add_node(item);
+			//reselect
+			if (get_tree()->is_editor_hint()) {
+				editor->call("edit_node",item);
+			}
+
+		}
+
+		if (p_drag) {
+			//prepare to move!
+
+			List<Node*> &selection = editor_selection->get_selected_node_list();
+
+			for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+				CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
+				if (!canvas_item || !canvas_item->is_visible())
+					continue;
+				CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+				if (!se)
+					continue;
+
+				se->undo_state=canvas_item->edit_get_state();
+				if (canvas_item->cast_to<Node2D>())
+					se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot();
+
+			}
+
+			drag=DRAG_ALL;
+			drag_from=transform.affine_inverse().xform(p_click_pos);
+			drag_point_from=_find_topleftmost_point();
+		}
+
+		viewport->update();
+
+		return true;
+
+	}
+}
+
 void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE p_move_mode) {
 
 
@@ -788,6 +919,24 @@ void CanvasItemEditor::_dialog_value_changed(double) {
 	}
 }
 
+void CanvasItemEditor::_selection_result_pressed(int p_result) {
+
+	if (selection_results.size() <= p_result)
+		return;
+
+	CanvasItem *item=selection_results[p_result].item;
+
+	if (item)
+		_select(item, Point2(), additive_selection, false);
+}
+
+void CanvasItemEditor::_selection_menu_hide() {
+
+	selection_results.clear();
+	selection_menu->clear();
+	selection_menu->set_size(Vector2(0, 0));
+}
+
 bool CanvasItemEditor::get_remove_list(List<Node*> *p_list) {
 
 
@@ -850,7 +999,60 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
 
 		if (b.button_index==BUTTON_RIGHT) {
 
+			if (!b.pressed && tool==TOOL_SELECT && b.mod.alt) {
+
+				Point2 click=Point2(b.x,b.y);
+
+				Node* scene = editor->get_edited_scene();
+				if (!scene)
+					return;
+
+				_find_canvas_items_at_pos(click, scene,transform,Matrix32(), selection_results);
+
+				if (selection_results.size() == 1) {
+
+					CanvasItem *item = selection_results[0].item;
+					selection_results.clear();
+
+					additive_selection=b.mod.shift;
+					if (!_select(item, click, additive_selection, false))
+						return;
+
+				} else if (!selection_results.empty()) {
+
+					selection_results.sort();
+
+					NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
+					StringName root_name = root_path.get_name(root_path.get_name_count()-1);
+
+					for (int i = 0; i < selection_results.size(); i++) {
+
+						CanvasItem *item=selection_results[i].item;
 
+						Ref<Texture> icon;
+						if (item->has_meta("_editor_icon"))
+							icon=item->get_meta("_editor_icon");
+						else
+							icon=get_icon( has_icon(item->get_type(),"EditorIcons")?item->get_type():String("Object"),"EditorIcons");
+
+						String node_path="/"+root_name+"/"+root_path.rel_path_to(item->get_path());
+
+						selection_menu->add_item(item->get_name());
+						selection_menu->set_item_icon(i, icon );
+						selection_menu->set_item_metadata(i, node_path);
+						selection_menu->set_item_tooltip(i,String(item->get_name())+
+								"\nType: "+item->get_type()+"\nPath: "+node_path);
+					}
+
+					additive_selection=b.mod.shift;
+
+					selection_menu->set_global_pos(Vector2( b.global_x, b.global_y ));
+					selection_menu->popup();
+					selection_menu->call_deferred("grab_click_focus");
+
+					return;
+				}
+			}
 
 			if (get_item_count() > 0 && drag!=DRAG_NONE) {
 				//cancel drag
@@ -1215,82 +1417,10 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
 #if 0
 		if ( b.pressed ) box_selection_start( click );
 #endif
-		if (b.mod.shift) { //additive selection
-
-			if (!c) {
-
-				drag_from=transform.affine_inverse().xform(click);
 
-				box_selecting=true;
-				box_selecting_to=drag_from;
-
-				return; //nothing to add
-			}
-
-			if (editor_selection->is_selected(c)) {
-				//already in here, erase it
-				editor_selection->remove_node(c);
-				//_remove_canvas_item(c);
-
-				viewport->update();
-				return;
-
-			}
-			_append_canvas_item(c);
-			viewport->update();
-		} else {
-			//regular selection
-
-
-
-			if (!c) {
-				//clear because nothing clicked
-				editor_selection->clear();;
-
-				drag_from=transform.affine_inverse().xform(click);
-
-				box_selecting=true;
-				box_selecting_to=drag_from;
-				viewport->update();
-				return;
-			}
-
-			if (!editor_selection->is_selected(c)) {
-				//select a new one and clear previous selection
-				editor_selection->clear();
-				editor_selection->add_node(c);
-				//reselect
-				if (get_tree()->is_editor_hint()) {
-					editor->call("edit_node",c);
-				}
-
-			}
-
-			//prepare to move!
-
-			List<Node*> &selection = editor_selection->get_selected_node_list();
-
-			for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
-
-				CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
-				if (!canvas_item || !canvas_item->is_visible())
-					continue;
-				CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
-				if (!se)
-					continue;
-
-				se->undo_state=canvas_item->edit_get_state();
-				if (canvas_item->cast_to<Node2D>())
-					se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot();
-
-			}
-
-			drag=DRAG_ALL;
-			drag_from=transform.affine_inverse().xform(click);
-			drag_point_from=_find_topleftmost_point();
-			viewport->update();
-
-		}
+		additive_selection=b.mod.shift;
+		if (!_select(c, click, additive_selection))
+			return;
 
 	}
 
@@ -2879,6 +3009,8 @@ void CanvasItemEditor::_bind_methods() {
 	ObjectTypeDB::bind_method("_viewport_draw",&CanvasItemEditor::_viewport_draw);
 	ObjectTypeDB::bind_method("_viewport_input_event",&CanvasItemEditor::_viewport_input_event);
 	ObjectTypeDB::bind_method("_snap_changed",&CanvasItemEditor::_snap_changed);
+	ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&CanvasItemEditor::_selection_result_pressed);
+	ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&CanvasItemEditor::_selection_menu_hide);
 
 	ADD_SIGNAL( MethodInfo("item_lock_status_changed") );
 	ADD_SIGNAL( MethodInfo("item_group_status_changed") );
@@ -3208,6 +3340,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	dialog_val->connect("value_changed",this,"_dialog_value_changed");
 	select_sb = Ref<StyleBoxTexture>( memnew( StyleBoxTexture) );
 
+	selection_menu = memnew( PopupMenu );
+	add_child(selection_menu);
+	selection_menu->set_custom_minimum_size(Vector2(100, 0));
+	selection_menu->connect("item_pressed", this, "_selection_result_pressed");
+	selection_menu->connect("popup_hide", this, "_selection_menu_hide");
+
 	key_pos=true;
 	key_rot=true;
 	key_scale=false;
@@ -3230,6 +3368,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	can_move_pivot=false;
 	drag=DRAG_NONE;
 	bone_last_frame=0;
+	additive_selection=false;
 }
 
 CanvasItemEditor *CanvasItemEditor::singleton=NULL;

+ 21 - 0
tools/editor/plugins/canvas_item_editor_plugin.h

@@ -150,6 +150,7 @@ class CanvasItemEditor : public VBoxContainer {
 	};
 
 	EditorSelection *editor_selection;
+	bool additive_selection;
 
 	Tool tool;
 	bool first_update;
@@ -183,6 +184,18 @@ class CanvasItemEditor : public VBoxContainer {
 
 	MenuOption last_option;
 
+	struct _SelectResult {
+
+		CanvasItem* item;
+		float z;
+		bool has_z;
+		_FORCE_INLINE_ bool operator<(const _SelectResult& p_rr) const {
+			return has_z && p_rr.has_z ? p_rr.z < z : p_rr.has_z;
+		}
+	};
+
+	Vector<_SelectResult> selection_results;
+
 	struct LockList {
 		Point2 pos;
 		bool lock;
@@ -249,6 +262,8 @@ class CanvasItemEditor : public VBoxContainer {
 	Button *key_scale_button;
 	Button *key_insert_button;
 
+	PopupMenu *selection_menu;
+
 	//PopupMenu *popup;
 	DragType drag;
 	Point2 drag_from;
@@ -276,8 +291,11 @@ class CanvasItemEditor : public VBoxContainer {
 
 	int handle_len;
 	CanvasItem* _select_canvas_item_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform);
+	void _find_canvas_items_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items);
 	void _find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items);
 
+	bool _select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag=true);
+
 	ConfirmationDialog *snap_dialog;
 	
 	AcceptDialog *value_dialog;
@@ -304,6 +322,9 @@ class CanvasItemEditor : public VBoxContainer {
 	void _append_canvas_item(CanvasItem *p_item);
 	void _dialog_value_changed(double);
 	void _snap_changed();
+	void _selection_result_pressed(int);
+	void _selection_menu_hide();
+
 	UndoRedo *undo_redo;
 
 	Point2 _find_topleftmost_point();

+ 144 - 9
tools/editor/plugins/spatial_editor_plugin.cpp

@@ -232,15 +232,6 @@ void SpatialEditorViewport::_select(Spatial *p_node, bool p_append,bool p_single
 
 }
 
-
-struct _RayResult {
-
-	Spatial* item;
-	float depth;
-	int handle;
-	_FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; }
-};
-
 ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle,bool p_alt_select) {
 
 	if (r_gizmo_handle)
@@ -379,6 +370,70 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,b
 
 }
 
+void SpatialEditorViewport::_find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select) {
+
+	Vector3 ray=_get_ray(p_pos);
+	Vector3 pos=_get_ray_pos(p_pos);
+
+	Vector<RID> instances=VisualServer::get_singleton()->instances_cull_ray(pos,ray,get_tree()->get_root()->get_world()->get_scenario() );
+	Set<Ref<SpatialEditorGizmo> > found_gizmos;
+
+	r_includes_current=false;
+
+	for (int i=0;i<instances.size();i++) {
+
+		uint32_t id=VisualServer::get_singleton()->instance_get_object_instance_ID(instances[i]);
+		Object *obj=ObjectDB::get_instance(id);
+		if (!obj)
+			continue;
+
+		Spatial *spat=obj->cast_to<Spatial>();
+
+		if (!spat)
+			continue;
+
+		Ref<SpatialEditorGizmo> seg = spat->get_gizmo();
+
+		if (!seg.is_valid())
+			continue;
+
+		if (found_gizmos.has(seg))
+			continue;
+
+		found_gizmos.insert(seg);
+		Vector3 point;
+		Vector3 normal;
+
+		int handle=-1;
+		bool inters = seg->intersect_ray(camera,p_pos,point,normal,NULL,p_alt_select);
+
+		if (!inters)
+			continue;
+
+		float dist = pos.distance_to(point);
+
+		if (dist<0)
+			continue;
+
+
+
+		if (editor_selection->is_selected(spat))
+			r_includes_current=true;
+
+		_RayResult res;
+		res.item=spat;
+		res.depth=dist;
+		res.handle=handle;
+		results.push_back(res);
+	}
+
+
+	if (results.empty())
+		return;
+
+	results.sort();
+}
+
 
 Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3& p_pos) {
 
@@ -724,6 +779,7 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
 				} break;
 				case BUTTON_RIGHT: {
 
+					NavigationScheme nav_scheme = _get_navigation_schema("3d_editor/navigation_scheme");
 
 					if (b.pressed && _edit.gizmo.is_valid()) {
 						//restore
@@ -832,6 +888,57 @@ void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
 						//VisualServer::get_singleton()->poly_clear(indicators);
 						set_message("Transform Aborted.",3);
 					}
+
+					if (!b.pressed && (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SELECT && b.mod.alt)) {
+
+						if (nav_scheme == NAVIGATION_MAYA)
+							break;
+
+						_find_items_at_pos(Vector2( b.x, b.y ),clicked_includes_current,selection_results,b.mod.shift);
+
+						clicked_wants_append=b.mod.shift;
+
+						if (selection_results.size() == 1) {
+
+							clicked=selection_results[0].item->get_instance_ID();
+							selection_results.clear();
+
+							if (clicked) {
+								_select_clicked(clicked_wants_append,true);
+								clicked=0;
+							}
+
+						} else if (!selection_results.empty()) {
+
+							NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
+							StringName root_name = root_path.get_name(root_path.get_name_count()-1);
+
+							for (int i = 0; i < selection_results.size(); i++) {
+
+								Spatial *spat=selection_results[i].item;
+
+								Ref<Texture> icon;
+								if (spat->has_meta("_editor_icon"))
+									icon=spat->get_meta("_editor_icon");
+								else
+									icon=get_icon( has_icon(spat->get_type(),"EditorIcons")?spat->get_type():String("Object"),"EditorIcons");
+
+								String node_path="/"+root_name+"/"+root_path.rel_path_to(spat->get_path());
+
+								selection_menu->add_item(spat->get_name());
+								selection_menu->set_item_icon(i, icon );
+								selection_menu->set_item_metadata(i, node_path);
+								selection_menu->set_item_tooltip(i,String(spat->get_name())+
+										"\nType: "+spat->get_type()+"\nPath: "+node_path);
+							}
+
+							selection_menu->set_global_pos(Vector2( b.global_x, b.global_y ));
+							selection_menu->popup();
+							selection_menu->call_deferred("grab_click_focus");
+
+							break;
+						}
+					}
 				} break;
 				case BUTTON_MIDDLE: {
 
@@ -2096,6 +2203,26 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
 	}
 }
 
+void SpatialEditorViewport::_selection_result_pressed(int p_result) {
+
+	if (selection_results.size() <= p_result)
+		return;
+
+	clicked=selection_results[p_result].item->get_instance_ID();
+
+	if (clicked) {
+		_select_clicked(clicked_wants_append,true);
+		clicked=0;
+	}
+}
+
+void SpatialEditorViewport::_selection_menu_hide() {
+
+	selection_results.clear();
+	selection_menu->clear();
+	selection_menu->set_size(Vector2(0, 0));
+}
+
 void SpatialEditorViewport::set_can_preview(Camera* p_preview) {
 
 	preview=p_preview;
@@ -2210,6 +2337,8 @@ void SpatialEditorViewport::_bind_methods(){
 	ObjectTypeDB::bind_method(_MD("_toggle_camera_preview"),&SpatialEditorViewport::_toggle_camera_preview);
 	ObjectTypeDB::bind_method(_MD("_preview_exited_scene"),&SpatialEditorViewport::_preview_exited_scene);
 	ObjectTypeDB::bind_method(_MD("update_transform_gizmo_view"),&SpatialEditorViewport::update_transform_gizmo_view);
+	ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&SpatialEditorViewport::_selection_result_pressed);
+	ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&SpatialEditorViewport::_selection_menu_hide);
 
 	ADD_SIGNAL( MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")) );
 }
@@ -2307,6 +2436,12 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 	preview=NULL;
 	gizmo_scale=1.0;
 
+	selection_menu = memnew( PopupMenu );
+	add_child(selection_menu);
+	selection_menu->set_custom_minimum_size(Vector2(100, 0));
+	selection_menu->connect("item_pressed", this, "_selection_result_pressed");
+	selection_menu->connect("popup_hide", this, "_selection_menu_hide");
+
 	if (p_index==0) {
 		view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER),true);
 		viewport->set_as_audio_listener(true);

+ 14 - 0
tools/editor/plugins/spatial_editor_plugin.h

@@ -111,12 +111,21 @@ private:
 	bool orthogonal;
 	float gizmo_scale;
 
+	struct _RayResult {
+
+		Spatial* item;
+		float depth;
+		int handle;
+		_FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; }
+	};
+
 	void _update_name();
 	void _compute_edit(const Point2& p_point);
 	void _clear_selected();
 	void _select_clicked(bool p_append,bool p_single);
 	void _select(Spatial *p_node, bool p_append,bool p_single);
 	ObjectID _select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle=NULL,bool p_alt_select=false);
+	void _find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select=false);
 	Vector3 _get_ray_pos(const Vector2& p_pos) const;
 	Vector3 _get_ray(const Vector2& p_pos);
 	Point2 _point_to_screen(const Vector3& p_point);
@@ -136,9 +145,12 @@ private:
 	float get_fov() const;
 
 	ObjectID clicked;
+	Vector<_RayResult> selection_results;
 	bool clicked_includes_current;
 	bool clicked_wants_append;
 
+	PopupMenu *selection_menu;
+
 	enum NavigationScheme {
 		NAVIGATION_GODOT,
 		NAVIGATION_MAYA,
@@ -225,6 +237,8 @@ private:
 	void _toggle_camera_preview(bool);
 	void _init_gizmo_instance(int p_idx);
 	void _finish_gizmo_instances();
+	void _selection_result_pressed(int);
+	void _selection_menu_hide();
 
 
 protected: