diff options
Diffstat (limited to 'scene')
31 files changed, 2078 insertions, 103 deletions
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index e9378b1d02..05136de5d6 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -123,26 +123,15 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2& p_offset,float p_sca Point2 new_ofs = ((orig_offset+p_offset)*motion_scale)*p_scale+motion_offset; if (mirroring.x) { - - while( new_ofs.x>=0) { - new_ofs.x -= mirroring.x*p_scale; - } - while(new_ofs.x < -mirroring.x*p_scale) { - new_ofs.x += mirroring.x*p_scale; - } + double den = mirroring.x*p_scale; + new_ofs.x -= den*ceil(new_ofs.x/den); } if (mirroring.y) { - - while( new_ofs.y>=0) { - new_ofs.y -= mirroring.y*p_scale; - } - while(new_ofs.y < -mirroring.y*p_scale) { - new_ofs.y += mirroring.y*p_scale; - } + double den = mirroring.y*p_scale; + new_ofs.y -= den*ceil(new_ofs.y/den); } - set_pos(new_ofs); set_scale(Vector2(1,1)*p_scale); diff --git a/scene/3d/immediate_geometry.cpp b/scene/3d/immediate_geometry.cpp index 4964582be4..e83fa69b4f 100644 --- a/scene/3d/immediate_geometry.cpp +++ b/scene/3d/immediate_geometry.cpp @@ -149,7 +149,7 @@ void ImmediateGeometry::add_sphere(int p_lats,int p_lons,float p_radius) { void ImmediateGeometry::_bind_methods() { - ObjectTypeDB::bind_method(_MD("begin","primitive","texture:Texture"),&ImmediateGeometry::begin); + ObjectTypeDB::bind_method(_MD("begin","primitive","texture:Texture"),&ImmediateGeometry::begin,DEFVAL(Ref<Texture>())); ObjectTypeDB::bind_method(_MD("set_normal","normal"),&ImmediateGeometry::set_normal); ObjectTypeDB::bind_method(_MD("set_tangent","tangent"),&ImmediateGeometry::set_tangent); ObjectTypeDB::bind_method(_MD("set_color","color"),&ImmediateGeometry::set_color); diff --git a/scene/3d/immediate_geometry.h b/scene/3d/immediate_geometry.h index 28b5735ca8..c1cc4f87d5 100644 --- a/scene/3d/immediate_geometry.h +++ b/scene/3d/immediate_geometry.h @@ -38,6 +38,8 @@ class ImmediateGeometry : public GeometryInstance { RID im; + //a list of texures drawn need to be kept, to avoid references + // in VisualServer from becoming invalid if the texture is no longer used List<Ref<Texture> > cached_textures; bool empty; AABB aabb; @@ -47,7 +49,7 @@ protected: public: - void begin(Mesh::PrimitiveType p_primitive,const Ref<Texture>& p_texture); + void begin(Mesh::PrimitiveType p_primitive,const Ref<Texture>& p_texture=Ref<Texture>()); void set_normal(const Vector3& p_normal); void set_tangent(const Plane& p_tangent); void set_color(const Color& p_color); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 699062fee4..41d766c1b7 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -416,7 +416,7 @@ Ref<ShortCut> BaseButton:: get_shortcut() const { void BaseButton::_unhandled_input(InputEvent p_event) { - if (!is_disabled() && is_visible() && p_event.is_pressed() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) { + if (!is_disabled() && is_visible() && p_event.is_pressed() && !p_event.is_echo() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) { if (get_viewport()->get_modal_stack_top() && !get_viewport()->get_modal_stack_top()->is_a_parent_of(this)) return; //ignore because of modal window diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 06f8c27957..b69646432e 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -140,14 +140,13 @@ void ColorPicker::_value_changed(double) { if (updating) return; - for(int i=0;i<3;i++) { + for(int i=0;i<4;i++) { color.components[i] = scroll[i]->get_val()/(raw_mode_enabled?1.0:255.0); } - color.components[3] = scroll[3]->get_val()/255.0; set_color(color); - c_text->set_text(color.to_html(edit_alpha && color.a<1)); + _update_text_value(); emit_signal("color_changed",color); @@ -174,22 +173,16 @@ void ColorPicker::_update_color() { for(int i=0;i<4;i++) { scroll[i]->set_max(255); scroll[i]->set_step(0.01); - if (raw_mode_enabled && i != 3) + if (raw_mode_enabled) { + if (i == 3) + scroll[i]->set_max(1); scroll[i]->set_val(color.components[i]); - else - scroll[i]->set_val(color.components[i]*255); + } else { + scroll[i]->set_val(color.components[i] * 255); + } } - if (text_is_constructor) { - String t = "Color("+String::num(color.r)+","+String::num(color.g)+","+String::num(color.b); - if (edit_alpha && color.a<1) - t+=(","+String::num(color.a)+")") ; - else - t+=")"; - c_text->set_text(t); - } else { - c_text->set_text(color.to_html(edit_alpha && color.a<1)); - } + _update_text_value(); sample->update(); updating=false; @@ -262,6 +255,20 @@ bool ColorPicker::is_raw_mode() const { return raw_mode_enabled; } + +void ColorPicker::_update_text_value() { + if (text_is_constructor) { + String t = "Color("+String::num(color.r)+","+String::num(color.g)+","+String::num(color.b); + if (edit_alpha && color.a<1) + t+=(","+String::num(color.a)+")") ; + else + t+=")"; + c_text->set_text(t); + } else { + c_text->set_text(color.to_html(edit_alpha && color.a<1)); + } +} + void ColorPicker::_sample_draw() { sample->draw_rect(Rect2(Point2(),Size2(256,20)),color); } @@ -271,12 +278,12 @@ void ColorPicker::_hsv_draw(int p_wich,Control* c) if (!c) return; if (p_wich==0) { - int x=c->get_size().x*s; - int y=c->get_size().y-c->get_size().y*v; + int x = CLAMP(c->get_size().x * s, 0, c->get_size().x); + int y = CLAMP(c->get_size().y-c->get_size().y * v, 0, c->get_size().y); Color col = color; col.a=1; c->draw_line(Point2(x,0),Point2(x,c->get_size().y),col.inverted()); - c->draw_line(Point2(0,y),Point2(c->get_size().x,y),col.inverted()); + c->draw_line(Point2(0, y),Point2(c->get_size().x, y),col.inverted()); c->draw_line(Point2(x,y),Point2(x,y),Color(1,1,1),2); } else if (p_wich==1) { int y=c->get_size().y-c->get_size().y*h; @@ -408,11 +415,12 @@ void ColorPicker::_screen_pick_pressed() if (!screen) { screen=memnew( Control ); r->add_child(screen); + screen->set_as_toplevel(true); screen->set_area_as_parent_rect(); screen->connect("input_event",this,"_screen_input"); } screen->raise(); - screen->show(); + screen->show_modal(); r->queue_screen_capture(); } diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index b9ef1f1e2f..5e2cc57274 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -80,6 +80,7 @@ private: void _update_controls(); void _update_color(); void _update_presets(); + void _update_text_value(); void _text_type_toggled(); void _sample_draw(); void _hsv_draw(int p_wich,Control *c); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index fc27c0d24f..c08e10b9ff 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1865,7 +1865,7 @@ void Control::_modal_stack_remove() { } -void Control::_propagate_theme_changed(CanvasItem *p_at,Control *p_owner) { +void Control::_propagate_theme_changed(CanvasItem *p_at,Control *p_owner,bool p_assign) { Control *c = p_at->cast_to<Control>(); @@ -1884,15 +1884,30 @@ void Control::_propagate_theme_changed(CanvasItem *p_at,Control *p_owner) { if (c) { - c->data.theme_owner=p_owner; + if (p_assign) { + c->data.theme_owner=p_owner; + } c->_notification(NOTIFICATION_THEME_CHANGED); c->update(); } } + +void Control::_theme_changed() { + + _propagate_theme_changed(this,this,false); +} + void Control::set_theme(const Ref<Theme>& p_theme) { + if (data.theme==p_theme) + return; + + if (data.theme.is_valid()) { + data.theme->disconnect("changed",this,"_theme_changed"); + } + data.theme=p_theme; if (!p_theme.is_null()) { @@ -1909,6 +1924,9 @@ void Control::set_theme(const Ref<Theme>& p_theme) { } + if (data.theme.is_valid()) { + data.theme->connect("changed",this,"_theme_changed"); + } } @@ -2244,6 +2262,7 @@ void Control::set_rotation(float p_radians) { data.rotation=p_radians; update(); _notify_transform(); + _change_notify("rect/rotation"); } float Control::get_rotation() const{ @@ -2448,6 +2467,10 @@ void Control::_bind_methods() { ObjectTypeDB::bind_method(_MD("minimum_size_changed"), &Control::minimum_size_changed); + ObjectTypeDB::bind_method(_MD("_theme_changed"), &Control::_theme_changed); + + + ObjectTypeDB::bind_method(_MD("_font_changed"), &Control::_font_changed); BIND_VMETHOD(MethodInfo("_input_event",PropertyInfo(Variant::INPUT_EVENT,"event"))); diff --git a/scene/gui/control.h b/scene/gui/control.h index 830ebd1620..1337cbc4b9 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -172,7 +172,9 @@ private: float _get_range(int p_idx) const; float _s2a(float p_val, AnchorType p_anchor,float p_range) const; float _a2s(float p_val, AnchorType p_anchor,float p_range) const; - void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner); + void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign=true); + void _theme_changed(); + void _change_notify_margins(); void _update_minimum_size(); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index d335399caa..6b43425edc 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -475,7 +475,7 @@ void FileDialog::update_filters() { String flt=filters[i].get_slice(";",0).strip_edges(); String desc=filters[i].get_slice(";",1).strip_edges(); if (desc.length()) - filter->add_item(desc+" ( "+flt+" )"); + filter->add_item(String(XL_MESSAGE(desc))+" ( "+flt+" )"); else filter->add_item("( "+flt+" )"); } @@ -498,6 +498,16 @@ void FileDialog::add_filter(const String& p_filter) { } +void FileDialog::set_filters(const Vector<String>& p_filters){ + filters=p_filters; + update_filters(); + invalidate(); +} + +Vector<String> FileDialog::get_filters() const{ + return filters; +} + String FileDialog::get_current_dir() const { return dir->get_text(); @@ -686,6 +696,8 @@ void FileDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("clear_filters"),&FileDialog::clear_filters); ObjectTypeDB::bind_method(_MD("add_filter","filter"),&FileDialog::add_filter); + ObjectTypeDB::bind_method(_MD("set_filters","filters"),&FileDialog::set_filters); + ObjectTypeDB::bind_method(_MD("get_filters"),&FileDialog::get_filters); ObjectTypeDB::bind_method(_MD("get_current_dir"),&FileDialog::get_current_dir); ObjectTypeDB::bind_method(_MD("get_current_file"),&FileDialog::get_current_file); ObjectTypeDB::bind_method(_MD("get_current_path"),&FileDialog::get_current_path); @@ -722,6 +734,11 @@ void FileDialog::_bind_methods() { BIND_CONSTANT( ACCESS_USERDATA ); BIND_CONSTANT( ACCESS_FILESYSTEM ); + ADD_PROPERTY( PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"),_SCS("set_mode"),_SCS("get_mode") ); + ADD_PROPERTY( PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"),_SCS("set_access"),_SCS("get_access") ); + ADD_PROPERTY( PropertyInfo(Variant::STRING_ARRAY, "filters"),_SCS("set_filters"),_SCS("get_filters") ); + ADD_PROPERTY( PropertyInfo(Variant::BOOL, "show_hidden_files"),_SCS("set_show_hidden_files"),_SCS("is_showing_hidden_files") ); + } diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 1fcf8387ce..150b24cb3f 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -131,6 +131,8 @@ public: void clear_filters(); void add_filter(const String& p_filter); + void set_filters(const Vector<String>& p_filters); + Vector<String> get_filters() const; void set_enable_multiple_selection(bool p_enable); Vector<String> get_selected_files() const; diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 94001b2ac1..3705541865 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -559,7 +559,12 @@ Color GraphNode::get_connection_output_color(int p_idx) { void GraphNode::_input_event(const InputEvent& p_ev) { if (p_ev.type==InputEvent::MOUSE_BUTTON) { + + ERR_EXPLAIN("GraphNode must be the child of a GraphEdit node."); + ERR_FAIL_COND(get_parent_control() == NULL); + get_parent_control()->grab_focus(); + if(p_ev.mouse_button.pressed && p_ev.mouse_button.button_index==BUTTON_LEFT) { Vector2 mpos = Vector2(p_ev.mouse_button.x,p_ev.mouse_button.y); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index d63c483ef9..105d919338 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -191,7 +191,7 @@ void ItemList::set_item_disabled(int p_idx,bool p_disabled){ ERR_FAIL_INDEX(p_idx,items.size()); items[p_idx].disabled=p_disabled; - + update(); } @@ -225,7 +225,7 @@ void ItemList::select(int p_idx,bool p_single){ if (p_single || select_mode==SELECT_SINGLE) { - if (!items[p_idx].selectable) { + if (!items[p_idx].selectable || items[p_idx].disabled) { return; } @@ -237,7 +237,7 @@ void ItemList::select(int p_idx,bool p_single){ ensure_selected_visible=false; } else { - if (items[p_idx].selectable) { + if (items[p_idx].selectable && !items[p_idx].disabled) { items[p_idx].selected=true; } } @@ -336,6 +336,7 @@ void ItemList::clear(){ current=-1; ensure_selected_visible=false; update(); + shape_changed=true; defer_select_single=-1; } @@ -461,7 +462,6 @@ void ItemList::_input_event(const InputEvent& p_event) { pos.y+=scroll_bar->get_val(); int closest = -1; - int closest_dist=0x7FFFFFFF; for(int i=0;i<items.size();i++) { @@ -474,12 +474,6 @@ void ItemList::_input_event(const InputEvent& p_event) { closest=i; break; } - - float dist = rc.distance_to(pos); - if (dist<closest_dist) { - closest=i; - closest_dist=dist; - } } if (closest!=-1) { @@ -510,7 +504,7 @@ void ItemList::_input_event(const InputEvent& p_event) { } } else { - if (!mb.doubleclick && !mb.mod.command && select_mode==SELECT_MULTI && items[i].selectable && items[i].selected && p_event.mouse_button.button_index==BUTTON_LEFT) { + if (!mb.doubleclick && !mb.mod.command && select_mode==SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && p_event.mouse_button.button_index==BUTTON_LEFT) { defer_select_single=i; return; } @@ -546,6 +540,11 @@ void ItemList::_input_event(const InputEvent& p_event) { return; + } else { + Vector<int> sItems = get_selected_items(); + for(int i = 0; i < sItems.size(); i++) { + unselect(sItems[i]); + } } } if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==BUTTON_WHEEL_UP && p_event.mouse_button.pressed) { @@ -694,7 +693,7 @@ void ItemList::_input_event(const InputEvent& p_event) { if (select_mode==SELECT_MULTI && current>=0 && current<items.size()) { - if (items[current].selectable && !items[current].selected) { + if (items[current].selectable && !items[current].disabled && !items[current].selected) { select(current,false); emit_signal("multi_selected",current,true); } else if (items[current].selected) { @@ -823,7 +822,7 @@ void ItemList::_notification(int p_what) { } if (shape_changed) { - + float max_column_width = 0; //1- compute item minimum sizes @@ -914,11 +913,11 @@ void ItemList::_notification(int p_what) { if (i<items.size()-1) separators.push_back(ofs.y+max_h+vseparation/2); - + for(int j=i;j>=0 && col>0;j--, col--) { items[j].rect_cache.size.y = max_h; } - + ofs.x=0; ofs.y+=max_h+vseparation; col=0; @@ -1029,10 +1028,14 @@ void ItemList::_notification(int p_what) { draw_rect.size=adj.size; } + Color modulate=Color(1,1,1,1); + if (items[i].disabled) + modulate.a*=0.5; + if (items[i].icon_region.has_no_area()) - draw_texture_rect(items[i].icon, draw_rect ); + draw_texture_rect(items[i].icon, draw_rect,false,modulate ); else - draw_texture_rect_region(items[i].icon, draw_rect, items[i].icon_region); + draw_texture_rect_region(items[i].icon, draw_rect, items[i].icon_region,modulate); } @@ -1053,6 +1056,10 @@ void ItemList::_notification(int p_what) { else max_len=size.x; + Color modulate=items[i].selected?font_color_selected:font_color; + if (items[i].disabled) + modulate.a*=0.5; + if (icon_mode==ICON_MODE_TOP && max_text_lines>0) { int ss = items[i].text.length(); @@ -1090,7 +1097,7 @@ void ItemList::_notification(int p_what) { if (line>=max_text_lines) break; } - ofs+=font->draw_char(get_canvas_item(),text_ofs+Vector2(ofs+(max_len-line_size_cache[line])/2,line*(font_height+line_separation)).floor(),items[i].text[j],items[i].text[j+1],items[i].selected?font_color_selected:font_color); + ofs+=font->draw_char(get_canvas_item(),text_ofs+Vector2(ofs+(max_len-line_size_cache[line])/2,line*(font_height+line_separation)).floor(),items[i].text[j],items[i].text[j+1],modulate); } //special multiline mode @@ -1110,7 +1117,7 @@ void ItemList::_notification(int p_what) { text_ofs+=base_ofs; text_ofs+=items[i].rect_cache.pos; - draw_string(font,text_ofs,items[i].text,items[i].selected?font_color_selected:font_color,max_len+1); + draw_string(font,text_ofs,items[i].text,modulate,max_len+1); } @@ -1203,8 +1210,11 @@ String ItemList::get_tooltip(const Point2& p_pos) const { } void ItemList::sort_items_by_text() { + items.sort(); update(); + shape_changed=true; + if (select_mode==SELECT_SINGLE) { for(int i=0;i<items.size();i++) { if (items[i].selected) { @@ -1296,7 +1306,7 @@ void ItemList::_bind_methods(){ ObjectTypeDB::bind_method(_MD("remove_item","idx"),&ItemList::remove_item); ObjectTypeDB::bind_method(_MD("clear"),&ItemList::clear); - ObjectTypeDB::bind_method(_MD("sort_items_by_text"),&ItemList::clear); + ObjectTypeDB::bind_method(_MD("sort_items_by_text"),&ItemList::sort_items_by_text); ObjectTypeDB::bind_method(_MD("set_fixed_column_width","width"),&ItemList::set_fixed_column_width); ObjectTypeDB::bind_method(_MD("get_fixed_column_width"),&ItemList::get_fixed_column_width); @@ -1375,4 +1385,3 @@ ItemList::ItemList() { ItemList::~ItemList() { } - diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 0431d824fa..4c025e92df 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -356,6 +356,21 @@ int Label::get_line_count() const { return line_count; } +int Label::get_visible_line_count() const { + + int line_spacing = get_constant("line_spacing"); + int font_h = get_font("font")->get_height()+line_spacing; + int lines_visible = (get_size().y+line_spacing)/font_h; + + if (lines_visible > line_count) + lines_visible = line_count; + + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) + lines_visible = max_lines_visible; + + return lines_visible; +} + void Label::regenerate_word_cache() { while (word_cache) { @@ -640,6 +655,7 @@ void Label::_bind_methods() { ObjectTypeDB::bind_method(_MD("is_uppercase"),&Label::is_uppercase); ObjectTypeDB::bind_method(_MD("get_line_height"),&Label::get_line_height); ObjectTypeDB::bind_method(_MD("get_line_count"),&Label::get_line_count); + ObjectTypeDB::bind_method(_MD("get_visible_line_count"),&Label::get_visible_line_count); ObjectTypeDB::bind_method(_MD("get_total_character_count"),&Label::get_total_character_count); ObjectTypeDB::bind_method(_MD("set_visible_characters","amount"),&Label::set_visible_characters); ObjectTypeDB::bind_method(_MD("get_visible_characters"),&Label::get_visible_characters); diff --git a/scene/gui/label.h b/scene/gui/label.h index 3c14add60d..b472eca1a2 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -132,6 +132,7 @@ public: int get_line_height() const; int get_line_count() const; + int get_visible_line_count() const; Label(const String& p_text=String()); ~Label(); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 6c47072b33..89c235e101 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -118,7 +118,7 @@ void LineEdit::_input_event(InputEvent p_event) { const InputEventMouseMotion& m=p_event.mouse_motion; - if (m.button_mask&1) { + if (m.button_mask&BUTTON_LEFT) { if (selection.creating) { set_cursor_at_pixel_pos(m.x); @@ -616,11 +616,11 @@ void LineEdit::_notification(int p_what) { } break; case ALIGN_CENTER: { - x_ofs=x_ofs=int(size.width-(cached_width))/2; + x_ofs=int(size.width-(cached_width))/2; } break; case ALIGN_RIGHT: { - x_ofs=x_ofs=int(size.width-style->get_offset().x-(cached_width)); + x_ofs=int(size.width-style->get_offset().x-(cached_width)); } break; } @@ -811,6 +811,9 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { if ( (pixel_ofs-p_x) < (char_w >> 1 ) ) { ofs+=1; + } else if ( (pixel_ofs-p_x) > (char_w >> 1 ) ) { + + ofs-=1; } break; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index d6e084765d..725f1ddf17 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -28,6 +28,7 @@ /*************************************************************************/ #include "menu_button.h" #include "os/keyboard.h" +#include "scene/main/viewport.h" void MenuButton::_unhandled_key_input(InputEvent p_event) { @@ -38,6 +39,9 @@ void MenuButton::_unhandled_key_input(InputEvent p_event) { if (!get_parent() || !is_visible() || is_disabled()) return; + if (get_viewport()->get_modal_stack_top() && !get_viewport()->get_modal_stack_top()->is_a_parent_of(this)) + return; //ignore because of modal window + if (popup->activate_item_by_event(p_event)) accept_event(); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 46b64ce401..af04fbd201 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1007,12 +1007,13 @@ void TextEdit::_notification(int p_what) { cursor_pos.y += (get_row_height() - 3); } + int caret_w = (str[j]=='\t') ? cache.font->get_char_size(' ').width : char_w; if (draw_caret) { if (insert_mode) { int caret_h = (block_caret) ? 4 : 1; - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(char_w,caret_h)),cache.caret_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(caret_w,caret_h)),cache.caret_color); } else { - int caret_w = (block_caret) ? char_w : 1; + caret_w = (block_caret) ? caret_w : 1; VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(caret_w,get_row_height())),cache.caret_color); } } @@ -2992,6 +2993,34 @@ void TextEdit::adjust_viewport_to_cursor() { } +void TextEdit::center_viewport_to_cursor() { + + if (cursor.line_ofs>cursor.line) + cursor.line_ofs=cursor.line; + + int visible_width=cache.size.width-cache.style_normal->get_minimum_size().width-cache.line_number_w-cache.breakpoint_gutter_width; + if (v_scroll->is_visible()) + visible_width-=v_scroll->get_combined_minimum_size().width; + visible_width-=20; // give it a little more space + + int visible_rows = get_visible_rows(); + if (h_scroll->is_visible()) + visible_rows-=((h_scroll->get_combined_minimum_size().height-1)/get_row_height()); + + int max_ofs = text.size()-(scroll_past_end_of_file_enabled?1:visible_rows); + cursor.line_ofs=CLAMP(cursor.line-(visible_rows/2),0,max_ofs); + + int cursor_x = get_column_x_offset( cursor.column, text[cursor.line] ); + + if (cursor_x>(cursor.x_ofs+visible_width)) + cursor.x_ofs=cursor_x-visible_width+1; + + if (cursor_x < cursor.x_ofs) + cursor.x_ofs=cursor_x; + + update(); +} + void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { if (p_col<0) diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 65e9615911..c3bdf7c856 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -396,6 +396,8 @@ public: } void set_auto_indent(bool p_auto_indent); + void center_viewport_to_cursor(); + void cursor_set_column(int p_col, bool p_adjust_viewport=true); void cursor_set_line(int p_row, bool p_adjust_viewport=true); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 55106f5ee7..a53c19d2e7 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1121,6 +1121,38 @@ Node *Node::get_owner() const { return data.owner; } + +Node* Node::find_common_parent_with(const Node *p_node) const { + + if (this==p_node) + return const_cast<Node*>(p_node); + + Set<const Node*> visited; + + const Node *n=this; + + while(n) { + + visited.insert(n); + n=n->data.parent; + } + + const Node *common_parent=p_node; + + while(common_parent) { + + if (visited.has(common_parent)) + break; + common_parent=common_parent->data.parent; + } + + if (!common_parent) + return NULL; + + return const_cast<Node*>(common_parent); + +} + NodePath Node::get_path_to(const Node *p_node) const { ERR_FAIL_NULL_V(p_node,NodePath()); diff --git a/scene/main/node.h b/scene/main/node.h index 10cfc5f9e6..18e403cd61 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -215,6 +215,7 @@ public: NodePath get_path() const; NodePath get_path_to(const Node *p_node) const; + Node* find_common_parent_with(const Node *p_node) const; void add_to_group(const StringName& p_identifier,bool p_persistent=false); void remove_from_group(const StringName& p_identifier); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index cf84f8f425..356ebc64c0 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1346,13 +1346,21 @@ Matrix32 Viewport::_get_input_pre_xform() const { void Viewport::_make_input_local(InputEvent& ev) { + switch(ev.type) { case InputEvent::MOUSE_BUTTON: { + Vector2 vp_ofs; + if (parent_control) { + vp_ofs = (parent_control->get_viewport()->get_final_transform() * parent_control->get_global_transform_with_canvas()).get_origin(); + } + Matrix32 ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); Vector2 g = ai.xform(Vector2(ev.mouse_button.global_x,ev.mouse_button.global_y)); - Vector2 l = ai.xform(Vector2(ev.mouse_button.x,ev.mouse_button.y)); + Vector2 l = ai.xform(Vector2(ev.mouse_button.x,ev.mouse_button.y)-vp_ofs); + + ev.mouse_button.x=l.x; ev.mouse_button.y=l.y; ev.mouse_button.global_x=g.x; @@ -1361,11 +1369,18 @@ void Viewport::_make_input_local(InputEvent& ev) { } break; case InputEvent::MOUSE_MOTION: { + Vector2 vp_ofs; + if (parent_control) { + vp_ofs = (parent_control->get_viewport()->get_final_transform() * parent_control->get_global_transform_with_canvas()).get_origin(); + } + Matrix32 ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); Vector2 g = ai.xform(Vector2(ev.mouse_motion.global_x,ev.mouse_motion.global_y)); - Vector2 l = ai.xform(Vector2(ev.mouse_motion.x,ev.mouse_motion.y)); + Vector2 l = ai.xform(Vector2(ev.mouse_motion.x,ev.mouse_motion.y)-vp_ofs); Vector2 r = ai.basis_xform(Vector2(ev.mouse_motion.relative_x,ev.mouse_motion.relative_y)); Vector2 s = ai.basis_xform(Vector2(ev.mouse_motion.speed_x,ev.mouse_motion.speed_y)); + + ev.mouse_motion.x=l.x; ev.mouse_motion.y=l.y; ev.mouse_motion.global_x=g.x; @@ -1378,16 +1393,28 @@ void Viewport::_make_input_local(InputEvent& ev) { } break; case InputEvent::SCREEN_TOUCH: { + Vector2 vp_ofs; + if (parent_control) { + vp_ofs = (parent_control->get_viewport()->get_final_transform() * parent_control->get_global_transform_with_canvas()).get_origin(); + } + Matrix32 ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); - Vector2 t = ai.xform(Vector2(ev.screen_touch.x,ev.screen_touch.y)); + Vector2 t = ai.xform(Vector2(ev.screen_touch.x,ev.screen_touch.y)-vp_ofs); + + ev.screen_touch.x=t.x; ev.screen_touch.y=t.y; } break; case InputEvent::SCREEN_DRAG: { + Vector2 vp_ofs; + if (parent_control) { + vp_ofs = (parent_control->get_viewport()->get_final_transform() * parent_control->get_global_transform_with_canvas()).get_origin(); + } + Matrix32 ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); - Vector2 t = ai.xform(Vector2(ev.screen_drag.x,ev.screen_drag.y)); + Vector2 t = ai.xform(Vector2(ev.screen_drag.x,ev.screen_drag.y)-vp_ofs); Vector2 r = ai.basis_xform(Vector2(ev.screen_drag.relative_x,ev.screen_drag.relative_y)); Vector2 s = ai.basis_xform(Vector2(ev.screen_drag.speed_x,ev.screen_drag.speed_y)); ev.screen_drag.x=t.x; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index d848b9e5a5..bc0951e436 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -222,6 +222,8 @@ #include "scene/3d/collision_polygon.h" #endif +#include "scene/resources/scene_format_text.h" + static ResourceFormatLoaderImage *resource_loader_image=NULL; static ResourceFormatLoaderWAV *resource_loader_wav=NULL; @@ -233,6 +235,9 @@ static ResourceFormatLoaderWAV *resource_loader_wav=NULL; static ResourceFormatLoaderTheme *resource_loader_theme=NULL; static ResourceFormatLoaderShader *resource_loader_shader=NULL; +static ResourceFormatSaverText *resource_saver_text=NULL; +static ResourceFormatLoaderText *resource_loader_text=NULL; + static ResourceFormatLoaderDynamicFont *resource_loader_dynamic_font=NULL; //static SceneStringNames *string_names; @@ -630,6 +635,13 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init + + resource_saver_text = memnew( ResourceFormatSaverText ); + ResourceSaver::add_resource_format_saver(resource_saver_text,true); + + resource_loader_text = memnew( ResourceFormatLoaderText ); + ResourceLoader::add_resource_format_loader(resource_loader_text,true); + } void unregister_scene_types() { @@ -649,5 +661,11 @@ void unregister_scene_types() { memdelete( resource_loader_theme ); memdelete( resource_loader_shader ); + if (resource_saver_text) { + memdelete(resource_saver_text); + } + if (resource_loader_text) { + memdelete(resource_loader_text); + } SceneStringNames::free(); } diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 88ff09e961..29460790ff 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -644,7 +644,7 @@ Vector2 Curve2D::interpolate_baked(float p_offset,bool p_cubic) const{ if (p_offset>=baked_max_ofs) return r[bpc-1]; - int idx = Math::floor(p_offset/bake_interval); + int idx = Math::floor((double)p_offset/(double)bake_interval); float frac = Math::fmod(p_offset,bake_interval); if (idx>=bpc-1) { @@ -1117,7 +1117,7 @@ Vector3 Curve3D::interpolate_baked(float p_offset,bool p_cubic) const{ if (p_offset>=baked_max_ofs) return r[bpc-1]; - int idx = Math::floor(p_offset/bake_interval); + int idx = Math::floor((double)p_offset/(double)bake_interval); float frac = Math::fmod(p_offset,bake_interval); if (idx>=bpc-1) { @@ -1161,7 +1161,7 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const{ if (p_offset>=baked_max_ofs) return r[bpc-1]; - int idx = Math::floor(p_offset/bake_interval); + int idx = Math::floor((double)p_offset/(double)bake_interval); float frac = Math::fmod(p_offset,bake_interval); if (idx>=bpc-1) { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index ffc5232e8f..499cf0a169 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -54,8 +54,10 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src,float p_left, float p_top, flo texture = Ref<ImageTexture>( memnew( ImageTexture ) ); Image img(p_src); - if (scale>1) + if (scale>1) { + img.convert(Image::FORMAT_RGBA); img.expand_x2_hq2x(); + } texture->create_from_image( img,ImageTexture::FLAG_FILTER ); (*tex_cache)[p_src]=texture; } @@ -92,8 +94,10 @@ static Ref<Texture> make_icon(T p_src) { Ref<ImageTexture> texture( memnew( ImageTexture ) ); Image img = Image(p_src); - if (scale>1) + if (scale>1) { + img.convert(Image::FORMAT_RGBA); img.expand_x2_hq2x(); + } texture->create_from_image( img,ImageTexture::FLAG_FILTER ); return texture; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index c36480420b..f16d68a521 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -732,22 +732,62 @@ Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map<StringName, // only connections that originate or end into main saved scene are saved // everything else is discarded - Node *n=c.target->cast_to<Node>(); - if (!n) { + + Node *target=c.target->cast_to<Node>(); + + + if (!target) { continue; } - //source node is outside saved scene? - bool src_is_out = p_node!=p_owner && (p_node->get_filename()!=String() || p_node->get_owner()!=p_owner); - //target node is outside saved scene? - bool dst_is_out = n!=p_owner && (n->get_filename()!=String() || n->get_owner()!=p_owner); + //find if this connection already exists + Node *common_parent = target->find_common_parent_with(p_node); + + ERR_CONTINUE(!common_parent); + + if (common_parent!=p_owner && common_parent->get_filename()==String()) { + common_parent=common_parent->get_owner(); + } + + bool exists=false; + + //go through ownership chain to see if this exists + while(common_parent) { + + + + Ref<SceneState> ps; + + if (common_parent==p_owner) + ps=common_parent->get_scene_inherited_state(); + else + ps=common_parent->get_scene_instance_state(); - //if both are out, ignore connection - if (src_is_out && dst_is_out) { + + if (ps.is_valid()) { + + NodePath signal_from = common_parent->get_path_to(p_node); + NodePath signal_to = common_parent->get_path_to(target); + + if (ps->has_connection(signal_from,c.signal,signal_to,c.method)) { + exists=true; + break; + } + + } + + if (common_parent==p_owner) + break; + else + common_parent=common_parent->get_owner(); + } + + if (exists) { //already exists (comes from instance or inheritance), so don't save continue; } + { Node *nl=p_node; @@ -760,7 +800,7 @@ Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map<StringName, Ref<SceneState> state = nl->get_scene_inherited_state(); if (state.is_valid()) { int from_node = state->find_node_by_path(nl->get_path_to(p_node)); - int to_node = state->find_node_by_path(nl->get_path_to(n)); + int to_node = state->find_node_by_path(nl->get_path_to(target)); if (from_node>=0 && to_node>=0) { //this one has state for this node, save @@ -778,7 +818,7 @@ Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map<StringName, Ref<SceneState> state = nl->get_scene_instance_state(); if (state.is_valid()) { int from_node = state->find_node_by_path(nl->get_path_to(p_node)); - int to_node = state->find_node_by_path(nl->get_path_to(n)); + int to_node = state->find_node_by_path(nl->get_path_to(target)); if (from_node>=0 && to_node>=0) { //this one has state for this node, save @@ -819,14 +859,14 @@ Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map<StringName, int target_id; - if (node_map.has(n)) { - target_id=node_map[n]; + if (node_map.has(target)) { + target_id=node_map[target]; } else { - if (nodepath_map.has(n)) { - target_id=FLAG_ID_IS_PATH|nodepath_map[n]; + if (nodepath_map.has(target)) { + target_id=FLAG_ID_IS_PATH|nodepath_map[target]; } else { int sidx=nodepath_map.size(); - nodepath_map[n]=sidx; + nodepath_map[target]=sidx; target_id=FLAG_ID_IS_PATH|sidx; } } @@ -1478,6 +1518,8 @@ StringName SceneState::get_connection_method(int p_idx) const{ return names[connections[p_idx].method]; } + + int SceneState::get_connection_flags(int p_idx) const{ ERR_FAIL_INDEX_V(p_idx,connections.size(),-1); @@ -1494,6 +1536,38 @@ Array SceneState::get_connection_binds(int p_idx) const { return binds; } +bool SceneState::has_connection(const NodePath& p_node_from, const StringName& p_signal, const NodePath& p_node_to, const StringName& p_method) const { + + for(int i=0;i<connections.size();i++) { + const ConnectionData &c = connections[i]; + + NodePath np_from; + + if (c.from&FLAG_ID_IS_PATH) { + np_from=node_paths[c.from&FLAG_MASK]; + } else { + np_from=get_node_path(c.from); + } + + NodePath np_to; + + if (c.to&FLAG_ID_IS_PATH) { + np_to=node_paths[c.to&FLAG_MASK]; + } else { + np_to=get_node_path(c.to); + } + + StringName sn_signal=names[c.signal]; + StringName sn_method=names[c.method]; + + if (np_from==p_node_from && sn_signal==p_signal && np_to==p_node_to && sn_method==p_method) { + return true; + } + } + + return false; +} + Vector<NodePath> SceneState::get_editable_instances() const { return editable_instances; } @@ -1505,6 +1579,16 @@ int SceneState::add_name(const StringName& p_name) { return names.size()-1; } +int SceneState::find_name(const StringName& p_name) const { + + for(int i=0;i<names.size();i++) { + if (names[i]==p_name) + return i; + } + + return -1; +} + int SceneState::add_value(const Variant& p_value) { variants.push_back(p_value); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 3b6c0898e0..f0e530f88a 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -163,11 +163,14 @@ public: int get_connection_flags(int p_idx) const; Array get_connection_binds(int p_idx) const; + bool has_connection(const NodePath &p_node_from, const StringName& p_signal, const NodePath &p_node_to, const StringName& p_method) const; + Vector<NodePath> get_editable_instances() const; //build API int add_name(const StringName& p_name); + int find_name(const StringName& p_name) const; int add_value(const Variant& p_value); int add_node_path(const NodePath& p_path); int add_node(int p_parent,int p_owner,int p_type,int p_name, int p_instance); diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp new file mode 100644 index 0000000000..7bb9ca90ae --- /dev/null +++ b/scene/resources/scene_format_text.cpp @@ -0,0 +1,1476 @@ +/*************************************************************************/ +/* scene_format_text.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "scene_format_text.h" + +#include "globals.h" +#include "version.h" +#include "os/dir_access.h" + +#define FORMAT_VERSION 1 + +#include "version.h" +#include "os/dir_access.h" + +#define _printerr() ERR_PRINT(String(res_path+":"+itos(lines)+" - Parse Error: "+error_text).utf8().get_data()); + + + + +/// + +void ResourceInteractiveLoaderText::set_local_path(const String& p_local_path) { + + res_path=p_local_path; +} + +Ref<Resource> ResourceInteractiveLoaderText::get_resource() { + + return resource; +} + +Error ResourceInteractiveLoaderText::_parse_sub_resource(VariantParser::Stream* p_stream,Ref<Resource>& r_res,int &line,String &r_err_str) { + + VariantParser::Token token; + VariantParser::get_token(p_stream,token,line,r_err_str); + if (token.type!=VariantParser::TK_NUMBER) { + r_err_str="Expected number (sub-resource index)"; + return ERR_PARSE_ERROR; + } + + int index = token.value; + + String path = local_path+"::"+itos(index); + + if (!ignore_resource_parsing) { + + if (!ResourceCache::has(path)) { + r_err_str="Can't load cached sub-resource: "+path; + return ERR_PARSE_ERROR; + } + + r_res=RES(ResourceCache::get(path)); + } else { + r_res=RES(); + } + + VariantParser::get_token(p_stream,token,line,r_err_str); + if (token.type!=VariantParser::TK_PARENTHESIS_CLOSE) { + r_err_str="Expected ')'"; + return ERR_PARSE_ERROR; + } + + + return OK; +} + +Error ResourceInteractiveLoaderText::_parse_ext_resource(VariantParser::Stream* p_stream,Ref<Resource>& r_res,int &line,String &r_err_str){ + + VariantParser::Token token; + VariantParser::get_token(p_stream,token,line,r_err_str); + if (token.type!=VariantParser::TK_NUMBER) { + r_err_str="Expected number (sub-resource index)"; + return ERR_PARSE_ERROR; + } + + int id = token.value; + + if (!ignore_resource_parsing) { + + if (!ext_resources.has(id)) { + r_err_str="Can't load cached ext-resource #"+itos(id); + return ERR_PARSE_ERROR; + } + + String path = ext_resources[id].path; + String type = ext_resources[id].type; + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); + + } + + r_res=ResourceLoader::load(path,type); + + if (r_res.is_null()) { + WARN_PRINT(String("Couldn't load external resource: "+path).utf8().get_data()); + } + } else { + r_res=RES(); + } + + VariantParser::get_token(p_stream,token,line,r_err_str); + if (token.type!=VariantParser::TK_PARENTHESIS_CLOSE) { + r_err_str="Expected ')'"; + return ERR_PARSE_ERROR; + } + + + return OK; +} + + +Error ResourceInteractiveLoaderText::poll() { + + if (error!=OK) + return error; + + if (next_tag.name=="ext_resource") { + + + if (!next_tag.fields.has("path")) { + error=ERR_FILE_CORRUPT; + error_text="Missing 'path' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("type")) { + error=ERR_FILE_CORRUPT; + error_text="Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("id")) { + error=ERR_FILE_CORRUPT; + error_text="Missing 'id' in external resource tag"; + _printerr(); + return error; + } + + String path=next_tag.fields["path"]; + String type=next_tag.fields["type"]; + int index=next_tag.fields["id"]; + + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); + } + + if (remaps.has(path)) { + path=remaps[path]; + } + + RES res = ResourceLoader::load(path,type); + + if (res.is_null()) { + + if (ResourceLoader::get_abort_on_missing_resources()) { + error=ERR_FILE_CORRUPT; + error_text="[ext_resource] referenced nonexistent resource at: "+path; + _printerr(); + return error; + } else { + ResourceLoader::notify_dependency_error(local_path,path,type); + } + } else { + + resource_cache.push_back(res); + } + + ExtResource er; + er.path=path; + er.type=type; + ext_resources[index]=er; + + error = VariantParser::parse_tag(&stream,lines,error_text,next_tag,&rp); + + if (error) { + _printerr(); + } + + resource_current++; + return error; + + + } else if (next_tag.name=="sub_resource") { + + + if (!next_tag.fields.has("type")) { + error=ERR_FILE_CORRUPT; + error_text="Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("id")) { + error=ERR_FILE_CORRUPT; + error_text="Missing 'index' in external resource tag"; + _printerr(); + return error; + } + + String type=next_tag.fields["type"]; + int id=next_tag.fields["id"]; + + String path = local_path+"::"+itos(id); + + + //bool exists=ResourceCache::has(path); + + Ref<Resource> res; + + if ( !ResourceCache::has(path)) { //only if it doesn't exist + + Object *obj = ObjectTypeDB::instance(type); + if (!obj) { + + error_text+="Can't create sub resource of type: "+type; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + + Resource *r = obj->cast_to<Resource>(); + if (!r) { + + error_text+="Can't create sub resource of type, because not a resource: "+type; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + res=Ref<Resource>(r); + resource_cache.push_back(res); + res->set_path(path); + + } + + resource_current++; + + while(true) { + + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream,lines,error_text,next_tag,assign,value,&rp); + + if (error) { + _printerr(); + return error; + } + + if (assign!=String()) { + if (res.is_valid()) { + res->set(assign,value); + } + //it's assignment + } else if (next_tag.name!=String()) { + + error=OK; + break; + } else { + error=ERR_FILE_CORRUPT; + error_text="Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; + } + + + } + + return OK; + + } else if (next_tag.name=="resource") { + + if (is_scene) { + + error_text+="found the 'resource' tag on a scene file!"; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + Object *obj = ObjectTypeDB::instance(res_type); + if (!obj) { + + error_text+="Can't create sub resource of type: "+res_type; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + + Resource *r = obj->cast_to<Resource>(); + if (!r) { + + error_text+="Can't create sub resource of type, because not a resource: "+res_type; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + resource=Ref<Resource>(r); + + resource_current++; + + while(true) { + + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream,lines,error_text,next_tag,assign,value,&rp); + + if (error) { + if (error!=ERR_FILE_EOF) { + _printerr(); + } else { + if (!ResourceCache::has(res_path)) { + resource->set_path(res_path); + } + } + return error; + } + + if (assign!=String()) { + resource->set(assign,value); + //it's assignment + } else if (next_tag.name!=String()) { + + error=ERR_FILE_CORRUPT; + error_text="Extra tag found when parsing main resource file"; + _printerr(); + return error; + } else { + error=ERR_FILE_EOF; + return error; + } + + } + + return OK; + + } else if (next_tag.name=="node") { + + if (!is_scene) { + + error_text+="found the 'node' tag on a resource file!"; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + /* + int add_name(const StringName& p_name); + int add_value(const Variant& p_value); + int add_node_path(const NodePath& p_path); + int add_node(int p_parent,int p_owner,int p_type,int p_name, int p_instance); + void add_node_property(int p_node,int p_name,int p_value); + void add_node_group(int p_node,int p_group); + void set_base_scene(int p_idx); + void add_connection(int p_from,int p_to, int p_signal, int p_method, int p_flags,const Vector<int>& p_binds); + void add_editable_instance(const NodePath& p_path); + + */ + + int parent=-1; + int owner=-1; + int type=-1; + int name=-1; + int instance=-1; +// int base_scene=-1; + + if (next_tag.fields.has("name")) { + name=packed_scene->get_state()->add_name(next_tag.fields["name"]); + } + + if (next_tag.fields.has("parent")) { + NodePath np = next_tag.fields["parent"]; + np.prepend_period(); //compatible to how it manages paths internally + parent=packed_scene->get_state()->add_node_path(np); + } + + + + if (next_tag.fields.has("type")) { + type=packed_scene->get_state()->add_name(next_tag.fields["type"]); + } else { + type=SceneState::TYPE_INSTANCED; //no type? assume this was instanced + } + + + if (next_tag.fields.has("instance")) { + + instance=packed_scene->get_state()->add_value(next_tag.fields["instance"]); + + if (packed_scene->get_state()->get_node_count()==0 && parent==-1) { + packed_scene->get_state()->set_base_scene(instance); + instance=-1; + } + } + + if (next_tag.fields.has("instance_placeholder")) { + + String path=next_tag.fields["instance_placeholder"]; + + int path_v = packed_scene->get_state()->add_value(path); + + if (packed_scene->get_state()->get_node_count()==0) { + error=ERR_FILE_CORRUPT; + error_text="Instance Placeholder can't be used for inheritance."; + _printerr(); + return error; + } + + instance=path_v|SceneState::FLAG_INSTANCE_IS_PLACEHOLDER; + } + + if (next_tag.fields.has("owner")) { + owner=packed_scene->get_state()->add_node_path(next_tag.fields["owner"]); + } else { + if (parent!=-1 && !(type==SceneState::TYPE_INSTANCED && instance==-1)) + owner=0; //if no owner, owner is root + } + + int node_id = packed_scene->get_state()->add_node(parent,owner,type,name,instance); + + if (next_tag.fields.has("groups")) { + + Array groups = next_tag.fields["groups"]; + for (int i=0;i<groups.size();i++) { + packed_scene->get_state()->add_node_group(node_id,packed_scene->get_state()->add_name(groups[i])); + } + } + + while(true) { + + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream,lines,error_text,next_tag,assign,value,&rp); + + if (error) { + if (error!=ERR_FILE_EOF) { + _printerr(); + } else { + resource=packed_scene; + if (!ResourceCache::has(res_path)) { + packed_scene->set_path(res_path); + } + } + return error; + } + + if (assign!=String()) { + int nameidx = packed_scene->get_state()->add_name(assign); + int valueidx = packed_scene->get_state()->add_value(value); + packed_scene->get_state()->add_node_property(node_id,nameidx,valueidx); + //it's assignment + } else if (next_tag.name!=String()) { + + error=OK; + return error; + } else { + + resource=packed_scene; + error=ERR_FILE_EOF; + return error; + } + + } + + return OK; + + } else if (next_tag.name=="connection") { + + if (!is_scene) { + + error_text+="found the 'connection' tag on a resource file!"; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + if (!next_tag.fields.has("from")) { + error=ERR_FILE_CORRUPT; + error_text="missing 'from' field fron connection tag"; + return error; + } + + if (!next_tag.fields.has("to")) { + error=ERR_FILE_CORRUPT; + error_text="missing 'to' field fron connection tag"; + return error; + } + + if (!next_tag.fields.has("signal")) { + error=ERR_FILE_CORRUPT; + error_text="missing 'signal' field fron connection tag"; + return error; + } + + if (!next_tag.fields.has("method")) { + error=ERR_FILE_CORRUPT; + error_text="missing 'method' field fron connection tag"; + return error; + } + + NodePath from = next_tag.fields["from"]; + NodePath to = next_tag.fields["to"]; + StringName method = next_tag.fields["method"]; + StringName signal = next_tag.fields["signal"]; + int flags=CONNECT_PERSIST; + Array binds; + + if (next_tag.fields.has("flags")) { + flags=next_tag.fields["flags"]; + } + + if (next_tag.fields.has("binds")) { + binds=next_tag.fields["binds"]; + } + + Vector<int> bind_ints; + for(int i=0;i<binds.size();i++) { + bind_ints.push_back( packed_scene->get_state()->add_value( binds[i] ) ); + } + + packed_scene->get_state()->add_connection( + packed_scene->get_state()->add_node_path(from.simplified()), + packed_scene->get_state()->add_node_path(to.simplified()), + packed_scene->get_state()->add_name(signal), + packed_scene->get_state()->add_name(method), + flags, + bind_ints + ); + + error = VariantParser::parse_tag(&stream,lines,error_text,next_tag,&rp); + + if (error) { + if (error!=ERR_FILE_EOF) { + _printerr(); + } else { + resource=packed_scene; + } + } + + return error; + } else if (next_tag.name=="editable") { + + if (!is_scene) { + + error_text+="found the 'editable' tag on a resource file!"; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + if (!next_tag.fields.has("path")) { + error=ERR_FILE_CORRUPT; + error_text="missing 'path' field fron connection tag"; + _printerr(); + return error; + } + + NodePath path = next_tag.fields["path"]; + + packed_scene->get_state()->add_editable_instance(path.simplified()); + + error = VariantParser::parse_tag(&stream,lines,error_text,next_tag,&rp); + + if (error) { + if (error!=ERR_FILE_EOF) { + _printerr(); + } else { + resource=packed_scene; + } + } + + return error; + + } else { + + error_text+="Unknown tag in file: "+next_tag.name; + _printerr(); + error=ERR_FILE_CORRUPT; + return error; + } + + return OK; +} + +int ResourceInteractiveLoaderText::get_stage() const { + + return resource_current; +} +int ResourceInteractiveLoaderText::get_stage_count() const { + + return resources_total;//+ext_resources; +} + +ResourceInteractiveLoaderText::~ResourceInteractiveLoaderText() { + + memdelete(f); +} + +void ResourceInteractiveLoaderText::get_dependencies(FileAccess *f,List<String> *p_dependencies,bool p_add_types) { + + + open(f); + ignore_resource_parsing=true; + ERR_FAIL_COND(error!=OK); + + while(next_tag.name=="ext_resource") { + + if (!next_tag.fields.has("type")) { + error=ERR_FILE_CORRUPT; + error_text="Missing 'type' in external resource tag"; + _printerr(); + return; + } + + if (!next_tag.fields.has("id")) { + error=ERR_FILE_CORRUPT; + error_text="Missing 'index' in external resource tag"; + _printerr(); + return; + } + + String path=next_tag.fields["path"]; + String type=next_tag.fields["type"]; + + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); + } + + + if (p_add_types) { + path+="::"+type; + } + + p_dependencies->push_back(path); + + Error err = VariantParser::parse_tag(&stream,lines,error_text,next_tag,&rp); + + if (err) { + print_line(error_text+" - "+itos(lines)); + error_text="Unexpected end of file"; + _printerr(); + error=ERR_FILE_CORRUPT; + } + + + } +} + +Error ResourceInteractiveLoaderText::rename_dependencies(FileAccess *p_f, const String &p_path,const Map<String,String>& p_map) { + + + open(p_f,true); + ERR_FAIL_COND_V(error!=OK,error); + ignore_resource_parsing=true; + //FileAccess + + FileAccess *fw = NULL; + + String base_path=local_path.get_base_dir(); + + + uint64_t tag_end = f->get_pos(); + + + while(true) { + + + + Error err = VariantParser::parse_tag(&stream,lines,error_text,next_tag,&rp); + + if (err!=OK) { + if (fw) { + memdelete(fw); + } + error=ERR_FILE_CORRUPT; + ERR_FAIL_V(error); + } + + if (next_tag.name!="ext_resource") { + + //nothing was done + if (!fw) + return OK; + + break; + + + } else { + + if (!fw) { + + fw=FileAccess::open(p_path+".depren",FileAccess::WRITE); + if (is_scene) { + fw->store_line("[gd_scene load_steps="+itos(resources_total)+" format="+itos(FORMAT_VERSION)+"]\n"); + } else { + fw->store_line("[gd_resource type=\""+res_type+"\" load_steps="+itos(resources_total)+" format="+itos(FORMAT_VERSION)+"]\n"); + } + } + + if (!next_tag.fields.has("path") || !next_tag.fields.has("id") || !next_tag.fields.has("type")) { + memdelete(fw); + error=ERR_FILE_CORRUPT; + ERR_FAIL_V(error); + } + + String path = next_tag.fields["path"]; + int index = next_tag.fields["id"]; + String type = next_tag.fields["type"]; + + + bool relative=false; + if (!path.begins_with("res://")) { + path=base_path.plus_file(path).simplify_path(); + relative=true; + } + + if (p_map.has(path)) { + String np=p_map[path]; + path=np; + } + + if (relative) { + //restore relative + path=base_path.path_to_file(path); + } + + fw->store_line("[ext_resource path=\""+path+"\" type=\""+type+"\" id="+itos(index)+"]"); + + tag_end = f->get_pos(); + + } + + } + + f->seek(tag_end); + + uint8_t c=f->get_8(); + while(!f->eof_reached()) { + fw->store_8(c); + c=f->get_8(); + } + f->close(); + + bool all_ok = fw->get_error()==OK; + + memdelete(fw); + + if (!all_ok) { + return ERR_CANT_CREATE; + } + + DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(p_path); + da->rename(p_path+".depren",p_path); + memdelete(da); + + return OK; + +} + + +void ResourceInteractiveLoaderText::open(FileAccess *p_f,bool p_skip_first_tag) { + + error=OK; + + lines=1; + f=p_f; + + + stream.f=f; + is_scene=false; + ignore_resource_parsing=false; + resource_current=0; + + + VariantParser::Tag tag; + Error err = VariantParser::parse_tag(&stream,lines,error_text,tag); + + if (err) { + + error=err; + _printerr(); + return; + } + + if (tag.fields.has("format")) { + int fmt = tag.fields["format"]; + if (fmt>FORMAT_VERSION) { + error_text="Saved with newer format version"; + _printerr(); + error=ERR_PARSE_ERROR; + return; + } + } + + + if (tag.name=="gd_scene") { + is_scene=true; + packed_scene.instance(); + + } else if (tag.name=="gd_resource") { + if (!tag.fields.has("type")) { + error_text="Missing 'type' field in 'gd_resource' tag"; + _printerr(); + error=ERR_PARSE_ERROR; + return; + } + + res_type=tag.fields["type"]; + + } else { + error_text="Unrecognized file type: "+tag.name; + _printerr(); + error=ERR_PARSE_ERROR; + return; + + } + + + + if (tag.fields.has("load_steps")) { + resources_total=tag.fields["load_steps"]; + } else { + resources_total=0; + } + + if (!p_skip_first_tag) { + + err = VariantParser::parse_tag(&stream,lines,error_text,next_tag,&rp); + + if (err) { + error_text="Unexpected end of file"; + _printerr(); + error=ERR_FILE_CORRUPT; + } + } + + rp.ext_func=_parse_ext_resources; + rp.sub_func=_parse_sub_resources; + rp.func=NULL; + rp.userdata=this; + +} + + + + +String ResourceInteractiveLoaderText::recognize(FileAccess *p_f) { + + error=OK; + + lines=1; + f=p_f; + + stream.f=f; + + ignore_resource_parsing=true; + + + VariantParser::Tag tag; + Error err = VariantParser::parse_tag(&stream,lines,error_text,tag); + + if (err) { + _printerr(); + return ""; + } + + if (tag.fields.has("format")) { + int fmt = tag.fields["format"]; + if (fmt>FORMAT_VERSION) { + error_text="Saved with newer format version"; + _printerr(); + return ""; + } + } + + if (tag.name=="gd_scene") + return "PackedScene"; + + if (tag.name!="gd_resource") + return ""; + + + + if (!tag.fields.has("type")) { + error_text="Missing 'type' field in 'gd_resource' tag"; + _printerr(); + return ""; + } + + return tag.fields["type"]; + + +} + +///////////////////// + +Ref<ResourceInteractiveLoader> ResourceFormatLoaderText::load_interactive(const String &p_path, Error *r_error) { + + if (r_error) + *r_error=ERR_CANT_OPEN; + + Error err; + FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err); + + + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK,Ref<ResourceInteractiveLoader>()); + } + + Ref<ResourceInteractiveLoaderText> ria = memnew( ResourceInteractiveLoaderText ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->open(f); + + return ria; +} + +void ResourceFormatLoaderText::get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const { + + if (p_type=="") { + get_recognized_extensions(p_extensions); + return; + } + + if (p_type=="PackedScene") + p_extensions->push_back("tscn"); + else + p_extensions->push_back("tres"); + +} + +void ResourceFormatLoaderText::get_recognized_extensions(List<String> *p_extensions) const{ + + p_extensions->push_back("tscn"); + p_extensions->push_back("tres"); +} + +bool ResourceFormatLoaderText::handles_type(const String& p_type) const{ + + return true; +} +String ResourceFormatLoaderText::get_resource_type(const String &p_path) const{ + + + + String ext=p_path.extension().to_lower(); + if (ext=="tscn") + return "PackedScene"; + else if (ext!="tres") + return String(); + + //for anyhting else must test.. + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + return ""; //could not rwead + } + + Ref<ResourceInteractiveLoaderText> ria = memnew( ResourceInteractiveLoaderText ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + String r = ria->recognize(f); + return r; +} + + +void ResourceFormatLoaderText::get_dependencies(const String& p_path,List<String> *p_dependencies,bool p_add_types) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + ERR_FAIL(); + } + + Ref<ResourceInteractiveLoaderText> ria = memnew( ResourceInteractiveLoaderText ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->get_dependencies(f,p_dependencies,p_add_types); + + +} + +Error ResourceFormatLoaderText::rename_dependencies(const String &p_path,const Map<String,String>& p_map) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + ERR_FAIL_V(ERR_CANT_OPEN); + } + + Ref<ResourceInteractiveLoaderText> ria = memnew( ResourceInteractiveLoaderText ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + return ria->rename_dependencies(f,p_path,p_map); +} + + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + + +String ResourceFormatSaverTextInstance::_write_resources(void *ud,const RES& p_resource) { + + ResourceFormatSaverTextInstance *rsi=(ResourceFormatSaverTextInstance*)ud; + return rsi->_write_resource(p_resource); + +} + +String ResourceFormatSaverTextInstance::_write_resource(const RES& res) { + + if (external_resources.has(res)) { + + return "ExtResource( "+itos(external_resources[res]+1)+" )"; + } else { + + if (internal_resources.has(res)) { + return "SubResource( "+itos(internal_resources[res])+" )"; + } else if (res->get_path().length() && res->get_path().find("::")==-1) { + + //external resource + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + return "Resource( \""+path+"\" )"; + } else { + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL_V("null"); + //internal resource + } + } + + return "null"; +} + +void ResourceFormatSaverTextInstance::_find_resources(const Variant& p_variant,bool p_main) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null() || external_resources.has(res)) + return; + + if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) { + int index = external_resources.size(); + external_resources[res]=index; + return; + } + + if (resource_set.has(res)) + return; + + List<PropertyInfo> property_list; + + res->get_property_list( &property_list ); + property_list.sort(); + + List<PropertyInfo>::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + + I=I->next(); + } + + resource_set.insert( res ); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i<len;i++) { + + Variant v=varray.get(i); + _find_resources(v); + } + + } break; + case Variant::DICTIONARY: { + + Dictionary d=p_variant; + List<Variant> keys; + d.get_key_list(&keys); + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + +static String _valprop(const String& p_name) { + + if (p_name.find("\"")!=-1 || p_name.find("=")!=-1 || p_name.find(" ")!=-1) + return "\""+p_name.c_escape()+"\""; + return p_name; +} + +Error ResourceFormatSaverTextInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + if (p_path.ends_with(".tscn")) { + packed_scene=p_resource; + } + + Error err; + f = FileAccess::open(p_path, FileAccess::WRITE,&err); + ERR_FAIL_COND_V( err, ERR_CANT_OPEN ); + FileAccessRef _fref(f); + + local_path = Globals::get_singleton()->localize_path(p_path); + + relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES; + takeover_paths=p_flags&ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS; + if (!p_path.begins_with("res://")) { + takeover_paths=false; + } + + // save resources + _find_resources(p_resource,true); + + if (packed_scene.is_valid()) { + //add instances to external resources if saving a packed scene + for(int i=0;i<packed_scene->get_state()->get_node_count();i++) { + if (packed_scene->get_state()->is_node_instance_placeholder(i)) + continue; + + Ref<PackedScene> instance=packed_scene->get_state()->get_node_instance(i); + if (instance.is_valid() && !external_resources.has(instance)) { + int index = external_resources.size(); + external_resources[instance]=index; + } + } + } + + + ERR_FAIL_COND_V(err!=OK,err); + + { + String title=packed_scene.is_valid()?"[gd_scene ":"[gd_resource "; + if (packed_scene.is_null()) + title+="type=\""+p_resource->get_type()+"\" "; + int load_steps=saved_resources.size()+external_resources.size(); + //if (packed_scene.is_valid()) { + // load_steps+=packed_scene->get_node_count(); + //} + //no, better to not use load steps from nodes, no point to that + + if (load_steps>1) { + title+="load_steps="+itos(load_steps)+" "; + } + title+="format="+itos(FORMAT_VERSION)+""; + //title+="engine_version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\""; + + f->store_string(title); + f->store_line("]\n"); //one empty line + } + + + Vector<RES> sorted_er; + sorted_er.resize(external_resources.size()); + + for(Map<RES,int>::Element *E=external_resources.front();E;E=E->next()) { + + sorted_er[E->get()]=E->key(); + } + + for(int i=0;i<sorted_er.size();i++) { + String p = sorted_er[i]->get_path(); + + f->store_string("[ext_resource path=\""+p+"\" type=\""+sorted_er[i]->get_save_type()+"\" id="+itos(i+1)+"]\n"); //bundled + } + + if (external_resources.size()) + f->store_line(String()); //separate + + Set<int> used_indices; + + for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + if (E->next() && (res->get_path()=="" || res->get_path().find("::") != -1 )) { + + if (res->get_subindex()!=0) { + if (used_indices.has(res->get_subindex())) { + res->set_subindex(0); //repeated + } else { + used_indices.insert(res->get_subindex()); + } + } + } + } + + for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + ERR_CONTINUE(!resource_set.has(res)); + bool main = (E->next()==NULL); + + if (main && packed_scene.is_valid()) + break; //save as a scene + + if (main) { + f->store_line("[resource]\n"); + } else { + String line="[sub_resource "; + if (res->get_subindex()==0) { + int new_subindex=1; + if (used_indices.size()) { + new_subindex=used_indices.back()->get()+1; + } + + res->set_subindex(new_subindex); + used_indices.insert(new_subindex); + } + + int idx = res->get_subindex(); + line+="type=\""+res->get_type()+"\" id="+itos(idx); + f->store_line(line+"]\n"); + if (takeover_paths) { + res->set_path(p_path+"::"+itos(idx),true); + } + + internal_resources[res]=idx; + + } + + + List<PropertyInfo> property_list; + res->get_property_list(&property_list); +// property_list.sort(); + for(List<PropertyInfo>::Element *PE = property_list.front();PE;PE=PE->next()) { + + + if (skip_editor && PE->get().name.begins_with("__editor")) + continue; + + if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) { + + String name = PE->get().name; + Variant value = res->get(name); + + + if ((PE->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero())||(PE->get().usage&PROPERTY_USAGE_STORE_IF_NONONE && value.is_one()) ) + continue; + + if (PE->get().type==Variant::OBJECT && value.is_zero() && !(PE->get().usage&PROPERTY_USAGE_STORE_IF_NULL)) + continue; + + String vars; + VariantWriter::write_to_string(value,vars,_write_resources,this); + f->store_string(_valprop(name)+" = "+vars+"\n"); + } + + + } + + f->store_string("\n"); + + } + + if (packed_scene.is_valid()) { + //if this is a scene, save nodes and connections! + Ref<SceneState> state = packed_scene->get_state(); + for(int i=0;i<state->get_node_count();i++) { + + StringName type = state->get_node_type(i); + StringName name = state->get_node_name(i); + NodePath path = state->get_node_path(i,true); + NodePath owner = state->get_node_owner_path(i); + Ref<PackedScene> instance = state->get_node_instance(i); + String instance_placeholder = state->get_node_instance_placeholder(i); + Vector<StringName> groups = state->get_node_groups(i); + + + + String header="[node"; + header+=" name=\""+String(name)+"\""; + if (type!=StringName()) { + header+=" type=\""+String(type)+"\""; + } + if (path!=NodePath()) { + header+=" parent=\""+String(path.simplified())+"\""; + } + if (owner!=NodePath() && owner!=NodePath(".")) { + header+=" owner=\""+String(owner.simplified())+"\""; + } + + if (groups.size()) { + String sgroups=" groups=[ "; + for(int j=0;j<groups.size();j++) { + if (j>0) + sgroups+=", "; + sgroups+="\""+groups[j].operator String().c_escape()+"\""; + } + sgroups+=" ]"; + header+=sgroups; + } + + f->store_string(header); + + if (instance_placeholder!=String()) { + + String vars; + f->store_string(" instance_placeholder="); + VariantWriter::write_to_string(instance_placeholder,vars,_write_resources,this); + f->store_string(vars); + } + + if (instance.is_valid()) { + + String vars; + f->store_string(" instance="); + VariantWriter::write_to_string(instance,vars,_write_resources,this); + f->store_string(vars); + + } + + f->store_line("]\n"); + + for(int j=0;j<state->get_node_property_count(i);j++) { + + String vars; + VariantWriter::write_to_string(state->get_node_property_value(i,j),vars,_write_resources,this); + + f->store_string(_valprop(String(state->get_node_property_name(i,j)))+" = "+vars+"\n"); + } + + if (state->get_node_property_count(i)) { + //add space + f->store_line(String()); + } + + } + + for(int i=0;i<state->get_connection_count();i++) { + + String connstr="[connection"; + connstr+=" signal=\""+String(state->get_connection_signal(i))+"\""; + connstr+=" from=\""+String(state->get_connection_source(i).simplified())+"\""; + connstr+=" to=\""+String(state->get_connection_target(i).simplified())+"\""; + connstr+=" method=\""+String(state->get_connection_method(i))+"\""; + int flags = state->get_connection_flags(i); + if (flags!=Object::CONNECT_PERSIST) { + connstr+=" flags="+itos(flags); + } + + Array binds=state->get_connection_binds(i); + f->store_string(connstr); + if (binds.size()) { + String vars; + VariantWriter::write_to_string(binds,vars,_write_resources,this); + f->store_string(" binds= "+vars); + + } + + f->store_line("]\n"); + } + + f->store_line(String()); + + Vector<NodePath> editable_instances = state->get_editable_instances(); + for(int i=0;i<editable_instances.size();i++) { + f->store_line("[editable path=\""+editable_instances[i].operator String()+"\"]"); + } + } + + if (f->get_error()!=OK && f->get_error()!=ERR_FILE_EOF) { + f->close(); + return ERR_CANT_CREATE; + } + + f->close(); + //memdelete(f); + + return OK; +} + + + +Error ResourceFormatSaverText::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + if (p_path.ends_with(".sct") && p_resource->get_type()!="PackedScene") { + return ERR_FILE_UNRECOGNIZED; + } + + ResourceFormatSaverTextInstance saver; + return saver.save(p_path,p_resource,p_flags); + +} + +bool ResourceFormatSaverText::recognize(const RES& p_resource) const { + + + return true; // all recognized! +} +void ResourceFormatSaverText::get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const { + + if (p_resource->get_type()=="PackedScene") + p_extensions->push_back("tscn"); //text scene + else + p_extensions->push_back("tres"); //text resource + +} + +ResourceFormatSaverText* ResourceFormatSaverText::singleton=NULL; +ResourceFormatSaverText::ResourceFormatSaverText() { + singleton=this; +} diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h new file mode 100644 index 0000000000..6122a1f9d8 --- /dev/null +++ b/scene/resources/scene_format_text.h @@ -0,0 +1,170 @@ +/*************************************************************************/ +/* scene_format_text.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SCENE_FORMAT_TEXT_H +#define SCENE_FORMAT_TEXT_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "scene/resources/packed_scene.h" +#include "variant_parser.h" + + + +class ResourceInteractiveLoaderText : public ResourceInteractiveLoader { + + String local_path; + String res_path; + String error_text; + + FileAccess *f; + + VariantParser::StreamFile stream; + + struct ExtResource { + String path; + String type; + }; + + + bool is_scene; + String res_type; + + bool ignore_resource_parsing; + +// Map<String,String> remaps; + + Map<int,ExtResource> ext_resources; + + int resources_total; + int resource_current; + String resource_type; + + VariantParser::Tag next_tag; + + mutable int lines; + + Map<String,String> remaps; + //void _printerr(); + + static Error _parse_sub_resources(void* p_self, VariantParser::Stream* p_stream,Ref<Resource>& r_res,int &line,String &r_err_str) { return reinterpret_cast<ResourceInteractiveLoaderText*>(p_self)->_parse_sub_resource(p_stream,r_res,line,r_err_str); } + static Error _parse_ext_resources(void* p_self, VariantParser::Stream* p_stream,Ref<Resource>& r_res,int &line,String &r_err_str) { return reinterpret_cast<ResourceInteractiveLoaderText*>(p_self)->_parse_ext_resource(p_stream,r_res,line,r_err_str); } + + Error _parse_sub_resource(VariantParser::Stream* p_stream,Ref<Resource>& r_res,int &line,String &r_err_str); + Error _parse_ext_resource(VariantParser::Stream* p_stream,Ref<Resource>& r_res,int &line,String &r_err_str); + + VariantParser::ResourceParser rp; + + + Ref<PackedScene> packed_scene; + + +friend class ResourceFormatLoaderText; + + List<RES> resource_cache; + Error error; + + RES resource; + +public: + + virtual void set_local_path(const String& p_local_path); + virtual Ref<Resource> get_resource(); + virtual Error poll(); + virtual int get_stage() const; + virtual int get_stage_count() const; + + void open(FileAccess *p_f, bool p_skip_first_tag=false); + String recognize(FileAccess *p_f); + void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types); + Error rename_dependencies(FileAccess *p_f, const String &p_path,const Map<String,String>& p_map); + + + ~ResourceInteractiveLoaderText(); + +}; + + + +class ResourceFormatLoaderText : public ResourceFormatLoader { +public: + + virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path,Error *r_error=NULL); + virtual void get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String& p_path, List<String> *p_dependencies, bool p_add_types=false); + virtual Error rename_dependencies(const String &p_path,const Map<String,String>& p_map); + + +}; + + +class ResourceFormatSaverTextInstance { + + String local_path; + + Ref<PackedScene> packed_scene; + + bool takeover_paths; + bool relative_paths; + bool bundle_resources; + bool skip_editor; + FileAccess *f; + Set<RES> resource_set; + List<RES> saved_resources; + Map<RES,int> external_resources; + Map<RES,int> internal_resources; + + void _find_resources(const Variant& p_variant,bool p_main=false); + + static String _write_resources(void *ud,const RES& p_resource); + String _write_resource(const RES& res); + + +public: + + Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + + +}; + +class ResourceFormatSaverText : public ResourceFormatSaver { +public: + static ResourceFormatSaverText* singleton; + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + virtual bool recognize(const RES& p_resource) const; + virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const; + + ResourceFormatSaverText(); +}; + + +#endif // SCENE_FORMAT_TEXT_H diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 12382e6678..e1769d099b 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -752,8 +752,6 @@ void SurfaceTool::generate_normals() { if (smooth_groups.has(0)) smooth=smooth_groups[0]; - print_line("SMOOTH BEGIN? "+itos(smooth)); - List< Vertex >::Element *B=vertex_array.front(); for(List< Vertex >::Element *E=B;E;) { @@ -862,6 +860,7 @@ void SurfaceTool::_bind_methods() { ObjectTypeDB::bind_method(_MD("deindex"),&SurfaceTool::deindex); ///ObjectTypeDB::bind_method(_MD("generate_flat_normals"),&SurfaceTool::generate_flat_normals); ObjectTypeDB::bind_method(_MD("generate_normals"),&SurfaceTool::generate_normals); + ObjectTypeDB::bind_method(_MD("add_index", "index"), &SurfaceTool::add_index); ObjectTypeDB::bind_method(_MD("commit:Mesh","existing:Mesh"),&SurfaceTool::commit,DEFVAL(Variant())); ObjectTypeDB::bind_method(_MD("clear"),&SurfaceTool::clear); diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 92a6f0c0b9..b351167e10 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -219,7 +219,22 @@ Ref<Theme> Theme::get_default() { void Theme::set_default_theme_font( const Ref<Font>& p_default_font ) { + if (default_theme_font==p_default_font) + return; + + if (default_theme_font.is_valid()) { + _unref_font(default_theme_font); + } + default_theme_font=p_default_font; + + if (default_theme_font.is_valid()) { + _ref_font(default_theme_font); + } + + _change_notify(); + emit_changed();; + } Ref<Font> Theme::get_default_theme_font() const { diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index e96cac170b..6b329a1a73 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -235,19 +235,20 @@ struct SpatialIndexer2D { List<VisibilityNotifier2D*> added; List<VisibilityNotifier2D*> removed; - for(int i=begin.x;i<=end.x;i++) { + int visible_cells=(end.x-begin.x)*(end.y-begin.y); - for(int j=begin.y;j<=end.y;j++) { + if (visible_cells>10000) { - CellKey ck; - ck.x=i; - ck.y=j; + //well you zoomed out a lot, it's your problem. To avoid freezing in the for loops below, we'll manually check cell by cell - Map<CellKey,CellData>::Element *F=cells.find(ck); - if (!F) { - continue; - } + for (Map<CellKey,CellData>::Element *F=cells.front();F;F=F->next()) { + + const CellKey &ck=F->key(); + if (ck.x<begin.x || ck.x>end.x) + continue; + if (ck.y<begin.y || ck.y>end.y) + continue; //notifiers in cell for (Map<VisibilityNotifier2D*,CellRef>::Element *G=F->get().notifiers.front();G;G=G->next()) { @@ -262,6 +263,38 @@ struct SpatialIndexer2D { } } } + + } else { + + //check cells in grid fashion + for(int i=begin.x;i<=end.x;i++) { + + for(int j=begin.y;j<=end.y;j++) { + + CellKey ck; + ck.x=i; + ck.y=j; + + Map<CellKey,CellData>::Element *F=cells.find(ck); + if (!F) { + continue; + } + + + //notifiers in cell + for (Map<VisibilityNotifier2D*,CellRef>::Element *G=F->get().notifiers.front();G;G=G->next()) { + + Map<VisibilityNotifier2D*,uint64_t>::Element *H=E->get().notifiers.find(G->key()); + if (!H) { + + H=E->get().notifiers.insert(G->key(),pass); + added.push_back(G->key()); + } else { + H->get()=pass; + } + } + } + } } for (Map<VisibilityNotifier2D*,uint64_t>::Element *F=E->get().notifiers.front();F;F=F->next()) { |