diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp index b03eed77b1e..90bfe0bfdf5 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.cpp +++ b/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()) + return; + + CanvasItem *c=p_node->cast_to(); + + 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(); + 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(); + + _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 *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 &selection = editor_selection->get_selected_node_list(); + + for(List::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to(); + if (!canvas_item || !canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data(canvas_item); + if (!se) + continue; + + se->undo_state=canvas_item->edit_get_state(); + if (canvas_item->cast_to()) + se->undo_pivot=canvas_item->cast_to()->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 *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 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 &selection = editor_selection->get_selected_node_list(); - - for(List::Element *E=selection.front();E;E=E->next()) { - - CanvasItem *canvas_item = E->get()->cast_to(); - if (!canvas_item || !canvas_item->is_visible()) - continue; - CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data(canvas_item); - if (!se) - continue; - - se->undo_state=canvas_item->edit_get_state(); - if (canvas_item->cast_to()) - se->undo_pivot=canvas_item->cast_to()->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( 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; diff --git a/tools/editor/plugins/canvas_item_editor_plugin.h b/tools/editor/plugins/canvas_item_editor_plugin.h index 485422028e1..b96d36f7dc1 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.h +++ b/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 *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(); diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp index 8d5e7bbaea2..e2202214dd8 100644 --- a/tools/editor/plugins/spatial_editor_plugin.cpp +++ b/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 &results,bool p_alt_select) { + + Vector3 ray=_get_ray(p_pos); + Vector3 pos=_get_ray_pos(p_pos); + + Vector instances=VisualServer::get_singleton()->instances_cull_ray(pos,ray,get_tree()->get_root()->get_world()->get_scenario() ); + Set > found_gizmos; + + r_includes_current=false; + + for (int i=0;iinstance_get_object_instance_ID(instances[i]); + Object *obj=ObjectDB::get_instance(id); + if (!obj) + continue; + + Spatial *spat=obj->cast_to(); + + if (!spat) + continue; + + Ref 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 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); diff --git a/tools/editor/plugins/spatial_editor_plugin.h b/tools/editor/plugins/spatial_editor_plugin.h index ff8912fca87..ebd3f77fe74 100644 --- a/tools/editor/plugins/spatial_editor_plugin.h +++ b/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 &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: