/*************************************************************************/ /* property_editor.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 "property_editor.h" #include "io/resource_loader.h" #include "io/image_loader.h" #include "object_type_db.h" #include "print_string.h" #include "globals.h" #include "scene/resources/font.h" #include "pair.h" #include "editor_settings.h" #include "editor_import_export.h" #include "editor_node.h" #include "multi_node_edit.h" #include "array_property_edit.h" #include "editor_help.h" #include "scene/resources/packed_scene.h" #include "os/input.h" #include "os/keyboard.h" void CustomPropertyEditor::_notification(int p_what) { if (p_what==NOTIFICATION_DRAW) { RID ci = get_canvas_item(); get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size())); } else if (p_what==NOTIFICATION_POPUP_HIDE) { if (!text_changed) return; if (Input::get_singleton()->is_key_pressed(KEY_ESCAPE)) return; _modified(String()); } } void CustomPropertyEditor::_menu_option(int p_which) { switch(type) { case Variant::INT: { if (hint==PROPERTY_HINT_FLAGS) { int val = v; if (val&(1<set_mode(EditorFileDialog::MODE_OPEN_FILE); String type=(hint==PROPERTY_HINT_RESOURCE_TYPE)?hint_text:String(); List extensions; for (int i=0;i valid_extensions; for (List::Element *E=extensions.front();E;E=E->next()) { valid_extensions.insert(E->get()); } file->clear_filters(); for (Set::Element *E=valid_extensions.front();E;E=E->next()) { file->add_filter("*."+E->get()+" ; "+E->get().to_upper() ); } file->popup_centered_ratio(); } break; case OBJ_MENU_EDIT: { RefPtr RefPtr=v; if (!RefPtr.is_null()) { emit_signal("resource_edit_request"); hide(); } } break; case OBJ_MENU_CLEAR: { v=Variant(); emit_signal("variant_changed"); hide(); } break; case OBJ_MENU_MAKE_UNIQUE: { RefPtr RefPtr=v; Ref res_orig = RefPtr; if (res_orig.is_null()) return; List property_list; res_orig->get_property_list(&property_list); List< Pair > propvalues; for(List::Element *E=property_list.front();E;E=E->next()) { Pair p; PropertyInfo &pi = E->get(); if (pi.usage&PROPERTY_USAGE_STORAGE) { p.first=pi.name; p.second=res_orig->get(pi.name); } propvalues.push_back(p); } String orig_type = res_orig->get_type(); Object *inst = ObjectTypeDB::instance( orig_type ); Ref res = Ref( inst->cast_to() ); ERR_FAIL_COND(res.is_null()); for(List< Pair >::Element *E=propvalues.front();E;E=E->next()) { Pair &p=E->get(); res->set(p.first,p.second); } v=res.get_ref_ptr(); emit_signal("variant_changed"); hide(); } break; case OBJ_MENU_COPY: { EditorSettings::get_singleton()->set_resource_clipboard(v); } break; case OBJ_MENU_PASTE: { v=EditorSettings::get_singleton()->get_resource_clipboard(); emit_signal("variant_changed"); } break; case OBJ_MENU_REIMPORT: { RES r=v; if (r.is_valid() && r->get_import_metadata().is_valid()) { Ref rimd = r->get_import_metadata(); Ref eip = EditorImportExport::get_singleton()->get_import_plugin_by_name(rimd->get_editor()); if (eip.is_valid()) { eip->import_dialog(r->get_path()); } } } break; default: { ERR_FAIL_COND( inheritors_array.empty() ); String intype=inheritors_array[p_which-TYPE_BASE_ID]; Object *obj = ObjectTypeDB::instance(intype); ERR_BREAK( !obj ); Resource *res=obj->cast_to(); ERR_BREAK( !res ); v=Ref(res).get_ref_ptr(); emit_signal("variant_changed"); } break; } } break; default:{} } } Variant CustomPropertyEditor::get_variant() const { return v; } String CustomPropertyEditor::get_name() const { return name; } bool CustomPropertyEditor::edit(Object* p_owner,const String& p_name,Variant::Type p_type, const Variant& p_variant,int p_hint,String p_hint_text) { text_changed=false; owner=p_owner; updating=true; name=p_name; v=p_variant; hint=p_hint; hint_text=p_hint_text; type_button->hide(); color_picker->hide(); texture_preview->hide(); inheritors_array.clear(); text_edit->hide(); easing_draw->hide(); spinbox->hide(); slider->hide(); for (int i=0;ihide(); value_label[i]->hide(); } for (int i=0;ihide(); } for(int i=0;i<20;i++) checks20[i]->hide(); type = (p_variant.get_type()!=Variant::NIL && p_variant.get_type()!=Variant::_RID && p_type!=Variant::OBJECT)? p_variant.get_type() : p_type; switch(type) { case Variant::INT: case Variant::REAL: { if (hint==PROPERTY_HINT_RANGE) { int c = hint_text.get_slice_count(","); float min=0,max=100,step=1; if (c>=1) { if (!hint_text.get_slice(",",0).empty()) min=hint_text.get_slice(",",0).to_double(); } if (c>=2) { if (!hint_text.get_slice(",",1).empty()) max=hint_text.get_slice(",",1).to_double(); } if (type==Variant::REAL && c>=3) { if (!hint_text.get_slice(",",2).empty()) step= hint_text.get_slice(",",2).to_double(); } if (c>=4 && hint_text.get_slice(",",3)=="slider") { slider->set_min(min); slider->set_max(max); slider->set_step((type==Variant::REAL) ? step : 1); slider->set_val(v); slider->show(); set_size(Size2(110,30)); } else { spinbox->set_min(min); spinbox->set_max(max); spinbox->set_step((type==Variant::REAL) ? step : 1); spinbox->set_val(v); spinbox->show(); set_size(Size2(70,35)); } } else if (hint==PROPERTY_HINT_ALL_FLAGS) { uint32_t flgs = v; for(int i=0;i<2;i++) { Point2 ofs(4,4); ofs.y+=22*i; for(int j=0;j<10;j++) { Button *c=checks20[i*10+j]; Point2 o=ofs; o.x+=j*22; if (j>=5) o.x+=4; c->set_pos(o); c->set_pressed( flgs & (1<<(i*10+j)) ); c->show(); } } set_size(checks20[19]->get_pos()+Size2(20,25)); } else if (hint==PROPERTY_HINT_EXP_EASING) { easing_draw->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,5); easing_draw->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,5); easing_draw->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,5); easing_draw->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,30); type_button->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,3); type_button->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,3); type_button->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,25); type_button->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,7); type_button->set_text("Preset.."); type_button->get_popup()->clear(); type_button->get_popup()->add_item("Linear",EASING_LINEAR); type_button->get_popup()->add_item("Ease In",EASING_EASE_IN); type_button->get_popup()->add_item("Ease Out",EASING_EASE_OUT); if (hint_text!="attenuation") { type_button->get_popup()->add_item("Zero",EASING_ZERO); type_button->get_popup()->add_item("Easing In-Out",EASING_IN_OUT); type_button->get_popup()->add_item("Easing Out-In",EASING_OUT_IN); } type_button->show(); easing_draw->show(); set_size(Size2(200,150)); } else if (hint==PROPERTY_HINT_FLAGS) { menu->clear(); Vector flags = hint_text.split(","); for(int i=0;iadd_check_item(flag,i); int f = v; if (f&(1<set_item_checked(menu->get_item_index(i),true); } menu->set_pos(get_pos()); menu->popup(); hide(); updating=false; return false; } else { List names; names.push_back("value:"); config_value_editors(1,1,50,names); Vector3 vec=v; value_editor[0]->set_text( String::num(v) ); } } break; case Variant::STRING: { if (hint==PROPERTY_HINT_FILE || hint==PROPERTY_HINT_GLOBAL_FILE) { List names; names.push_back("File.."); names.push_back("Clear"); config_action_buttons(names); } else if (hint==PROPERTY_HINT_DIR || hint==PROPERTY_HINT_GLOBAL_DIR) { List names; names.push_back("Dir.."); names.push_back("Clear"); config_action_buttons(names); } else if (hint==PROPERTY_HINT_ENUM) { } else if (hint==PROPERTY_HINT_MULTILINE_TEXT) { text_edit->show(); text_edit->set_text(v); //action_buttons[0]; int button_margin = get_constant("button_margin","Dialogs"); int margin = get_constant("margin","Dialogs"); action_buttons[0]->set_anchor( MARGIN_LEFT, ANCHOR_END ); action_buttons[0]->set_anchor( MARGIN_TOP, ANCHOR_END ); action_buttons[0]->set_anchor( MARGIN_RIGHT, ANCHOR_END ); action_buttons[0]->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); action_buttons[0]->set_begin( Point2( 70, button_margin-5 ) ); action_buttons[0]->set_end( Point2( margin, margin ) ); action_buttons[0]->set_text("Close"); action_buttons[0]->show(); } else { List names; names.push_back("string:"); config_value_editors(1,1,50,names); Vector3 vec=v; value_editor[0]->set_text( v ); } } break; case Variant::VECTOR2: { List names; names.push_back("x"); names.push_back("y"); config_value_editors(2,2,10,names); Vector2 vec=v; value_editor[0]->set_text( String::num( vec.x) ); value_editor[1]->set_text( String::num( vec.y) ); } break; case Variant::RECT2: { List names; names.push_back("x"); names.push_back("y"); names.push_back("w"); names.push_back("h"); config_value_editors(4,4,10,names); Rect2 r=v; value_editor[0]->set_text( String::num( r.pos.x) ); value_editor[1]->set_text( String::num( r.pos.y) ); value_editor[2]->set_text( String::num( r.size.x) ); value_editor[3]->set_text( String::num( r.size.y) ); } break; case Variant::VECTOR3: { List names; names.push_back("x"); names.push_back("y"); names.push_back("z"); config_value_editors(3,3,10,names); Vector3 vec=v; value_editor[0]->set_text( String::num( vec.x) ); value_editor[1]->set_text( String::num( vec.y) ); value_editor[2]->set_text( String::num( vec.z) ); } break; case Variant::PLANE: { List names; names.push_back("x"); names.push_back("y"); names.push_back("z"); names.push_back("d"); config_value_editors(4,4,10,names); Plane plane=v; value_editor[0]->set_text( String::num( plane.normal.x ) ); value_editor[1]->set_text( String::num( plane.normal.y ) ); value_editor[2]->set_text( String::num( plane.normal.z ) ); value_editor[3]->set_text( String::num( plane.d ) ); } break; case Variant::QUAT: { List names; names.push_back("x"); names.push_back("y"); names.push_back("z"); names.push_back("w"); config_value_editors(4,4,10,names); Quat q=v; value_editor[0]->set_text( String::num( q.x ) ); value_editor[1]->set_text( String::num( q.y ) ); value_editor[2]->set_text( String::num( q.z ) ); value_editor[3]->set_text( String::num( q.w ) ); } break; case Variant::_AABB: { List names; names.push_back("px"); names.push_back("py"); names.push_back("pz"); names.push_back("sx"); names.push_back("sy"); names.push_back("sz"); config_value_editors(6,3,16,names); AABB aabb=v; value_editor[0]->set_text( String::num( aabb.pos.x ) ); value_editor[1]->set_text( String::num( aabb.pos.y ) ); value_editor[2]->set_text( String::num( aabb.pos.z ) ); value_editor[3]->set_text( String::num( aabb.size.x ) ); value_editor[4]->set_text( String::num( aabb.size.y ) ); value_editor[5]->set_text( String::num( aabb.size.z ) ); } break; case Variant::MATRIX32: { List names; names.push_back("xx"); names.push_back("xy"); names.push_back("yx"); names.push_back("yy"); names.push_back("ox"); names.push_back("oy"); config_value_editors(6,2,16,names); Matrix32 basis=v; for(int i=0;i<6;i++) { value_editor[i]->set_text( String::num( basis.elements[i/2][i%2] ) ); } } break; case Variant::MATRIX3: { List names; names.push_back("xx"); names.push_back("xy"); names.push_back("xz"); names.push_back("yx"); names.push_back("yy"); names.push_back("yz"); names.push_back("zx"); names.push_back("zy"); names.push_back("zz"); config_value_editors(9,3,16,names); Matrix3 basis=v; for(int i=0;i<9;i++) { value_editor[i]->set_text( String::num( basis.elements[i/3][i%3] ) ); } } break; case Variant::TRANSFORM: { List names; names.push_back("xx"); names.push_back("xy"); names.push_back("xz"); names.push_back("xo"); names.push_back("yx"); names.push_back("yy"); names.push_back("yz"); names.push_back("yo"); names.push_back("zx"); names.push_back("zy"); names.push_back("zz"); names.push_back("zo"); config_value_editors(12,4,16,names); Transform tr=v; for(int i=0;i<9;i++) { value_editor[(i/3)*4+i%3]->set_text( String::num( tr.basis.elements[i/3][i%3] ) ); } value_editor[3]->set_text( String::num( tr.origin.x ) ); value_editor[7]->set_text( String::num( tr.origin.y ) ); value_editor[11]->set_text( String::num( tr.origin.z ) ); } break; case Variant::COLOR: { color_picker->show(); color_picker->set_edit_alpha(hint!=PROPERTY_HINT_COLOR_NO_ALPHA); color_picker->set_color(v); set_size( Size2(300, color_picker->get_combined_minimum_size().height+10)); } break; case Variant::IMAGE: { List names; names.push_back("New"); names.push_back("Load"); names.push_back("Clear"); config_action_buttons(names); } break; case Variant::NODE_PATH: { List names; names.push_back("Assign"); names.push_back("Clear"); config_action_buttons(names); } break; case Variant::OBJECT: { if (hint!=PROPERTY_HINT_RESOURCE_TYPE) break; menu->clear(); menu->set_size(Size2(1,1)); if (hint_text!="") { int idx=0; for(int i=0;i valid_inheritors; valid_inheritors.insert(base); List inheritors; ObjectTypeDB::get_inheriters_from(base.strip_edges(),&inheritors); List::Element *E=inheritors.front(); while(E) { valid_inheritors.insert(E->get()); E=E->next(); } for(Set::Element *E=valid_inheritors.front();E;E=E->next()) { String t = E->get(); if (!ObjectTypeDB::can_instance(t)) continue; inheritors_array.push_back(t); int id = TYPE_BASE_ID+idx; if (has_icon(t,"EditorIcons")) { menu->add_icon_item(get_icon(t,"EditorIcons"),"New "+t,id); } else { menu->add_item("New "+t,id); } idx++; } } if (menu->get_item_count()) menu->add_separator(); } menu->add_icon_item(get_icon("Load","EditorIcons"),"Load",OBJ_MENU_LOAD); if (!RES(v).is_null()) { menu->add_icon_item(get_icon("EditResource","EditorIcons"),"Edit",OBJ_MENU_EDIT); menu->add_icon_item(get_icon("Del","EditorIcons"),"Clear",OBJ_MENU_CLEAR); menu->add_icon_item(get_icon("Duplicate","EditorIcons"),"Make Unique",OBJ_MENU_MAKE_UNIQUE); RES r = v; if (r.is_valid() && r->get_path().is_resource_file() && r->get_import_metadata().is_valid()) { menu->add_separator(); menu->add_icon_item(get_icon("Reload","EditorIcons"),"Re-Import",OBJ_MENU_REIMPORT); } /*if (r.is_valid() && r->get_path().is_resource_file()) { menu->set_item_tooltip(1,r->get_path()); } else if (r.is_valid()) { menu->set_item_tooltip(1,r->get_name()+" ("+r->get_type()+")"); }*/ } else { } RES cb=EditorSettings::get_singleton()->get_resource_clipboard(); bool paste_valid=false; if (cb.is_valid()) { if (hint_text=="") paste_valid=true; else for (int i = 0; i < hint_text.get_slice_count(",");i++) if (ObjectTypeDB::is_type(cb->get_type(),hint_text.get_slice(",",i))) { paste_valid=true; break; } } if (!RES(v).is_null() || paste_valid) { menu->add_separator(); if (!RES(v).is_null()) { menu->add_item("Copy",OBJ_MENU_COPY); } if (paste_valid) { menu->add_item("Paste",OBJ_MENU_PASTE); } } menu->set_pos(get_pos()); menu->popup(); hide(); updating=false; return false; } break; case Variant::INPUT_EVENT: { } break; case Variant::DICTIONARY: { } break; case Variant::RAW_ARRAY: { } break; case Variant::INT_ARRAY: { } break; case Variant::REAL_ARRAY: { } break; case Variant::STRING_ARRAY: { } break; case Variant::VECTOR3_ARRAY: { } break; case Variant::COLOR_ARRAY: { } break; default: {} } updating=false; return true; } void CustomPropertyEditor::_file_selected(String p_file) { switch(type) { case Variant::STRING: { if (hint==PROPERTY_HINT_FILE || hint==PROPERTY_HINT_DIR) { v=Globals::get_singleton()->localize_path(p_file); emit_signal("variant_changed"); hide(); } if (hint==PROPERTY_HINT_GLOBAL_FILE || hint==PROPERTY_HINT_GLOBAL_DIR) { v=p_file; emit_signal("variant_changed"); hide(); } } break; case Variant::OBJECT: { String type=(hint==PROPERTY_HINT_RESOURCE_TYPE)?hint_text:String(); RES res = ResourceLoader::load(p_file,type); if (res.is_null()) { error->set_text("Error loading file: Not a resource!"); error->popup_centered_minsize(); break; } v=res.get_ref_ptr(); emit_signal("variant_changed"); hide(); } break; case Variant::IMAGE: { Image image; Error err = ImageLoader::load_image(p_file,&image); ERR_EXPLAIN("Couldn't load image"); ERR_FAIL_COND(err); v=image; emit_signal("variant_changed"); hide(); } break; default: {} } } void CustomPropertyEditor::_type_create_selected(int p_idx) { if (type==Variant::INT || type==Variant::REAL) { float newval=0; switch(p_idx) { case EASING_LINEAR: { newval=1; } break; case EASING_EASE_IN: { newval=2.0; } break; case EASING_EASE_OUT: { newval=0.5; } break; case EASING_ZERO: { newval=0; } break; case EASING_IN_OUT: { newval=-0.5; } break; case EASING_OUT_IN: { newval=-2.0; } break; } v=newval; emit_signal("variant_changed"); easing_draw->update(); } else if (type==Variant::OBJECT) { ERR_FAIL_INDEX(p_idx,inheritors_array.size()); //List inheritors; //ObjectTypeDB::get_inheriters_from(hint_text,&inheritors); //inheritors.push_front(hint_text); //ERR_FAIL_INDEX( p_idx, inheritors.size() ); String intype=inheritors_array[p_idx]; Object *obj = ObjectTypeDB::instance(intype); ERR_FAIL_COND( !obj ); Resource *res=obj->cast_to(); ERR_FAIL_COND( !res ); v=Ref(res).get_ref_ptr(); emit_signal("variant_changed"); hide(); } } void CustomPropertyEditor::_color_changed(const Color& p_color) { v=p_color; emit_signal("variant_changed"); } void CustomPropertyEditor::_node_path_selected(NodePath p_path) { if (owner) { Node *node=NULL; if (owner->is_type("Node")) node = owner->cast_to(); else if (owner->is_type("ArrayPropertyEdit")) node = owner->cast_to()->get_node(); if (!node) { v=p_path; emit_signal("variant_changed"); call_deferred("hide"); //to not mess with dialogs return; } Node *tonode=node->get_node(p_path); if (tonode) { p_path=node->get_path_to(tonode); } } v=p_path; emit_signal("variant_changed"); call_deferred("hide"); //to not mess with dialogs } void CustomPropertyEditor::_action_pressed(int p_which) { if (updating) return; switch(type) { case Variant::INT: { if (hint==PROPERTY_HINT_ALL_FLAGS) { uint32_t f = v; if (checks20[p_which]->is_pressed()) f|=(1<set_access(EditorFileDialog::ACCESS_RESOURCES); else file->set_access(EditorFileDialog::ACCESS_FILESYSTEM); file->set_mode(EditorFileDialog::MODE_OPEN_FILE); file->clear_filters(); file->clear_filters(); if (hint_text!="") { Vector extensions=hint_text.split(","); for(int i=0;iadd_filter(filter+" ; "+extensions[i].to_upper() ); } } file->popup_centered_ratio(); } else { v=""; emit_signal("variant_changed"); hide(); } } else if (hint==PROPERTY_HINT_DIR || hint==PROPERTY_HINT_GLOBAL_DIR) { if (p_which==0) { if (hint==PROPERTY_HINT_DIR) file->set_access(EditorFileDialog::ACCESS_RESOURCES); else file->set_access(EditorFileDialog::ACCESS_FILESYSTEM); file->set_mode(EditorFileDialog::MODE_OPEN_DIR); file->clear_filters(); file->popup_centered_ratio(); } else { v=""; emit_signal("variant_changed"); hide(); } } } break; case Variant::NODE_PATH: { if (p_which==0) { scene_tree->popup_centered_ratio(); } else if (p_which==1) { v=NodePath(); emit_signal("variant_changed"); hide(); } } break; case Variant::OBJECT: { if (p_which==0) { ERR_FAIL_COND( inheritors_array.empty() ); String intype=inheritors_array[0]; if (hint==PROPERTY_HINT_RESOURCE_TYPE) { Object *obj = ObjectTypeDB::instance(intype); ERR_BREAK( !obj ); Resource *res=obj->cast_to(); ERR_BREAK( !res ); v=Ref(res).get_ref_ptr(); emit_signal("variant_changed"); hide(); } } else if (p_which==1) { file->set_access(EditorFileDialog::ACCESS_RESOURCES); file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List extensions; String type=(hint==PROPERTY_HINT_RESOURCE_TYPE)?hint_text:String(); ResourceLoader::get_recognized_extensions_for_type(type,&extensions); file->clear_filters(); for (List::Element *E=extensions.front();E;E=E->next()) { file->add_filter("*."+E->get()+" ; "+E->get().to_upper() ); } file->popup_centered_ratio(); } else if (p_which==2) { RefPtr RefPtr=v; if (!RefPtr.is_null()) { emit_signal("resource_edit_request"); hide(); } } else if (p_which==3) { v=Variant(); emit_signal("variant_changed"); hide(); } else if (p_which==4) { RefPtr RefPtr=v; Ref res_orig = RefPtr; if (res_orig.is_null()) return; List property_list; res_orig->get_property_list(&property_list); List< Pair > propvalues; for(List::Element *E=property_list.front();E;E=E->next()) { Pair p; PropertyInfo &pi = E->get(); if (pi.usage&PROPERTY_USAGE_STORAGE) { p.first=pi.name; p.second=res_orig->get(pi.name); } propvalues.push_back(p); } Ref res = Ref( ObjectTypeDB::instance( res_orig->get_type() )); ERR_FAIL_COND(res.is_null()); for(List< Pair >::Element *E=propvalues.front();E;E=E->next()) { Pair &p=E->get(); res->set(p.first,p.second); } v=res.get_ref_ptr(); emit_signal("variant_changed"); hide(); } } break; case Variant::IMAGE: { if (p_which==0) { //new image too difficult ERR_PRINT("New Image Unimplemented"); } else if (p_which==1) { file->set_access(EditorFileDialog::ACCESS_RESOURCES); file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List extensions; ImageLoader::get_recognized_extensions(&extensions); file->clear_filters(); for (List::Element *E=extensions.front();E;E=E->next()) { file->add_filter("*."+E->get()+" ; "+E->get().to_upper() ); } file->popup_centered_ratio(); } else if (p_which==2) { v=Image(); emit_signal("variant_changed"); hide(); } } break; default: {}; } } void CustomPropertyEditor::_drag_easing(const InputEvent& p_ev) { if (p_ev.type==InputEvent::MOUSE_MOTION && p_ev.mouse_motion.button_mask&BUTTON_MASK_LEFT) { float rel = p_ev.mouse_motion.relative_x; if (rel==0) return; bool flip=hint_text=="attenuation"; if (flip) rel=-rel; float val = v; if (val==0) return; bool sg = val < 0; val = Math::absf(val); val = Math::log(val)/Math::log(2); //logspace val+=rel*0.05; // val = Math::pow(2,val); if (sg) val=-val; v=val; easing_draw->update(); //emit_signal("variant_changed"); emit_signal("variant_changed"); } if (p_ev.type==InputEvent::MOUSE_BUTTON && p_ev.mouse_button.button_index==BUTTON_LEFT) { } } void CustomPropertyEditor::_draw_easing() { RID ci = easing_draw->get_canvas_item(); Size2 s = easing_draw->get_size(); Rect2 r(Point2(),s); r=r.grow(3); get_stylebox("normal","LineEdit")->draw(ci,r); //VisualServer::get_singleton()->canvas_item_add int points = 48; float prev=1.0; float exp=v; bool flip=hint_text=="attenuation"; Ref f = get_font("font","Label"); Color color = get_color("font_color","Label"); for(int i=1;i<=points;i++) { float ifl = i/float(points); float iflp = (i-1)/float(points); float h = 1.0-Math::ease(ifl,exp); if (flip) { ifl=1.0-ifl; iflp=1.0-iflp; } VisualServer::get_singleton()->canvas_item_add_line(ci,Point2(iflp*s.width,prev*s.height),Point2(ifl*s.width,h*s.height),color); prev=h; } f->draw(ci,Point2(10,10+f->get_ascent()),String::num(exp,2),color); } void CustomPropertyEditor::_text_edit_changed() { v=text_edit->get_text(); emit_signal("variant_changed"); } void CustomPropertyEditor::_modified(String p_string) { if (updating) return; updating=true; text_changed=false; switch(type) { case Variant::REAL: { if (hint!=PROPERTY_HINT_EXP_EASING) { v=value_editor[0]->get_text().to_double(); emit_signal("variant_changed"); } } break; case Variant::STRING: { v=value_editor[0]->get_text(); emit_signal("variant_changed"); } break; case Variant::VECTOR2: { Vector2 vec; vec.x=value_editor[0]->get_text().to_double(); vec.y=value_editor[1]->get_text().to_double(); v=vec; emit_signal("variant_changed"); } break; case Variant::RECT2: { Rect2 r2; r2.pos.x=value_editor[0]->get_text().to_double(); r2.pos.y=value_editor[1]->get_text().to_double(); r2.size.x=value_editor[2]->get_text().to_double(); r2.size.y=value_editor[3]->get_text().to_double(); v=r2; emit_signal("variant_changed"); } break; case Variant::VECTOR3: { Vector3 vec; vec.x=value_editor[0]->get_text().to_double(); vec.y=value_editor[1]->get_text().to_double(); vec.z=value_editor[2]->get_text().to_double(); v=vec; emit_signal("variant_changed"); } break; case Variant::PLANE: { Plane pl; pl.normal.x=value_editor[0]->get_text().to_double(); pl.normal.y=value_editor[1]->get_text().to_double(); pl.normal.z=value_editor[2]->get_text().to_double(); pl.d=value_editor[3]->get_text().to_double(); v=pl; emit_signal("variant_changed"); } break; case Variant::QUAT: { Quat q; q.x=value_editor[0]->get_text().to_double(); q.y=value_editor[1]->get_text().to_double(); q.z=value_editor[2]->get_text().to_double(); q.w=value_editor[3]->get_text().to_double(); v=q; emit_signal("variant_changed"); } break; case Variant::_AABB: { Vector3 pos; pos.x=value_editor[0]->get_text().to_double(); pos.y=value_editor[1]->get_text().to_double(); pos.z=value_editor[2]->get_text().to_double(); Vector3 size; size.x=value_editor[3]->get_text().to_double(); size.y=value_editor[4]->get_text().to_double(); size.z=value_editor[5]->get_text().to_double(); v=AABB(pos,size); emit_signal("variant_changed"); } break; case Variant::MATRIX32: { Matrix32 m; for(int i=0;i<6;i++) { m.elements[i/2][i%2]=value_editor[i]->get_text().to_double(); } v=m; emit_signal("variant_changed"); } break; case Variant::MATRIX3: { Matrix3 m; for(int i=0;i<9;i++) { m.elements[i/3][i%3]=value_editor[i]->get_text().to_double(); } v=m; emit_signal("variant_changed"); } break; case Variant::TRANSFORM: { Matrix3 basis; for(int i=0;i<9;i++) { basis.elements[i/3][i%3]=value_editor[(i/3)*4+i%3]->get_text().to_double(); } Vector3 origin; origin.x=value_editor[3]->get_text().to_double(); origin.y=value_editor[7]->get_text().to_double(); origin.z=value_editor[11]->get_text().to_double(); v=Transform(basis,origin); emit_signal("variant_changed"); } break; case Variant::COLOR: { } break; case Variant::IMAGE: { } break; case Variant::NODE_PATH: { } break; case Variant::INPUT_EVENT: { } break; case Variant::DICTIONARY: { } break; case Variant::RAW_ARRAY: { } break; case Variant::INT_ARRAY: { } break; case Variant::REAL_ARRAY: { } break; case Variant::STRING_ARRAY: { } break; case Variant::VECTOR3_ARRAY: { } break; case Variant::COLOR_ARRAY: { } break; default: {} } updating=false; } void CustomPropertyEditor::_range_modified(double p_value) { v=p_value; emit_signal("variant_changed"); } void CustomPropertyEditor::_focus_enter() { switch(type) { case Variant::REAL: case Variant::STRING: case Variant::VECTOR2: case Variant::RECT2: case Variant::VECTOR3: case Variant::PLANE: case Variant::QUAT: case Variant::_AABB: case Variant::MATRIX32: case Variant::MATRIX3: case Variant::TRANSFORM: { for (int i=0;ihas_focus()) { value_editor[i]->select_all(); break; } } } break; default: {} } } void CustomPropertyEditor::_focus_exit() { switch(type) { case Variant::REAL: case Variant::STRING: case Variant::VECTOR2: case Variant::RECT2: case Variant::VECTOR3: case Variant::PLANE: case Variant::QUAT: case Variant::_AABB: case Variant::MATRIX32: case Variant::MATRIX3: case Variant::TRANSFORM: { for (int i=0;iselect(0, 0); } } break; default: {} } } void CustomPropertyEditor::config_action_buttons(const List& p_strings) { int w=100; int h=18; int m=5; set_size( Size2( w, m*2+(h+m)*p_strings.size() ) ); for (int i=0;ishow(); action_buttons[i]->set_text(p_strings[i]); action_buttons[i]->set_pos( Point2( m, m+i*(h+m) )); action_buttons[i]->set_size( Size2( w-m*2, h ) ); action_buttons[i]->set_flat(true); } else { action_buttons[i]->hide(); } } } void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns,int p_label_w,const List& p_strings) { int w=80; int h=20; int m=10; int rows=((p_amount-1)/p_columns)+1; set_size( Size2( m*(1+p_columns)+(w+p_label_w)*p_columns, m*(1+rows)+h*rows ) ); for (int i=0;ishow(); value_label[i]->show(); value_label[i]->set_text(iset_pos( Point2( m+p_label_w+c*(w+m+p_label_w), m+r*(h+m) )); value_editor[i]->set_size( Size2( w, h ) ); value_label[i]->set_pos( Point2( m+c*(w+m+p_label_w), m+r*(h+m) ) ); value_editor[i]->set_editable(!read_only); } else { value_editor[i]->hide(); value_label[i]->hide(); } } } void CustomPropertyEditor::_text_editor_changed(String p_text) { text_changed=true; } void CustomPropertyEditor::_bind_methods() { ObjectTypeDB::bind_method("_focus_enter", &CustomPropertyEditor::_focus_enter); ObjectTypeDB::bind_method("_focus_exit", &CustomPropertyEditor::_focus_exit); ObjectTypeDB::bind_method("_modified",&CustomPropertyEditor::_modified); ObjectTypeDB::bind_method("_range_modified", &CustomPropertyEditor::_range_modified); ObjectTypeDB::bind_method("_action_pressed",&CustomPropertyEditor::_action_pressed); ObjectTypeDB::bind_method("_file_selected",&CustomPropertyEditor::_file_selected); ObjectTypeDB::bind_method("_type_create_selected",&CustomPropertyEditor::_type_create_selected); ObjectTypeDB::bind_method("_node_path_selected",&CustomPropertyEditor::_node_path_selected); ObjectTypeDB::bind_method("_color_changed",&CustomPropertyEditor::_color_changed); ObjectTypeDB::bind_method("_draw_easing",&CustomPropertyEditor::_draw_easing); ObjectTypeDB::bind_method("_drag_easing",&CustomPropertyEditor::_drag_easing); ObjectTypeDB::bind_method("_text_edit_changed",&CustomPropertyEditor::_text_edit_changed); ObjectTypeDB::bind_method("_menu_option",&CustomPropertyEditor::_menu_option); ObjectTypeDB::bind_method("_text_editor_changed",&CustomPropertyEditor::_text_editor_changed); ADD_SIGNAL( MethodInfo("variant_changed") ); ADD_SIGNAL( MethodInfo("resource_edit_request") ); } CustomPropertyEditor::CustomPropertyEditor() { read_only=false; updating=false; text_changed=false; for (int i=0;ihide(); value_label[i]->hide(); value_editor[i]->connect("text_entered", this,"_modified"); value_editor[i]->connect("text_changed", this, "_text_editor_changed"); value_editor[i]->connect("focus_enter", this, "_focus_enter"); value_editor[i]->connect("focus_exit", this, "_focus_exit"); } for(int i=0;i<20;i++) { checks20[i]=memnew( Button ); checks20[i]->set_toggle_mode(true); checks20[i]->set_focus_mode(FOCUS_NONE); add_child(checks20[i]); checks20[i]->hide(); checks20[i]->connect("pressed",this,"_action_pressed",make_binds(i)); checks20[i]->set_tooltip("Bit "+itos(i)+", val "+itos(1<set_area_as_parent_rect(); for(int i=0;i<4;i++) text_edit->set_margin((Margin)i,5); text_edit->set_margin(MARGIN_BOTTOM,30); text_edit->hide(); text_edit->connect("text_changed",this,"_text_edit_changed"); for (int i=0;ihide(); add_child(action_buttons[i]); Vector binds; binds.push_back(i); action_buttons[i]->connect("pressed", this,"_action_pressed",binds); } color_picker = memnew( ColorPicker ); add_child(color_picker); color_picker->hide(); color_picker->set_area_as_parent_rect(); for(int i=0;i<4;i++) color_picker->set_margin((Margin)i,5); color_picker->connect("color_changed",this,"_color_changed"); set_as_toplevel(true); file = memnew ( EditorFileDialog ); add_child(file); file->hide(); file->connect("file_selected", this,"_file_selected"); file->connect("dir_selected", this,"_file_selected"); error = memnew( ConfirmationDialog ); error->set_title("Error!"); add_child(error); //error->get_cancel()->hide(); type_button = memnew( MenuButton ); add_child(type_button); type_button->hide(); type_button->get_popup()->connect("item_pressed", this,"_type_create_selected"); scene_tree = memnew( SceneTreeDialog ); add_child(scene_tree); scene_tree->connect("selected", this,"_node_path_selected"); scene_tree->get_scene_tree()->set_show_enabled_subscene(true); texture_preview = memnew( TextureFrame ); add_child( texture_preview); texture_preview->hide(); easing_draw=memnew( Control ); add_child(easing_draw); easing_draw->hide(); easing_draw->connect("draw",this,"_draw_easing"); easing_draw->connect("input_event",this,"_drag_easing"); easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE); menu = memnew(PopupMenu); add_child(menu); menu->connect("item_pressed",this,"_menu_option"); spinbox = memnew ( SpinBox ); add_child(spinbox); spinbox->set_area_as_parent_rect(5); spinbox->connect("value_changed",this,"_range_modified"); slider = memnew ( HSlider ); add_child(slider); slider->set_area_as_parent_rect(5); slider->connect("value_changed",this,"_range_modified"); } bool PropertyEditor::_might_be_in_instance() { if (!obj) return NULL; Node *node = obj->cast_to(); Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); bool might_be=false; while(node) { if (node->get_scene_instance_state().is_valid()) { might_be=true; break; } if (node==edited_scene) { if (node->get_scene_inherited_state().is_valid()) { might_be=true; break; } might_be=false; break; } node=node->get_owner(); } return might_be; } bool PropertyEditor::_get_instanced_node_original_property(const StringName& p_prop, Variant& value) { Node *node = obj->cast_to(); if (!node) return false; Node *orig=node; Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); bool found=false; // print_line("for prop - "+String(p_prop)); while(node) { Ref ss; if (node==edited_scene) { ss=node->get_scene_inherited_state(); } else { ss=node->get_scene_instance_state(); } // print_line("at - "+String(edited_scene->get_path_to(node))); if (ss.is_valid()) { NodePath np = node->get_path_to(orig); int node_idx = ss->find_node_by_path(np); // print_line("\t valid, nodeidx "+itos(node_idx)); if (node_idx>=0) { bool lfound=false; Variant lvar; lvar=ss->get_property_value(node_idx,p_prop,lfound); if (lfound) { found=true; value=lvar; // print_line("\t found value "+String(value)); } } } if (node==edited_scene) { //just in case break; } node=node->get_owner(); } return found; } bool PropertyEditor::_is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage) { { Node *node = obj->cast_to(); if (!node) return false; Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); bool found_state=false; // print_line("for prop - "+String(p_prop)); while(node) { Ref ss; if (node==edited_scene) { ss=node->get_scene_inherited_state(); } else { ss=node->get_scene_instance_state(); } if (ss.is_valid()) { found_state=true; } if (node==edited_scene) { //just in case break; } node=node->get_owner(); } if (!found_state) return false; //pointless to check if we are not comparing against anything. } if (p_orig.get_type()==Variant::NIL) { //special cases if (p_current.is_zero() && p_usage&PROPERTY_USAGE_STORE_IF_NONZERO) return false; if (p_current.is_one() && p_usage&PROPERTY_USAGE_STORE_IF_NONONE) return false; } return bool(Variant::evaluate(Variant::OP_NOT_EQUAL,p_current,p_orig)); } TreeItem *PropertyEditor::find_item(TreeItem *p_item,const String& p_name) { if (!p_item) return NULL; String name = p_item->get_metadata(1); if (name==p_name) { return p_item; } TreeItem *c=p_item->get_children(); while (c) { TreeItem *found = find_item(c,p_name); if (found) return found; c=c->get_next(); } return NULL; } void PropertyEditor::_changed_callback(Object *p_changed,const char * p_prop) { _changed_callbacks(p_changed,p_prop); } void PropertyEditor::_changed_callbacks(Object *p_changed,const String& p_prop) { if (p_changed!=obj) return; if (changing) return; if (p_prop==String()) update_tree_pending=true; else { pending[p_prop]=p_prop; } } void PropertyEditor::update_property(const String& p_prop) { if (obj) _changed_callbacks(obj,p_prop); } void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p_name, int p_hint, const String& p_hint_text) { switch( p_type ) { case Variant::BOOL: { p_item->set_checked( 1, obj->get( p_name ) ); } break; case Variant::REAL: case Variant::INT: { if (p_hint==PROPERTY_HINT_ALL_FLAGS) { tree->update(); break; } if (p_hint==PROPERTY_HINT_FLAGS) { Vector values = p_hint_text.split(","); String flags; int val = obj->get(p_name); for(int i=0;iset_text(1, flags ); break; } if (p_hint==PROPERTY_HINT_EXP_EASING) { p_item->set_text(1, String::num(obj->get( p_name ),2) ); break; } //p_item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE ); if (p_type==Variant::REAL) { p_item->set_range(1, obj->get( p_name ) ); } else { p_item->set_range(1, obj->get( p_name ) ); } p_item->set_editable(1,!read_only); } break; case Variant::STRING: if (p_hint==PROPERTY_HINT_ENUM) { Vector strings = p_hint_text.split(","); String current = obj->get(p_name); int idx=0; for(int x=0;xset_text(1, p_hint_text); p_item->set_range(1,idx); break; } case Variant::VECTOR3: case Variant::QUAT: case Variant::VECTOR2: case Variant::_AABB: case Variant::RECT2: case Variant::MATRIX32: case Variant::MATRIX3: case Variant::TRANSFORM: { p_item->set_text(1,obj->get(p_name)); } break; case Variant::COLOR: { p_item->set_custom_bg_color(1,obj->get(p_name)); //p_item->set_text(1,obj->get(p_name)); } break; case Variant::IMAGE: { Image img = obj->get( p_name ); if (img.empty()) p_item->set_text(1,"[Image (empty)]"); else p_item->set_text(1,"[Image "+itos(img.get_width())+"x"+itos(img.get_height())+"]"); } break; case Variant::NODE_PATH: { p_item->set_text(1,obj->get(p_name)); } break; case Variant::OBJECT: { if (obj->get( p_name ).get_type() == Variant::NIL || obj->get( p_name ).operator RefPtr().is_null()) { p_item->set_text(1,""); Dictionary d = p_item->get_metadata(0); int hint=d.has("hint")?d["hint"].operator int():-1; String hint_text=d.has("hint_text")?d["hint_text"]:""; if (hint==PROPERTY_HINT_RESOURCE_TYPE && hint_text == "Texture") { p_item->set_icon(1,NULL); } } else { RES res = obj->get( p_name ).operator RefPtr(); if (res->is_type("Texture")) { int tw = EditorSettings::get_singleton()->get("property_editor/texture_preview_width"); p_item->set_icon_max_width(1,tw); p_item->set_icon(1,res); p_item->set_text(1,""); } else if (res->get_name() != "") { p_item->set_text(1, res->get_name()); } else if (res->get_path()!="" && !res->get_path().begins_with("local://")) { p_item->set_text(1, res->get_path().get_file()); } else { p_item->set_text(1,"<"+res->get_type()+">"); }; if (res.is_valid() && res->get_path().is_resource_file()) { p_item->set_tooltip(1,res->get_path()); } else if (res.is_valid()) { p_item->set_tooltip(1,res->get_name()+" ("+res->get_type()+")"); } if (has_icon(res->get_type(),"EditorIcons")) { p_item->set_icon(0,get_icon(res->get_type(),"EditorIcons")); } else { Dictionary d = p_item->get_metadata(0); int hint=d.has("hint")?d["hint"].operator int():-1; String hint_text=d.has("hint_text")?d["hint_text"]:""; if (hint==PROPERTY_HINT_RESOURCE_TYPE) { if (has_icon(hint_text,"EditorIcons")) { p_item->set_icon(0,get_icon(hint_text,"EditorIcons")); } else { p_item->set_icon(0,get_icon("Object","EditorIcons")); } } } } } break; default: {}; } } void PropertyEditor::_check_reload_status(const String&p_name, TreeItem* item) { bool has_reload=false; int found=-1; for(int i=0;iget_button_count(1);i++) { if (item->get_button_id(1,i)==3) { found=i; break; } } if (_might_be_in_instance()) { Variant vorig; Dictionary d=item->get_metadata(0); int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0; if (_get_instanced_node_original_property(p_name,vorig) || usage) { Variant v = obj->get(p_name); bool changed = _is_property_different(v,vorig,usage); if ((found!=-1)!=changed) { if (changed) { has_reload=true; } else { } } } } if (!has_reload && !obj->get_script().is_null()) { Ref