From d7318f69653ca090575d1243256fcafe8d9ca25f Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Wed, 11 May 2016 11:46:08 -0300 Subject: [PATCH] -begun implementing drag & drop editor wide -filesystem dock dnd support -property list dnd support -scene tree dnd support --- scene/gui/item_list.cpp | 14 +- scene/gui/item_list.h | 1 + scene/gui/tree.cpp | 253 +++++++++++++++++- scene/gui/tree.h | 32 ++- scene/main/node.cpp | 2 + scene/main/node.h | 2 + scene/main/viewport.cpp | 35 ++- scene/main/viewport.h | 2 + .../resources/default_theme/default_theme.cpp | 1 + tools/editor/editor_node.cpp | 126 ++++++++- tools/editor/editor_node.h | 10 +- .../plugins/canvas_item_editor_plugin.cpp | 2 + tools/editor/property_editor.cpp | 200 +++++++++++++- tools/editor/property_editor.h | 13 + tools/editor/scene_tree_dock.cpp | 209 ++++++++++++++- tools/editor/scene_tree_dock.h | 4 + tools/editor/scene_tree_editor.cpp | 97 +++++++ tools/editor/scene_tree_editor.h | 6 + tools/editor/scenes_dock.cpp | 145 +++++++++- tools/editor/scenes_dock.h | 3 + 20 files changed, 1120 insertions(+), 37 deletions(-) diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 6a251a5ac5c..64f1de42afa 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1044,8 +1044,7 @@ void ItemList::_scroll_changed(double) { update(); } - -String ItemList::get_tooltip(const Point2& p_pos) const { +int ItemList::get_item_at_pos(const Point2& p_pos) const { Vector2 pos=p_pos; Ref bg = get_stylebox("bg"); @@ -1074,6 +1073,13 @@ String ItemList::get_tooltip(const Point2& p_pos) const { } } + return closest; +} + +String ItemList::get_tooltip(const Point2& p_pos) const { + + int closest = get_item_at_pos(p_pos); + if (closest!=-1) { if (items[closest].tooltip!="") { return items[closest].tooltip; @@ -1084,8 +1090,6 @@ String ItemList::get_tooltip(const Point2& p_pos) const { } return Control::get_tooltip(p_pos); - - } void ItemList::sort_items_by_text() { @@ -1170,6 +1174,8 @@ void ItemList::_bind_methods(){ ObjectTypeDB::bind_method(_MD("set_min_icon_size","size"),&ItemList::set_min_icon_size); ObjectTypeDB::bind_method(_MD("get_min_icon_size"),&ItemList::get_min_icon_size); + ObjectTypeDB::bind_method(_MD("get_item_at_pos","pos"),&ItemList::get_item_at_pos); + ObjectTypeDB::bind_method(_MD("ensure_current_is_visible"),&ItemList::ensure_current_is_visible); ObjectTypeDB::bind_method(_MD("_scroll_changed"),&ItemList::_scroll_changed); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 5aec946686e..fcb4dfae5a5 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -140,6 +140,7 @@ public: int find_metadata(const Variant& p_metadata) const; virtual String get_tooltip(const Point2& p_pos) const; + int get_item_at_pos(const Point2& p_pos) const; ItemList(); ~ItemList(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index c4efe82eaa3..c141635bbf7 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -604,10 +604,11 @@ String TreeItem::get_tooltip(int p_column) const{ return cells[p_column].tooltip; } -void TreeItem::set_custom_bg_color(int p_column,const Color& p_color) { +void TreeItem::set_custom_bg_color(int p_column,const Color& p_color,bool p_bg_outline) { ERR_FAIL_INDEX( p_column, cells.size() ); cells[p_column].custom_bg_color=true; + cells[p_column].custom_bg_outline=p_bg_outline; cells[p_column].bg_color=p_color; _changed_notify(p_column); } @@ -685,7 +686,7 @@ void TreeItem::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_custom_color","column","color"),&TreeItem::set_custom_color); ObjectTypeDB::bind_method(_MD("clear_custom_color","column"),&TreeItem::clear_custom_color); - ObjectTypeDB::bind_method(_MD("set_custom_bg_color","column","color"),&TreeItem::set_custom_bg_color); + ObjectTypeDB::bind_method(_MD("set_custom_bg_color","column","color","just_outline"),&TreeItem::set_custom_bg_color,DEFVAL(false)); ObjectTypeDB::bind_method(_MD("clear_custom_bg_color","column"),&TreeItem::clear_custom_bg_color); ObjectTypeDB::bind_method(_MD("get_custom_bg_color","column"),&TreeItem::get_custom_bg_color); @@ -757,6 +758,13 @@ TreeItem::~TreeItem() { if (tree && tree->selected_item==this) tree->selected_item=NULL; + + if (tree && tree->drop_mode_over==this) + tree->drop_mode_over=NULL; + + if (tree && tree->single_select_defer==this) + tree->single_select_defer=NULL; + if (tree && tree->edited_item==this) { tree->edited_item=NULL; tree->pressing_for_editor=false; @@ -797,17 +805,13 @@ void Tree::update_cache() { cache.font_color=get_color("font_color"); cache.font_color_selected=get_color("font_color_selected"); cache.guide_color=get_color("guide_color"); + cache.drop_position_color=get_color("drop_position_color"); cache.hseparation=get_constant("hseparation"); cache.vseparation=get_constant("vseparation"); cache.item_margin=get_constant("item_margin"); cache.button_margin=get_constant("button_margin"); cache.guide_width=get_constant("guide_width"); - Ref title_button; - Ref title_button_hover; - Ref title_button_pressed; - Color title_button_color; - cache.title_button = get_stylebox("title_button_normal"); cache.title_button_pressed = get_stylebox("title_button_pressed"); cache.title_button_hover = get_stylebox("title_button_hover"); @@ -1087,7 +1091,34 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& Rect2 r=cell_rect; r.pos.x-=cache.hseparation; r.size.x+=cache.hseparation; - VisualServer::get_singleton()->canvas_item_add_rect(ci,r,p_item->cells[i].bg_color); + if (p_item->cells[i].custom_bg_outline) { + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,r.size.x,1),p_item->cells[i].bg_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y+r.size.y-1,r.size.x,1),p_item->cells[i].bg_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,1,r.size.y),p_item->cells[i].bg_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x+r.size.x-1,r.pos.y,1,r.size.y),p_item->cells[i].bg_color); + } else { + VisualServer::get_singleton()->canvas_item_add_rect(ci,r,p_item->cells[i].bg_color); + } + } + + if (drop_mode_flags && drop_mode_over==p_item) { + + Rect2 r=cell_rect; + + if (drop_mode_section==-1 || drop_mode_section==0) { + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,r.size.x,1),cache.drop_position_color); + + } + + if (drop_mode_section==0) { + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,1,r.size.y),cache.drop_position_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x+r.size.x-1,r.pos.y,1,r.size.y),cache.drop_position_color); + + } + + if (drop_mode_section==1 || drop_mode_section==0) { + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y+r.size.y,r.size.x,1),cache.drop_position_color); + } } Color col=p_item->cells[i].custom_color?p_item->cells[i].color:get_color( p_item->cells[i].selected?"font_color_selected":"font_color"); @@ -1280,6 +1311,25 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& } +int Tree::_count_selected_items(TreeItem* p_from) const { + + int count=0; + for(int i=0;iis_selected(i)) + count++; + } + + if (p_from->get_children()) { + count+=_count_selected_items(p_from->get_children()); + } + + if (p_from->get_next()) { + count+=_count_selected_items(p_from->get_next()); + } + + return count; + +} void Tree::select_single_item(TreeItem *p_selected,TreeItem *p_current,int p_col,TreeItem *p_prev,bool *r_in_range) { TreeItem::Cell &selected_cell=p_selected->cells[p_col]; @@ -1548,7 +1598,15 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ select_single_item( p_item, root, col,selected_item,&inrange ); } else { - select_single_item( p_item, root, col ); + + int icount = _count_selected_items(root); + + if (select_mode==SELECT_MULTI && icount>1) { + single_select_defer=p_item; + single_select_defer_column=col; + } else { + select_single_item( p_item, root, col ); + } } //if (!c.selected && select_mode==SELECT_MULTI) { @@ -2159,6 +2217,31 @@ void Tree::_input_event(InputEvent p_event) { } + if (drop_mode_flags && root) { + + Point2 mpos=Point2(b.x,b.y); + mpos -= cache.bg->get_offset(); + mpos.y-=_get_title_button_height(); + if (mpos.y>=0) { + + if (h_scroll->is_visible()) + mpos.x+=h_scroll->get_val(); + if (v_scroll->is_visible()) + mpos.y+=v_scroll->get_val(); + + int col,h,section; + TreeItem *it = _find_item_at_pos(root,mpos,col,h,section); + + if (it!=drop_mode_over || section!=drop_mode_section) { + drop_mode_over=it; + drop_mode_section=section; + update(); + } + } + } + + + if (cache.hover_type!=old_hover || cache.hover_index!=old_index) { update(); } @@ -2211,6 +2294,12 @@ void Tree::_input_event(InputEvent p_event) { if (b.button_index==BUTTON_LEFT) { + + if (single_select_defer) { + select_single_item( single_select_defer, root, single_select_defer_column ); + single_select_defer=NULL; + } + range_click_timer->stop(); if (pressing_for_editor) { @@ -2522,6 +2611,15 @@ void Tree::_notification(int p_what) { update_cache();; } + if (p_what==NOTIFICATION_DRAG_END) { + + drop_mode_flags=0; + update(); + } + if (p_what==NOTIFICATION_DRAG_BEGIN) { + + single_select_defer=NULL; + } if (p_what==NOTIFICATION_FIXED_PROCESS) { if (drag_touching) { @@ -3135,7 +3233,7 @@ void Tree::_do_incr_search(const String& p_add) { } -TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_column,int &h) const { +TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_column,int &h,int §ion) const { Point2 pos = p_pos; @@ -3145,15 +3243,33 @@ TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_co h = compute_item_height(p_item)+cache.vseparation;; if (pos.y=(h*3/4)) { + section=1; + } else { + section=0; + } + for(int i=0;iget_offset(); + pos.y-=_get_title_button_height(); + if (pos.y<0) + return -1; + + if (h_scroll->is_visible()) + pos.x+=h_scroll->get_val(); + if (v_scroll->is_visible()) + pos.y+=v_scroll->get_val(); + + int col,h,section; + TreeItem *it = _find_item_at_pos(root,pos,col,h,section); + + if (it) { + return col; + } + } + + return -1; + +} + +int Tree::get_drop_section_at_pos(const Point2& p_pos) const { + + if (root) { + + Point2 pos=p_pos; + pos -= cache.bg->get_offset(); + pos.y-=_get_title_button_height(); + if (pos.y<0) + return -100; + + if (h_scroll->is_visible()) + pos.x+=h_scroll->get_val(); + if (v_scroll->is_visible()) + pos.y+=v_scroll->get_val(); + + int col,h,section; + TreeItem *it = _find_item_at_pos(root,pos,col,h,section); + + if (it) { + return section; + } + } + + return -100; + +} +TreeItem* Tree::get_item_at_pos(const Point2& p_pos) const { + + + if (root) { + + Point2 pos=p_pos; + pos -= cache.bg->get_offset(); + pos.y-=_get_title_button_height(); + if (pos.y<0) + return NULL; + + if (h_scroll->is_visible()) + pos.x+=h_scroll->get_val(); + if (v_scroll->is_visible()) + pos.y+=v_scroll->get_val(); + + int col,h,section; + TreeItem *it = _find_item_at_pos(root,pos,col,h,section); + + if (it) { + + return it; + } + } + + return NULL; + +} + String Tree::get_tooltip(const Point2& p_pos) const { if (root) { @@ -3199,8 +3397,8 @@ String Tree::get_tooltip(const Point2& p_pos) const { if (v_scroll->is_visible()) pos.y+=v_scroll->get_val(); - int col,h; - TreeItem *it = _find_item_at_pos(root,pos,col,h); + int col,h,section; + TreeItem *it = _find_item_at_pos(root,pos,col,h,section); if (it) { @@ -3241,6 +3439,20 @@ void Tree::set_value_evaluator(ValueEvaluator *p_evaluator) { evaluator = p_evaluator; } +void Tree::set_drop_mode_flags(int p_flags) { + drop_mode_flags=p_flags; + if (drop_mode_flags==0) { + drop_mode_over=NULL; + } + + update(); +} + +int Tree::get_drop_mode_flags() const { + + return drop_mode_flags; +} + void Tree::_bind_methods() { ObjectTypeDB::bind_method(_MD("_range_click_timeout"),&Tree::_range_click_timeout); @@ -3273,6 +3485,8 @@ void Tree::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_edited_column"),&Tree::get_edited_column); ObjectTypeDB::bind_method(_MD("get_custom_popup_rect"),&Tree::get_custom_popup_rect); ObjectTypeDB::bind_method(_MD("get_item_area_rect","item:TreeItem","column"),&Tree::_get_item_rect,DEFVAL(-1)); + ObjectTypeDB::bind_method(_MD("get_item_at_pos:TreeItem","pos"),&Tree::get_item_at_pos); + ObjectTypeDB::bind_method(_MD("get_column_at_pos","pos"),&Tree::get_column_at_pos); ObjectTypeDB::bind_method(_MD("ensure_cursor_is_visible"),&Tree::ensure_cursor_is_visible); @@ -3286,6 +3500,9 @@ void Tree::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_hide_folding","hide"),&Tree::set_hide_folding); ObjectTypeDB::bind_method(_MD("is_folding_hidden"),&Tree::is_folding_hidden); + ObjectTypeDB::bind_method(_MD("set_drop_mode_flags","flags"),&Tree::set_drop_mode_flags); + ObjectTypeDB::bind_method(_MD("get_drop_mode_flags"),&Tree::get_drop_mode_flags); + ADD_SIGNAL( MethodInfo("item_selected")); ADD_SIGNAL( MethodInfo("cell_selected")); @@ -3300,6 +3517,11 @@ void Tree::_bind_methods() { BIND_CONSTANT( SELECT_SINGLE ); BIND_CONSTANT( SELECT_ROW ); BIND_CONSTANT( SELECT_MULTI ); + + BIND_CONSTANT( DROP_MODE_DISABLED ); + BIND_CONSTANT( DROP_MODE_ON_ITEM ); + BIND_CONSTANT( DROP_MODE_INBETWEEN ); + } Tree::Tree() { @@ -3381,6 +3603,11 @@ Tree::Tree() { hide_folding=false; evaluator=NULL; + + drop_mode_flags=0; + drop_mode_over=NULL; + drop_mode_section=0; + single_select_defer=NULL; } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 75bcfd8e336..43a913392e4 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -79,7 +79,9 @@ friend class Tree; bool custom_color; Color color; bool custom_bg_color; + bool custom_bg_outline; Color bg_color; + Variant meta; String tooltip; @@ -226,7 +228,7 @@ public: Color get_custom_color(int p_column) const; void clear_custom_color(int p_column); - void set_custom_bg_color(int p_column,const Color& p_color); + void set_custom_bg_color(int p_column, const Color& p_color, bool p_bg_outline=false); void clear_custom_bg_color(int p_column); Color get_custom_bg_color(int p_column) const; @@ -257,6 +259,12 @@ public: SELECT_MULTI }; + enum DropModeFlags { + DROP_MODE_DISABLED=0, + DROP_MODE_ON_ITEM=1, + DROP_MODE_INBETWEEN=2 + }; + private: friend class TreeItem; @@ -265,6 +273,11 @@ friend class TreeItem; TreeItem *selected_item; TreeItem *edited_item; + TreeItem *drop_mode_over; + int drop_mode_section; + + TreeItem *single_select_defer; + int single_select_defer_column; int pressed_button; bool pressing_for_editor; @@ -289,6 +302,8 @@ friend class TreeItem; int blocked; + int drop_mode_flags; + struct ColumnInfo { int min_width; @@ -361,6 +376,8 @@ friend class TreeItem; Color font_color; Color font_color_selected; Color guide_color; + Color drop_position_color; + int hseparation; int vseparation; int item_margin; @@ -405,7 +422,7 @@ friend class TreeItem; TreeItem* _search_item_text(TreeItem *p_at, const String& p_find,int *r_col,bool p_selectable,bool p_backwards=false); - TreeItem* _find_item_at_pos(TreeItem *p_current, const Point2& p_pos,int& r_column,int &h) const; + TreeItem* _find_item_at_pos(TreeItem *p_current, const Point2& p_pos, int& r_column, int &h, int §ion) const; /* float drag_speed; float drag_accum; @@ -426,6 +443,8 @@ friend class TreeItem; ValueEvaluator *evaluator; + int _count_selected_items(TreeItem* p_from) const; + protected: static void _bind_methods(); @@ -433,10 +452,16 @@ protected: Object* _create_item(Object *p_parent) { return create_item(p_parent->cast_to() ); } TreeItem *_get_next_selected(Object *p_item) { return get_next_selected(p_item->cast_to() ); } Rect2 _get_item_rect(Object *p_item,int p_column) const { return get_item_rect(p_item->cast_to(),p_column ); } + + public: virtual String get_tooltip(const Point2& p_pos) const; + TreeItem* get_item_at_pos(const Point2& p_pos) const; + int get_column_at_pos(const Point2& p_pos) const; + int get_drop_section_at_pos(const Point2& p_pos) const; + void clear(); TreeItem* create_item(TreeItem *p_parent=0); @@ -486,6 +511,9 @@ public: void set_hide_folding(bool p_hide); bool is_folding_hidden() const; + void set_drop_mode_flags(int p_flags); + int get_drop_mode_flags() const; + void set_value_evaluator(ValueEvaluator *p_evaluator); Tree(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 8475ca0b39e..da14fa11111 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2095,6 +2095,8 @@ void Node::_bind_methods() { BIND_CONSTANT( NOTIFICATION_PAUSED ); BIND_CONSTANT( NOTIFICATION_UNPAUSED ); BIND_CONSTANT( NOTIFICATION_INSTANCED ); + BIND_CONSTANT( NOTIFICATION_DRAG_BEGIN ); + BIND_CONSTANT( NOTIFICATION_DRAG_END ); diff --git a/scene/main/node.h b/scene/main/node.h index 560a2e588a5..83086bb0cf2 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -180,6 +180,8 @@ public: NOTIFICATION_PARENTED=18, NOTIFICATION_UNPARENTED=19, NOTIFICATION_INSTANCED=20, + NOTIFICATION_DRAG_BEGIN=21, + NOTIFICATION_DRAG_END=22, }; /* NODE/TREE */ diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 5017ae61ff6..265ee53e587 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -985,6 +985,16 @@ void Viewport::_propagate_enter_world(Node *p_node) { } } +void Viewport::_propagate_viewport_notification(Node* p_node,int p_what) { + + p_node->notification(p_what); + for(int i=0;iget_child_count();i++) { + Node *c = p_node->get_child(i); + if (c->cast_to()) + continue; + _propagate_viewport_notification(c,p_what); + } +} void Viewport::_propagate_exit_world(Node *p_node) { @@ -1676,6 +1686,9 @@ void Viewport::_gui_input_event(InputEvent p_event) { if (p_event.mouse_button.button_index==BUTTON_LEFT) { gui.drag_accum=Vector2(); gui.drag_attempted=false; + if (gui.drag_data.get_type()!=Variant::NIL) { + _propagate_viewport_notification(this,NOTIFICATION_DRAG_END); + } gui.drag_data=Variant(); } @@ -1736,6 +1749,7 @@ void Viewport::_gui_input_event(InputEvent p_event) { gui.mouse_over->drop_data(pos,gui.drag_data); gui.drag_data=Variant(); + _propagate_viewport_notification(this,NOTIFICATION_DRAG_END); //change mouse accordingly } @@ -1759,6 +1773,7 @@ void Viewport::_gui_input_event(InputEvent p_event) { } if (gui.drag_data.get_type()!=Variant::NIL && p_event.mouse_button.button_index==BUTTON_LEFT) { + _propagate_viewport_notification(this,NOTIFICATION_DRAG_END); gui.drag_data=Variant(); //always clear } @@ -1790,6 +1805,10 @@ void Viewport::_gui_input_event(InputEvent p_event) { gui.mouse_focus=NULL; } gui.drag_attempted=true; + if (gui.drag_data.get_type()!=Variant::NIL) { + + _propagate_viewport_notification(this,NOTIFICATION_DRAG_BEGIN); + } } } @@ -1814,6 +1833,8 @@ void Viewport::_gui_input_event(InputEvent p_event) { } } + + if (over!=gui.mouse_over) { if (gui.mouse_over) @@ -1896,7 +1917,15 @@ void Viewport::_gui_input_event(InputEvent p_event) { if (gui.drag_data.get_type()!=Variant::NIL && p_event.mouse_motion.button_mask&BUTTON_MASK_LEFT) { - /*bool can_drop =*/ over->can_drop_data(pos,gui.drag_data); + + bool can_drop = over->can_drop_data(pos,gui.drag_data); + + if (!can_drop) { + OS::get_singleton()->set_cursor_shape( OS::CURSOR_FORBIDDEN ); + } else { + OS::get_singleton()->set_cursor_shape( OS::CURSOR_CAN_DROP ); + + } //change mouse accordingly i guess } @@ -2367,6 +2396,9 @@ bool Viewport::is_input_disabled() const { return disable_input; } +Variant Viewport::gui_get_drag_data() const { + return gui.drag_data; +} void Viewport::_bind_methods() { @@ -2452,6 +2484,7 @@ void Viewport::_bind_methods() { ObjectTypeDB::bind_method(_MD("warp_mouse","to_pos"), &Viewport::warp_mouse); ObjectTypeDB::bind_method(_MD("gui_has_modal_stack"), &Viewport::gui_has_modal_stack); + ObjectTypeDB::bind_method(_MD("gui_get_drag_data:Variant"), &Viewport::gui_get_drag_data); ObjectTypeDB::bind_method(_MD("set_disable_input","disable"), &Viewport::set_disable_input); ObjectTypeDB::bind_method(_MD("is_input_disabled"), &Viewport::is_input_disabled); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index afabd499a97..6107cf570f3 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -159,6 +159,7 @@ friend class RenderTargetTexture; void _propagate_enter_world(Node *p_node); void _propagate_exit_world(Node *p_node); + void _propagate_viewport_notification(Node *p_node, int p_what); void _update_stretch_transform(); @@ -361,6 +362,7 @@ public: bool gui_has_modal_stack() const; + Variant gui_get_drag_data() const; Viewport(); ~Viewport(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 727c8eee298..ade56c4f49e 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -655,6 +655,7 @@ void make_default_theme() { t->set_color("selection_color","Tree", Color(0.1,0.1,1,0.8) ); t->set_color("cursor_color","Tree", Color(0,0,0) ); t->set_color("guide_color","Tree", Color(0,0,0,0.1) ); + t->set_color("drop_position_color","Tree", Color(1,0.3,0.2) ); t->set_constant("hseparation","Tree",4); t->set_constant("vseparation","Tree",4); diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index 2736a75ac30..8e5087b4057 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -523,7 +523,7 @@ void EditorNode::save_resource(const Ref& p_resource) { } } -void EditorNode::save_resource_as(const Ref& p_resource) { +void EditorNode::save_resource_as(const Ref& p_resource,const String& p_at_path) { file->set_mode(EditorFileDialog::MODE_SAVE_FILE); bool relpaths = (p_resource->has_meta("__editor_relpaths__") && p_resource->get_meta("__editor_relpaths__").operator bool()); @@ -546,7 +546,21 @@ void EditorNode::save_resource_as(const Ref& p_resource) { } //file->set_current_path(current_path); - if (p_resource->get_path()!="") { + + if (p_at_path!=String()) { + + file->set_current_dir(p_at_path); + if (p_resource->get_path().is_resource_file()) { + file->set_current_file(p_resource->get_path().get_file()); + } else { + if (extensions.size()) { + file->set_current_file("new_"+p_resource->get_type().to_lower()+"."+preferred.front()->get().to_lower()); + } else { + file->set_current_file(String()); + } + } + } else if (p_resource->get_path()!="") { + file->set_current_path(p_resource->get_path()); if (extensions.size()) { String ext=p_resource->get_path().extension().to_lower(); @@ -4881,6 +4895,114 @@ void EditorNode::remove_control_from_dock(Control* p_control) { _update_dock_slots_visibility(); } +Variant EditorNode::drag_resource(const Ref& p_res,Control* p_from) { + + + Control *drag_control = memnew( Control ); + TextureFrame *drag_preview = memnew( TextureFrame ); + Label* label=memnew( Label ); + + Ref preview; + + { + //todo make proper previews + Ref pic = gui_base->get_icon("FileBig","EditorIcons"); + Image img = pic->get_data(); + img.resize(48,48); //meh + Ref resized_pic = Ref( memnew( ImageTexture) ); + resized_pic->create_from_image(img); + preview=resized_pic; + } + + drag_preview->set_texture(preview); + drag_control->add_child(drag_preview); + if (p_res->get_path().is_resource_file()) { + label->set_text(p_res->get_path().get_file()); + } else if (p_res->get_name()!="") { + label->set_text(p_res->get_name()); + } else { + label->set_text(p_res->get_type()); + + } + + drag_control->add_child(label); + + p_from->set_drag_preview(drag_control); //wait until it enters scene + + label->set_pos( Point2((preview->get_width()-label->get_minimum_size().width)/2,preview->get_height()) ); + + Dictionary drag_data; + drag_data["type"]="resource"; + drag_data["resource"]=p_res; + drag_data["from"]=p_from; + + + return drag_data; + +} + +Variant EditorNode::drag_files(const Vector& p_files, Control *p_from){ + + VBoxContainer *files = memnew( VBoxContainer ); + + int max_files=6; + + for(int i=0;iset_text(p_files[i].get_file()); + files->add_child(label); + } + + if (p_files.size()>max_files) { + + Label* label=memnew( Label ); + label->set_text(itos(p_files.size()-max_files)+" "+TTR("More File(s)")); + files->add_child(label); + + } + Dictionary drag_data; + drag_data["type"]="files"; + drag_data["files"]=p_files; + drag_data["from"]=p_from; + + p_from->set_drag_preview(files); //wait until it enters scene + + return drag_data; + +} + +Variant EditorNode::drag_files_and_dirs(const Vector& p_files, Control *p_from){ + + VBoxContainer *files = memnew( VBoxContainer ); + + int max_files=6; + + for(int i=0;iset_text(p_files[i].get_file()); + files->add_child(label); + } + + if (p_files.size()>max_files) { + + Label* label=memnew( Label ); + label->set_text(itos(p_files.size()-max_files)+" "+TTR("More File(s) and/or Directory(s)")); + files->add_child(label); + + } + Dictionary drag_data; + drag_data["type"]="files_and_dirs"; + drag_data["files"]=p_files; + drag_data["from"]=p_from; + + p_from->set_drag_preview(files); //wait until it enters scene + + return drag_data; + +} + void EditorNode::_bind_methods() { diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h index c3b7a817c54..b3faa21cf3c 100644 --- a/tools/editor/editor_node.h +++ b/tools/editor/editor_node.h @@ -584,6 +584,9 @@ public: static void add_editor_plugin(EditorPlugin *p_editor); static void remove_editor_plugin(EditorPlugin *p_editor); + + + void add_control_to_dock(DockSlot p_slot,Control* p_control); void remove_control_from_dock(Control* p_control); @@ -599,7 +602,7 @@ public: void save_resource_in_path(const Ref& p_resource,const String& p_path); void save_resource(const Ref& p_resource); - void save_resource_as(const Ref& p_resource); + void save_resource_as(const Ref& p_resource, const String &p_at_path=String()); static bool has_unsaved_changes() { return singleton->unsaved_cache; } @@ -696,6 +699,11 @@ public: void hide_bottom_panel(); void remove_bottom_panel_item(Control *p_item); + Variant drag_resource(const Ref& p_res,Control* p_from); + Variant drag_files(const Vector& p_files,Control* p_from); + Variant drag_files_and_dirs(const Vector& p_files,Control* p_from); + + EditorNode(); ~EditorNode(); void get_singleton(const char* arg1, bool arg2); diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp index 7ece65e75a3..0213dbda594 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.cpp +++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp @@ -572,6 +572,8 @@ bool CanvasItemEditor::_select(CanvasItem *item, Point2 p_click_pos, bool p_appe _append_canvas_item(item); viewport->update(); + return true; + } else { //regular selection diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index 7c5aca579c7..6f809101502 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -43,7 +43,8 @@ #include "array_property_edit.h" #include "editor_help.h" #include "scene/resources/packed_scene.h" - +#include "scene/main/viewport.h" +#include "editor_file_system.h" void CustomPropertyEditor::_notification(int p_what) { @@ -224,6 +225,10 @@ void CustomPropertyEditor::_menu_option(int p_which) { } +void CustomPropertyEditor::hide_menu() { + menu->hide(); +} + Variant CustomPropertyEditor::get_variant() const { return v; @@ -2257,6 +2262,179 @@ void PropertyEditor::_check_reload_status(const String&p_name, TreeItem* item) { } } + + +bool PropertyEditor::_is_drop_valid(const Dictionary& p_drag_data, const Dictionary& p_item_data) const { + + Dictionary d = p_item_data; + + if (d.has("type")) { + + int type = d["type"]; + if (type==Variant::OBJECT && d.has("hint") && d.has("hint_text") && int(d["hint"])==PROPERTY_HINT_RESOURCE_TYPE) { + + + String allowed_type=d["hint_text"]; + + Dictionary drag_data = p_drag_data; + if (drag_data.has("type") && String(drag_data["type"])=="resource") { + Ref res = drag_data["resource"]; + for(int i=0;iget_type(),at)) { + return true; + } + } + + } + if (drag_data.has("type") && String(drag_data["type"])=="files") { + + Vector files = drag_data["files"]; + + if (files.size()==1) { + String file = files[0]; + String ftype = EditorFileSystem::get_singleton()->get_file_type(file); + if (ftype!="") { + + for(int i=0;igui_get_drag_data(),p_at->get_metadata(0))) + p_at->set_custom_bg_color(1,Color(0.7,0.5,0.2),true); + + if (p_at->get_children()) { + _mark_drop_fields(p_at->get_children()); + } + + if (p_at->get_next()) { + _mark_drop_fields(p_at->get_next()); + } +} + +Variant PropertyEditor::get_drag_data_fw(const Point2& p_point,Control* p_from) { + + TreeItem *item = tree->get_item_at_pos(p_point); + if (!item) + return Variant(); + + int col = tree->get_column_at_pos(p_point); + if (col!=1) + return Variant(); + + + Dictionary d = item->get_metadata(0); + if (!d.has("name")) + return Variant(); + + Variant val = obj->get(d["name"]); + + if (val.get_type()==Variant::OBJECT) { + RES res = val; + if (res.is_valid()) { + + custom_editor->hide_menu(); + return EditorNode::get_singleton()->drag_resource(res,p_from); + } + } + + return Variant(); +} + +bool PropertyEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const{ + + TreeItem *item = tree->get_item_at_pos(p_point); + if (!item) + return false; + + int col = tree->get_column_at_pos(p_point); + if (col!=1) + return false; + + return _is_drop_valid(p_data,item->get_metadata(0)); + +} +void PropertyEditor::drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from){ + + TreeItem *item = tree->get_item_at_pos(p_point); + if (!item) + return; + + int col = tree->get_column_at_pos(p_point); + if (col!=1) + return; + + if (!_is_drop_valid(p_data,item->get_metadata(0))) + return; + + Dictionary d = item->get_metadata(0); + + if (!d.has("name")) + return; + + String name=d["name"]; + + Dictionary drag_data = p_data; + if (drag_data.has("type") && String(drag_data["type"])=="resource") { + Ref res = drag_data["resource"]; + if (res.is_valid()) { + _edit_set(name,res); + return; + } + } + + if (drag_data.has("type") && String(drag_data["type"])=="files") { + + Vector files = drag_data["files"]; + + if (files.size()==1) { + String file = files[0]; + RES res = ResourceLoader::load(file); + if (res.is_valid()) { + _edit_set(name,res); + return; + } + } + } +} + + +void PropertyEditor::_clear_drop_fields(TreeItem* p_at) { + + Dictionary d = p_at->get_metadata(0); + + if (d.has("type")) { + + int type = d["type"]; + if (type==Variant::OBJECT) { + p_at->clear_custom_bg_color(1); + } + + } + + if (p_at->get_children()) { + _clear_drop_fields(p_at->get_children()); + } + + if (p_at->get_next()) { + _clear_drop_fields(p_at->get_next()); + } +} + void PropertyEditor::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { @@ -2270,6 +2448,20 @@ void PropertyEditor::_notification(int p_what) { } + if (p_what==NOTIFICATION_DRAG_BEGIN) { + + if (is_visible() && tree->get_root()) { + _mark_drop_fields(tree->get_root()); + } + } + + if (p_what==NOTIFICATION_DRAG_END) { + if (is_visible() && tree->get_root()) { + _clear_drop_fields(tree->get_root()); + } + + } + if (p_what==NOTIFICATION_FIXED_PROCESS) { @@ -3661,6 +3853,10 @@ void PropertyEditor::_bind_methods() { ObjectTypeDB::bind_method( "_filter_changed",&PropertyEditor::_filter_changed); ObjectTypeDB::bind_method( "update_tree",&PropertyEditor::update_tree); + ObjectTypeDB::bind_method(_MD("get_drag_data_fw"), &PropertyEditor::get_drag_data_fw); + ObjectTypeDB::bind_method(_MD("can_drop_data_fw"), &PropertyEditor::can_drop_data_fw); + ObjectTypeDB::bind_method(_MD("drop_data_fw"), &PropertyEditor::drop_data_fw); + 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") ) ); ADD_SIGNAL( MethodInfo("property_keyed",PropertyInfo( Variant::STRING, "property"))); @@ -3778,6 +3974,8 @@ PropertyEditor::PropertyEditor() { tree->connect("item_edited", this,"_item_edited",varray(),CONNECT_DEFERRED); tree->connect("cell_selected", this,"_item_selected"); + tree->set_drag_forwarding(this); + set_fixed_process(true); custom_editor = memnew( CustomPropertyEditor ); diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h index 693bfb3efd2..ac58011d436 100644 --- a/tools/editor/property_editor.h +++ b/tools/editor/property_editor.h @@ -137,6 +137,10 @@ protected: static void _bind_methods(); public: + + + void hide_menu(); + Variant get_variant() const; String get_name() const; @@ -219,6 +223,15 @@ class PropertyEditor : public Control { void _filter_changed(const String& p_text); + void _mark_drop_fields(TreeItem* p_at); + void _clear_drop_fields(TreeItem* p_at); + + bool _is_drop_valid(const Dictionary& p_drag_data, const Dictionary& p_item_data) const; + Variant get_drag_data_fw(const Point2& p_point,Control* p_from); + bool can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const; + void drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from); + + UndoRedo *undo_redo; protected: diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp index 113d18ea731..252e7c890c8 100644 --- a/tools/editor/scene_tree_dock.cpp +++ b/tools/editor/scene_tree_dock.cpp @@ -40,6 +40,9 @@ #include "tools/editor/plugins/animation_player_editor_plugin.h" #include "animation_editor.h" + + + void SceneTreeDock::_unhandled_key_input(InputEvent p_event) { uint32_t sc = p_event.key.get_scancode_with_modifiers(); @@ -943,16 +946,37 @@ bool SceneTreeDock::_validate_no_foreign() { void SceneTreeDock::_node_reparent(NodePath p_path,bool p_keep_global_xform) { - Node *node = scene_tree->get_selected(); - ERR_FAIL_COND(!node); - ERR_FAIL_COND(node==edited_scene); Node *new_parent = scene_root->get_node(p_path); ERR_FAIL_COND(!new_parent); + //ok all valid + + List selection = editor_selection->get_selected_node_list(); + + if (selection.empty()) + return; //nothing to reparent + + Vector nodes; + + for(List::Element *E=selection.front();E;E=E->next()) { + nodes.push_back(E->get()); + } + + _do_reparent(new_parent,-1,nodes,p_keep_global_xform); + +} + + +void SceneTreeDock::_do_reparent(Node* p_new_parent,int p_position_in_parent,Vector p_nodes,bool p_keep_global_xform) { + + + Node *new_parent = p_new_parent; + ERR_FAIL_COND(!new_parent); + Node *validate=new_parent; while(validate) { - if (editor_selection->is_selected(validate)) { + if (p_nodes.find(validate)!=-1) { ERR_EXPLAIN("Selection changed at some point.. can't reparent"); ERR_FAIL(); return; @@ -964,20 +988,20 @@ void SceneTreeDock::_node_reparent(NodePath p_path,bool p_keep_global_xform) { List selection = editor_selection->get_selected_node_list(); - if (selection.empty()) + if (p_nodes.size()==0) return; //nothing to reparent //sort by tree order, so re-adding is easy - selection.sort_custom(); + p_nodes.sort_custom(); editor_data->get_undo_redo().create_action(TTR("Reparent Node")); List > path_renames; - for(List::Element *E=selection.front();E;E=E->next()) { + for(int ni=0;niget(); + Node *node = p_nodes[ni]; fill_path_renames(node,new_parent,&path_renames); @@ -994,6 +1018,9 @@ void SceneTreeDock::_node_reparent(NodePath p_path,bool p_keep_global_xform) { editor_data->get_undo_redo().add_do_method(node->get_parent(),"remove_child",node); editor_data->get_undo_redo().add_do_method(new_parent,"add_child",node); + if (p_position_in_parent>=0) + editor_data->get_undo_redo().add_do_method(new_parent,"move_child",node,p_position_in_parent+ni); + ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); String new_name = new_parent->validate_child_name(node->get_name()); editor_data->get_undo_redo().add_do_method(sed,"live_debug_reparent_node",edited_scene->get_path_to(node),edited_scene->get_path_to(new_parent),new_name,-1); @@ -1030,9 +1057,9 @@ void SceneTreeDock::_node_reparent(NodePath p_path,bool p_keep_global_xform) { - for(List::Element *E=selection.front();E;E=E->next()) { + for(int ni=0;niget(); + Node *node = p_nodes[ni]; List owned; node->get_owned_by(node->get_owner(),&owned); @@ -1078,7 +1105,6 @@ void SceneTreeDock::_node_reparent(NodePath p_path,bool p_keep_global_xform) { //node->set_owner(owner); } - void SceneTreeDock::_script_created(Ref