diff options
34 files changed, 1453 insertions, 87 deletions
diff --git a/.gitignore b/.gitignore index 35fadafbda..6db75f2324 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ platform/android/java/build.gradle platform/android/java/AndroidManifest.xml platform/android/java/libs/* platform/android/java/assets +platform/android/java/.idea/* +platform/android/java/*.iml # General c++ generated files *.lib @@ -44,6 +46,7 @@ gmon.out # QT project files *.config *.creator +*.creator.* *.files *.includes diff --git a/core/dictionary.cpp b/core/dictionary.cpp index ba0de95861..d68411a572 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -50,6 +50,32 @@ void Dictionary::get_key_list(List<Variant> *p_keys) const { } } +Variant Dictionary::get_key_at_index(int p_index) const { + + int index = 0; + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.key(); + } + index++; + } + + return Variant(); +} + +Variant Dictionary::get_value_at_index(int p_index) const { + + int index = 0; + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { + if (index == p_index) { + return E.value(); + } + index++; + } + + return Variant(); +} + Variant &Dictionary::operator[](const Variant &p_key) { return _p->variant_map[p_key]; diff --git a/core/dictionary.h b/core/dictionary.h index 9eef265d5b..84a5cafe1d 100644 --- a/core/dictionary.h +++ b/core/dictionary.h @@ -47,6 +47,8 @@ class Dictionary { public: void get_key_list(List<Variant> *p_keys) const; + Variant get_key_at_index(int p_index) const; + Variant get_value_at_index(int p_index) const; Variant &operator[](const Variant &p_key); const Variant &operator[](const Variant &p_key) const; diff --git a/core/ustring.cpp b/core/ustring.cpp index 85b7a16e6a..51f05468e2 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -753,6 +753,46 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p return ret; } +Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int p_maxsplit) const { + + Vector<String> ret; + const int len = length(); + int from = len; + + while (true) { + + int end = rfind(p_splitter, from); + if (end < 0) + end = 0; + + if (p_allow_empty || (end < from)) { + const String str = substr(end > 0 ? end + p_splitter.length() : end, end > 0 ? from - end : from + 2); + + if (p_maxsplit <= 0) { + ret.push_back(str); + } else if (p_maxsplit > 0) { + + // Put rest of the string and leave cycle. + if (p_maxsplit == ret.size()) { + ret.push_back(substr(0, from + 2)); + break; + } + + // Otherwise, push items until positive limit is reached. + ret.push_back(str); + } + } + + if (end == 0) + break; + + from = end - p_splitter.length(); + } + + ret.invert(); + return ret; +} + Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty) const { Vector<float> ret; diff --git a/core/ustring.h b/core/ustring.h index 1ed694bb80..b57e9629d9 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -172,6 +172,7 @@ public: String get_slicec(CharType p_splitter, int p_slice) const; Vector<String> split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector<String> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; Vector<String> split_spaces() const; Vector<float> split_floats(const String &p_splitter, bool p_allow_empty = true) const; Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 4e883d496f..4158c2a60e 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -257,6 +257,7 @@ struct _VariantCall { VCALL_LOCALMEM2R(String, insert); VCALL_LOCALMEM0R(String, capitalize); VCALL_LOCALMEM3R(String, split); + VCALL_LOCALMEM3R(String, rsplit); VCALL_LOCALMEM2R(String, split_floats); VCALL_LOCALMEM0R(String, to_upper); VCALL_LOCALMEM0R(String, to_lower); @@ -1469,6 +1470,7 @@ void register_variant_methods() { ADDFUNC2R(STRING, STRING, String, insert, INT, "position", STRING, "what", varray()); ADDFUNC0R(STRING, STRING, String, capitalize, varray()); ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, split, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); + ADDFUNC3R(STRING, POOL_STRING_ARRAY, String, rsplit, STRING, "divisor", BOOL, "allow_empty", INT, "maxsplit", varray(true, 0)); ADDFUNC2R(STRING, POOL_REAL_ARRAY, String, split_floats, STRING, "divisor", BOOL, "allow_empty", varray(true)); ADDFUNC0R(STRING, STRING, String, to_upper, varray()); diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 83fb76f287..a55e184474 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -688,6 +688,20 @@ If [code]maxsplit[/code] is given, at most maxsplit number of splits occur, and the remainder of the string is returned as the final element of the list (thus, the list will have at most maxsplit+1 elements) </description> </method> + <method name="rsplit"> + <return type="PoolStringArray"> + </return> + <argument index="0" name="divisor" type="String"> + </argument> + <argument index="1" name="allow_empty" type="bool" default="True"> + </argument> + <argument index="2" name="maxsplit" type="int" default="0"> + </argument> + <description> + Splits the string by a [code]divisor[/code] string and returns an array of the substrings, starting from right. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". + If [code]maxsplit[/code] is specified, then it is number of splits to do, default is 0 which splits all the items. + </description> + </method> <method name="split_floats"> <return type="PoolRealArray"> </return> diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml index 7a4208ccea..95afc5d281 100644 --- a/doc/classes/TextureRect.xml +++ b/doc/classes/TextureRect.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="TextureRect" inherits="Control" category="Core" version="3.1"> <brief_description> - Draws a sprite or a texture inside a User Interface. The texture can tile or not. + Control for drawing textures. </brief_description> <description> - Use TextureRect to draw icons and sprites in your User Interfaces. To create panels and menu boxes, take a look at [NinePatchFrame]. Its Stretch Mode property controls the texture's scale and placement. It can scale, tile and stay centered inside its bounding rectangle. TextureRect is one of the 5 most common nodes to create game UI. + Used to draw icons and sprites in a user interface. The texture's placement can be controlled with the [member stretch_mode] property. It can scale, tile, or stay centered inside its bounding rectangle. </description> <tutorials> </tutorials> @@ -14,10 +14,10 @@ </methods> <members> <member name="expand" type="bool" setter="set_expand" getter="has_expand"> - If [code]true[/code], the texture scales to fit its bounding rectangle. Default value: [code]false[/code]. + If [code]true[/code] the texture scales to fit its bounding rectangle. Default value: [code]false[/code]. </member> <member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureRect.StretchMode"> - Controls the texture's behavior when you resize the node's bounding rectangle. Set it to one of the [code]STRETCH_*[/code] constants. See the constants to learn more. + Controls the texture's behavior when resizing the node's bounding rectangle. See [enum StretchMode]. </member> <member name="texture" type="Texture" setter="set_texture" getter="get_texture"> The node's [Texture] resource. diff --git a/doc/classes/VideoPlayer.xml b/doc/classes/VideoPlayer.xml index d2639590a1..9ffa3aa52b 100644 --- a/doc/classes/VideoPlayer.xml +++ b/doc/classes/VideoPlayer.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VideoPlayer" inherits="Control" category="Core" version="3.1"> <brief_description> - Control to play video files. + Control for playing video streams. </brief_description> <description> - This control has the ability to play video streams. The only format accepted is the OGV Theora, so any other format must be converted before using in a project. + Control node for playing video streams. Supported formats are WebM and OGV Theora. </description> <tutorials> </tutorials> @@ -15,51 +15,56 @@ <return type="String"> </return> <description> - Get the name of the video stream. + Returns the video stream's name. </description> </method> <method name="get_video_texture"> <return type="Texture"> </return> <description> - Get the current frame of the video as a [Texture]. + Returns the current frame as a [Texture]. </description> </method> <method name="is_playing" qualifiers="const"> <return type="bool"> </return> <description> - Get whether or not the video is playing. + Returns [code]true[/code] if the video is playing. </description> </method> <method name="play"> <return type="void"> </return> <description> - Start the video playback. + Starts the video playback. </description> </method> <method name="stop"> <return type="void"> </return> <description> - Stop the video playback. + Stops the video playback. </description> </method> </methods> <members> <member name="audio_track" type="int" setter="set_audio_track" getter="get_audio_track"> + The embedded audio track to play. </member> <member name="autoplay" type="bool" setter="set_autoplay" getter="has_autoplay"> + If [code]true[/code] playback starts when the scene loads. Default value: [code]false[/code]. </member> <member name="buffering_msec" type="int" setter="set_buffering_msec" getter="get_buffering_msec"> - The amount of milliseconds to store in buffer while playing. + Amount of time in milliseconds to store in buffer while playing. </member> <member name="bus" type="String" setter="set_bus" getter="get_bus"> + Audio bus to use for sound playback. </member> <member name="expand" type="bool" setter="set_expand" getter="has_expand"> + If [code]true[/code] the video scales to the control size. Default value: [code]true[/code]. </member> <member name="paused" type="bool" setter="set_paused" getter="is_paused"> + If [code]true[/code] the video is paused. </member> <member name="stream" type="VideoStream" setter="set_stream" getter="get_stream"> </member> @@ -67,14 +72,16 @@ The current position of the stream, in seconds. </member> <member name="volume" type="float" setter="set_volume" getter="get_volume"> - The volume of the audio track as a linear value. + Audio volume as a linear value. </member> <member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db"> + Audio volume in dB. </member> </members> <signals> <signal name="finished"> <description> + Emitted when playback is finished. </description> </signal> </signals> diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 4626a5ed3c..ab48e682d6 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -346,7 +346,7 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c if (OS::get_singleton()->is_layered_allowed()) { if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#ifdef WINDOWS_ENABLED +#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); if (data) { @@ -401,7 +401,7 @@ void RasterizerGLES2::end_frame(bool p_swap_buffers) { if (OS::get_singleton()->is_layered_allowed()) { if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#ifdef WINDOWS_ENABLED +#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); if (data) { diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 12e29827b0..1abdaa5f80 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -335,7 +335,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c if (OS::get_singleton()->is_layered_allowed()) { if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#ifdef WINDOWS_ENABLED +#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); if (data) { @@ -392,7 +392,7 @@ void RasterizerGLES3::end_frame(bool p_swap_buffers) { if (OS::get_singleton()->is_layered_allowed()) { if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#ifdef WINDOWS_ENABLED +#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); if (data) { diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index c2d2f7ddcc..f94b7cd6ee 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -520,6 +520,8 @@ bool EditorProperty::is_draw_red() const { void EditorProperty::_focusable_focused(int p_index) { + if (!selectable) + return; bool already_selected = selected; selected = true; selected_focusable = p_index; @@ -596,7 +598,7 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - if (!selected) { + if (!selected && selectable) { selected = true; emit_signal("selected", property, -1); update(); @@ -681,6 +683,19 @@ void EditorProperty::expand_all_folding() { void EditorProperty::collapse_all_folding() { } +void EditorProperty::set_selectable(bool p_selectable) { + selectable = p_selectable; +} + +bool EditorProperty::is_selectable() const { + return selectable; +} + +void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { + object = p_object; + property = p_property; +} + void EditorProperty::_bind_methods() { ClassDB::bind_method(D_METHOD("set_label", "text"), &EditorProperty::set_label); @@ -729,6 +744,7 @@ void EditorProperty::_bind_methods() { EditorProperty::EditorProperty() { + selectable = true; text_size = 0; read_only = false; checkable = false; diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 14387c46df..a6b183799f 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -72,6 +72,7 @@ private: bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value); void _focusable_focused(int p_index); + bool selectable; bool selected; int selected_focusable; @@ -130,6 +131,10 @@ public: virtual Variant get_drag_data(const Point2 &p_point); + void set_selectable(bool p_selectable); + bool is_selectable() const; + + void set_object_and_property(Object *p_object, const StringName &p_property); EditorProperty(); }; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 812e461a14..0625aeee54 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -31,7 +31,20 @@ #include "editor_properties.h" #include "editor/editor_resource_preview.h" #include "editor_node.h" +#include "editor_properties_array_dict.h" #include "scene/main/viewport.h" + +///////////////////// NULL ///////////////////////// + +void EditorPropertyNil::update_property() { +} + +EditorPropertyNil::EditorPropertyNil() { + Label *label = memnew(Label); + label->set_text("[null]"); + add_child(label); +} + ///////////////////// TEXT ///////////////////////// void EditorPropertyText::_text_changed(const String &p_string) { if (updating) @@ -2273,6 +2286,10 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ switch (p_type) { // atomic types + case Variant::NIL: { + EditorPropertyNil *editor = memnew(EditorPropertyNil); + add_property_editor(p_path, editor); + } break; case Variant::BOOL: { EditorPropertyCheck *editor = memnew(EditorPropertyCheck); add_property_editor(p_path, editor); @@ -2631,24 +2648,40 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } break; case Variant::DICTIONARY: { + EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary); + add_property_editor(p_path, editor); } break; case Variant::ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; - - // arrays case Variant::POOL_BYTE_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; // 20 case Variant::POOL_INT_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; case Variant::POOL_REAL_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; case Variant::POOL_STRING_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; case Variant::POOL_VECTOR2_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; case Variant::POOL_VECTOR3_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; // 25 case Variant::POOL_COLOR_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + add_property_editor(p_path, editor); } break; default: {} } diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 19884cc9dc..03e72b4ec2 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -39,6 +39,15 @@ #include "editor/scene_tree_editor.h" #include "scene/gui/color_picker.h" +class EditorPropertyNil : public EditorProperty { + GDCLASS(EditorPropertyNil, EditorProperty) + LineEdit *text; + +public: + virtual void update_property(); + EditorPropertyNil(); +}; + class EditorPropertyText : public EditorProperty { GDCLASS(EditorPropertyText, EditorProperty) LineEdit *text; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp new file mode 100644 index 0000000000..90f8d0e157 --- /dev/null +++ b/editor/editor_properties_array_dict.cpp @@ -0,0 +1,999 @@ +#include "editor_properties_array_dict.h" +#include "editor/editor_scale.h" +#include "editor_properties.h" + +bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) { + + String pn = p_name; + + if (pn.begins_with("indices")) { + int idx = pn.get_slicec('/', 1).to_int(); + array.set(idx, p_value); + return true; + } + + return false; +} + +bool EditorPropertyArrayObject::_get(const StringName &p_name, Variant &r_ret) const { + + String pn = p_name; + + if (pn.begins_with("indices")) { + + int idx = pn.get_slicec('/', 1).to_int(); + bool valid; + r_ret = array.get(idx, &valid); + return valid; + } + + return false; +} + +void EditorPropertyArrayObject::set_array(const Variant &p_array) { + array = p_array; +} + +Variant EditorPropertyArrayObject::get_array() { + return array; +} + +EditorPropertyArrayObject::EditorPropertyArrayObject() { +} + +/////////////////// + +bool EditorPropertyDictionaryObject::_set(const StringName &p_name, const Variant &p_value) { + + String pn = p_name; + + if (pn == "new_item_key") { + + new_item_key = p_value; + return true; + } + + if (pn == "new_item_value") { + + new_item_value = p_value; + return true; + } + + if (pn.begins_with("indices")) { + int idx = pn.get_slicec('/', 1).to_int(); + Variant key = dict.get_key_at_index(idx); + dict[key] = p_value; + return true; + } + + return false; +} + +bool EditorPropertyDictionaryObject::_get(const StringName &p_name, Variant &r_ret) const { + + String pn = p_name; + + if (pn == "new_item_key") { + + r_ret = new_item_key; + return true; + } + + if (pn == "new_item_value") { + + r_ret = new_item_value; + return true; + } + + if (pn.begins_with("indices")) { + + int idx = pn.get_slicec('/', 1).to_int(); + Variant key = dict.get_key_at_index(idx); + r_ret = dict[key]; + return true; + } + + return false; +} + +void EditorPropertyDictionaryObject::set_dict(const Dictionary &p_dict) { + dict = p_dict; +} + +Dictionary EditorPropertyDictionaryObject::get_dict() { + return dict; +} + +void EditorPropertyDictionaryObject::set_new_item_key(const Variant &p_new_item) { + new_item_key = p_new_item; +} + +Variant EditorPropertyDictionaryObject::get_new_item_key() { + return new_item_key; +} + +void EditorPropertyDictionaryObject::set_new_item_value(const Variant &p_new_item) { + new_item_value = p_new_item; +} + +Variant EditorPropertyDictionaryObject::get_new_item_value() { + return new_item_value; +} + +EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() { +} + +///////////////////// ARRAY /////////////////////////// + +void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value) { + + if (p_prop.begins_with("indices")) { + int idx = p_prop.get_slice("/", 1).to_int(); + Variant array = object->get_array(); + array.set(idx, p_value); + emit_signal("property_changed", get_edited_property(), array); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); //dupe, so undo/redo works better + } + object->set_array(array); + } +} + +void EditorPropertyArray::_change_type(Object *p_button, int p_index) { + + Button *button = Object::cast_to<Button>(p_button); + + Rect2 rect = button->get_global_rect(); + change_type->set_as_minsize(); + change_type->set_global_position(rect.position + rect.size - Vector2(change_type->get_combined_minimum_size().x, 0)); + change_type->popup(); + changing_type_idx = p_index; +} + +void EditorPropertyArray::_change_type_menu(int p_index) { + + Variant value; + Variant::CallError ce; + value = Variant::construct(Variant::Type(p_index), NULL, 0, ce); + Variant array = object->get_array(); + array.set(changing_type_idx, value); + + emit_signal("property_changed", get_edited_property(), array); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); //dupe, so undo/redo works better + } + object->set_array(array); + update_property(); +} + +void EditorPropertyArray::update_property() { + + Variant array = get_edited_object()->get(get_edited_property()); + + if ((!array.is_array()) != edit->is_disabled()) { + + if (array.is_array()) { + edit->set_disabled(false); + edit->set_pressed(false); + + } else { + edit->set_disabled(true); + if (vbox) { + memdelete(vbox); + } + } + } + + if (!array.is_array()) { + return; + } + + String arrtype; + switch (array.get_type()) { + case Variant::ARRAY: { + + arrtype = "Array"; + + } break; + + // arrays + case Variant::POOL_BYTE_ARRAY: { + arrtype = "ByteArray"; + + } break; + case Variant::POOL_INT_ARRAY: { + arrtype = "IntArray"; + + } break; + case Variant::POOL_REAL_ARRAY: { + + arrtype = "FltArray"; + } break; + case Variant::POOL_STRING_ARRAY: { + + arrtype = "StrArray"; + } break; + case Variant::POOL_VECTOR2_ARRAY: { + + arrtype = "Vec2Array"; + } break; + case Variant::POOL_VECTOR3_ARRAY: { + arrtype = "Vec3Array"; + + } break; + case Variant::POOL_COLOR_ARRAY: { + arrtype = "ColArray"; + } break; + default: {} + } + + edit->set_text(arrtype + "[" + itos(array.call("size")) + "]"); + +#ifdef TOOLS_ENABLED + + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (edit->is_pressed() != unfolded) { + edit->set_pressed(unfolded); + } + + if (unfolded) { + + updating = true; + + if (!vbox) { + + vbox = memnew(VBoxContainer); + add_child(vbox); + set_bottom_editor(vbox); + HBoxContainer *hbc = memnew(HBoxContainer); + vbox->add_child(hbc); + Label *label = memnew(Label(TTR("Size: "))); + label->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(label); + length = memnew(EditorSpinSlider); + length->set_step(1); + length->set_max(1000000); + length->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(length); + length->connect("value_changed", this, "_length_changed"); + + page_hb = memnew(HBoxContainer); + vbox->add_child(page_hb); + label = memnew(Label(TTR("Page: "))); + label->set_h_size_flags(SIZE_EXPAND_FILL); + page_hb->add_child(label); + page = memnew(EditorSpinSlider); + page->set_step(1); + page_hb->add_child(page); + page->set_h_size_flags(SIZE_EXPAND_FILL); + page->connect("value_changed", this, "_page_changed"); + } else { + //bye bye children of the box + while (vbox->get_child_count() > 2) { + memdelete(vbox->get_child(2)); + } + } + + int len = array.call("size"); + + length->set_value(len); + + int pages = MAX(0, len - 1) / page_len + 1; + + page->set_max(pages); + page_idx = MIN(page_idx, pages - 1); + page->set_value(page_idx); + page_hb->set_visible(pages > 1); + + int offset = page_idx * page_len; + + int amount = MIN(len - offset, page_len); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); + } + + object->set_array(array); + + for (int i = 0; i < amount; i++) { + String prop_name = "indices/" + itos(i + offset); + + EditorProperty *prop = NULL; + Variant value = array.get(i + offset); + + switch (value.get_type()) { + case Variant::NIL: { + prop = memnew(EditorPropertyNil); + + } break; + + // atomic types + case Variant::BOOL: { + + prop = memnew(EditorPropertyCheck); + + } break; + case Variant::INT: { + EditorPropertyInteger *ed = memnew(EditorPropertyInteger); + ed->setup(-100000, 100000, true, true); + prop = ed; + + } break; + case Variant::REAL: { + + EditorPropertyFloat *ed = memnew(EditorPropertyFloat); + ed->setup(-100000, 100000, 0.001, true, false, true, true); + prop = ed; + } break; + case Variant::STRING: { + + prop = memnew(EditorPropertyText); + + } break; + + // math types + + case Variant::VECTOR2: { + + EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::RECT2: { + + EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::VECTOR3: { + + EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM2D: { + + EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::PLANE: { + + EditorPropertyPlane *ed = memnew(EditorPropertyPlane); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::QUAT: { + + EditorPropertyQuat *ed = memnew(EditorPropertyQuat); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::AABB: { + + EditorPropertyAABB *ed = memnew(EditorPropertyAABB); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::BASIS: { + EditorPropertyBasis *ed = memnew(EditorPropertyBasis); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM: { + EditorPropertyTransform *ed = memnew(EditorPropertyTransform); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + + // misc types + case Variant::COLOR: { + prop = memnew(EditorPropertyColor); + + } break; + case Variant::NODE_PATH: { + prop = memnew(EditorPropertyNodePath); + + } break; + case Variant::_RID: { + prop = memnew(EditorPropertyNil); + + } break; + case Variant::OBJECT: { + + prop = memnew(EditorPropertyResource); + + } break; + case Variant::DICTIONARY: { + prop = memnew(EditorPropertyDictionary); + + } break; + case Variant::ARRAY: { + + prop = memnew(EditorPropertyArray); + + } break; + + // arrays + case Variant::POOL_BYTE_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_INT_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_REAL_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_STRING_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR2_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR3_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_COLOR_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + default: {} + } + + prop->set_object_and_property(object.ptr(), prop_name); + prop->set_label(itos(i + offset)); + prop->set_selectable(false); + prop->connect("property_changed", this, "_property_changed"); + if (array.get_type() == Variant::ARRAY) { + HBoxContainer *hb = memnew(HBoxContainer); + vbox->add_child(hb); + hb->add_child(prop); + prop->set_h_size_flags(SIZE_EXPAND_FILL); + Button *edit = memnew(Button); + edit->set_icon(get_icon("Edit", "EditorIcons")); + hb->add_child(edit); + edit->connect("pressed", this, "_change_type", varray(edit, i + offset)); + } else { + vbox->add_child(prop); + } + + prop->update_property(); + } + + updating = false; + + } else { + if (vbox) { + set_bottom_editor(NULL); + memdelete(vbox); + vbox = NULL; + } + } +#endif +} + +void EditorPropertyArray::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + } +} +void EditorPropertyArray::_edit_pressed() { + + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); + update_property(); +} + +void EditorPropertyArray::_page_changed(double p_page) { + if (updating) + return; + page_idx = p_page; + update_property(); +} + +void EditorPropertyArray::_length_changed(double p_page) { + if (updating) + return; + + Variant array = object->get_array(); + array.call("resize", int(p_page)); + emit_signal("property_changed", get_edited_property(), array); + + if (array.get_type() == Variant::ARRAY) { + array = array.call("duplicate"); //dupe, so undo/redo works better + } + object->set_array(array); + update_property(); +} + +void EditorPropertyArray::_bind_methods() { + ClassDB::bind_method("_edit_pressed", &EditorPropertyArray::_edit_pressed); + ClassDB::bind_method("_page_changed", &EditorPropertyArray::_page_changed); + ClassDB::bind_method("_length_changed", &EditorPropertyArray::_length_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed); + ClassDB::bind_method("_change_type", &EditorPropertyArray::_change_type); + ClassDB::bind_method("_change_type_menu", &EditorPropertyArray::_change_type_menu); +} + +EditorPropertyArray::EditorPropertyArray() { + + object.instance(); + page_idx = 0; + page_len = 10; + edit = memnew(Button); + edit->set_flat(true); + edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit->set_clip_text(true); + edit->connect("pressed", this, "_edit_pressed"); + edit->set_toggle_mode(true); + add_child(edit); + add_focusable(edit); + vbox = NULL; + page = NULL; + length = NULL; + updating = false; + change_type = memnew(PopupMenu); + add_child(change_type); + change_type->connect("id_pressed", this, "_change_type_menu"); + changing_type_idx = -1; + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + String type = Variant::get_type_name(Variant::Type(i)); + change_type->add_item(type, i); + } + changing_type_idx = -1; +} + +///////////////////// DICTIONARY /////////////////////////// + +void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value) { + + if (p_prop == "new_item_key") { + + object->set_new_item_key(p_value); + } else if (p_prop == "new_item_value") { + + object->set_new_item_value(p_value); + } else if (p_prop.begins_with("indices")) { + int idx = p_prop.get_slice("/", 1).to_int(); + Dictionary dict = object->get_dict(); + Variant key = dict.get_key_at_index(idx); + dict[key] = p_value; + + emit_signal("property_changed", get_edited_property(), dict); + + dict = dict.duplicate(); //dupe, so undo/redo works better + object->set_dict(dict); + } +} + +void EditorPropertyDictionary::_change_type(Object *p_button, int p_index) { + + Button *button = Object::cast_to<Button>(p_button); + + Rect2 rect = button->get_global_rect(); + change_type->set_as_minsize(); + change_type->set_global_position(rect.position + rect.size - Vector2(change_type->get_combined_minimum_size().x, 0)); + change_type->popup(); + changing_type_idx = p_index; +} + +void EditorPropertyDictionary::_add_key_value() { + + Dictionary dict = object->get_dict(); + dict[object->get_new_item_key()] = object->get_new_item_value(); + object->set_new_item_key(Variant()); + object->set_new_item_value(Variant()); + + emit_signal("property_changed", get_edited_property(), dict); + + dict = dict.duplicate(); //dupe, so undo/redo works better + object->set_dict(dict); + update_property(); +} + +void EditorPropertyDictionary::_change_type_menu(int p_index) { + + if (changing_type_idx < 0) { + Variant value; + Variant::CallError ce; + value = Variant::construct(Variant::Type(p_index), NULL, 0, ce); + if (changing_type_idx == -1) { + object->set_new_item_key(value); + } else { + object->set_new_item_value(value); + } + update_property(); + return; + } + + Dictionary dict = object->get_dict(); + + if (p_index < Variant::VARIANT_MAX) { + + Variant value; + Variant::CallError ce; + value = Variant::construct(Variant::Type(p_index), NULL, 0, ce); + Variant key = dict.get_key_at_index(changing_type_idx); + dict[key] = value; + } else { + Variant key = dict.get_key_at_index(changing_type_idx); + dict.erase(key); + } + + emit_signal("property_changed", get_edited_property(), dict); + + dict = dict.duplicate(); //dupe, so undo/redo works better + object->set_dict(dict); + update_property(); +} + +void EditorPropertyDictionary::update_property() { + + Dictionary dict = get_edited_object()->get(get_edited_property()); + + edit->set_text("Dict[" + itos(dict.size()) + "]"); + +#ifdef TOOLS_ENABLED + + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (edit->is_pressed() != unfolded) { + edit->set_pressed(unfolded); + } + + if (unfolded) { + + updating = true; + + if (!vbox) { + + vbox = memnew(VBoxContainer); + add_child(vbox); + set_bottom_editor(vbox); + + page_hb = memnew(HBoxContainer); + vbox->add_child(page_hb); + Label *label = memnew(Label(TTR("Page: "))); + label->set_h_size_flags(SIZE_EXPAND_FILL); + page_hb->add_child(label); + page = memnew(EditorSpinSlider); + page->set_step(1); + page_hb->add_child(page); + page->set_h_size_flags(SIZE_EXPAND_FILL); + page->connect("value_changed", this, "_page_changed"); + } else { + //bye bye children of the box + while (vbox->get_child_count() > 1) { + memdelete(vbox->get_child(1)); + } + } + + int len = dict.size(); + + int pages = MAX(0, len - 1) / page_len + 1; + + page->set_max(pages); + page_idx = MIN(page_idx, pages - 1); + page->set_value(page_idx); + page_hb->set_visible(pages > 1); + + int offset = page_idx * page_len; + + int amount = MIN(len - offset, page_len); + + dict = dict.duplicate(); + + object->set_dict(dict); + VBoxContainer *add_vbox = NULL; + + for (int i = 0; i < amount + 2; i++) { + String prop_name; + Variant key; + Variant value; + + if (i < amount) { + prop_name = "indices/" + itos(i + offset); + key = dict.get_key_at_index(i + offset); + value = dict.get_value_at_index(i + offset); + } else if (i == amount) { + prop_name = "new_item_key"; + value = object->get_new_item_key(); + } else if (i == amount + 1) { + prop_name = "new_item_value"; + value = object->get_new_item_value(); + } + + EditorProperty *prop = NULL; + + switch (value.get_type()) { + case Variant::NIL: { + prop = memnew(EditorPropertyNil); + + } break; + + // atomic types + case Variant::BOOL: { + + prop = memnew(EditorPropertyCheck); + + } break; + case Variant::INT: { + EditorPropertyInteger *ed = memnew(EditorPropertyInteger); + ed->setup(-100000, 100000, true, true); + prop = ed; + + } break; + case Variant::REAL: { + + EditorPropertyFloat *ed = memnew(EditorPropertyFloat); + ed->setup(-100000, 100000, 0.001, true, false, true, true); + prop = ed; + } break; + case Variant::STRING: { + + prop = memnew(EditorPropertyText); + + } break; + + // math types + + case Variant::VECTOR2: { + + EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::RECT2: { + + EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::VECTOR3: { + + EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM2D: { + + EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::PLANE: { + + EditorPropertyPlane *ed = memnew(EditorPropertyPlane); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::QUAT: { + + EditorPropertyQuat *ed = memnew(EditorPropertyQuat); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::AABB: { + + EditorPropertyAABB *ed = memnew(EditorPropertyAABB); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::BASIS: { + EditorPropertyBasis *ed = memnew(EditorPropertyBasis); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + case Variant::TRANSFORM: { + EditorPropertyTransform *ed = memnew(EditorPropertyTransform); + ed->setup(-100000, 100000, 0.001, true); + prop = ed; + + } break; + + // misc types + case Variant::COLOR: { + prop = memnew(EditorPropertyColor); + + } break; + case Variant::NODE_PATH: { + prop = memnew(EditorPropertyNodePath); + + } break; + case Variant::_RID: { + prop = memnew(EditorPropertyNil); + + } break; + case Variant::OBJECT: { + + prop = memnew(EditorPropertyResource); + + } break; + case Variant::DICTIONARY: { + prop = memnew(EditorPropertyDictionary); + + } break; + case Variant::ARRAY: { + + prop = memnew(EditorPropertyArray); + + } break; + + // arrays + case Variant::POOL_BYTE_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_INT_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_REAL_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_STRING_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR2_ARRAY: { + + prop = memnew(EditorPropertyArray); + } break; + case Variant::POOL_VECTOR3_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + case Variant::POOL_COLOR_ARRAY: { + prop = memnew(EditorPropertyArray); + + } break; + default: {} + } + + if (i == amount) { + PanelContainer *pc = memnew(PanelContainer); + vbox->add_child(pc); + Ref<StyleBoxFlat> flat; + flat.instance(); + for (int j = 0; j < 4; j++) { + flat->set_default_margin(Margin(j), 2 * EDSCALE); + } + flat->set_bg_color(get_color("prop_subsection", "Editor")); + + pc->add_style_override("panel", flat); + add_vbox = memnew(VBoxContainer); + pc->add_child(add_vbox); + } + prop->set_object_and_property(object.ptr(), prop_name); + int change_index; + + if (i < amount) { + String cs = key.get_construct_string(); + prop->set_label(key.get_construct_string()); + prop->set_tooltip(cs); + change_index = i + offset; + } else if (i == amount) { + prop->set_label(TTR("New Key:")); + change_index = -1; + } else if (i == amount + 1) { + prop->set_label(TTR("New Value:")); + change_index = -2; + } + + prop->set_selectable(false); + prop->connect("property_changed", this, "_property_changed"); + + HBoxContainer *hb = memnew(HBoxContainer); + if (add_vbox) { + add_vbox->add_child(hb); + } else { + vbox->add_child(hb); + } + hb->add_child(prop); + prop->set_h_size_flags(SIZE_EXPAND_FILL); + Button *edit = memnew(Button); + edit->set_icon(get_icon("Edit", "EditorIcons")); + hb->add_child(edit); + edit->connect("pressed", this, "_change_type", varray(edit, change_index)); + + prop->update_property(); + + if (i == amount + 1) { + Button *add_item = memnew(Button); + add_item->set_text(TTR("Add Key/Value Pair")); + add_vbox->add_child(add_item); + add_item->connect("pressed", this, "_add_key_value"); + } + } + + updating = false; + + } else { + if (vbox) { + set_bottom_editor(NULL); + memdelete(vbox); + vbox = NULL; + } + } +#endif +} + +void EditorPropertyDictionary::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + } +} +void EditorPropertyDictionary::_edit_pressed() { + + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); + update_property(); +} + +void EditorPropertyDictionary::_page_changed(double p_page) { + if (updating) + return; + page_idx = p_page; + update_property(); +} + +void EditorPropertyDictionary::_bind_methods() { + ClassDB::bind_method("_edit_pressed", &EditorPropertyDictionary::_edit_pressed); + ClassDB::bind_method("_page_changed", &EditorPropertyDictionary::_page_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed); + ClassDB::bind_method("_change_type", &EditorPropertyDictionary::_change_type); + ClassDB::bind_method("_change_type_menu", &EditorPropertyDictionary::_change_type_menu); + ClassDB::bind_method("_add_key_value", &EditorPropertyDictionary::_add_key_value); +} + +EditorPropertyDictionary::EditorPropertyDictionary() { + + object.instance(); + page_idx = 0; + page_len = 10; + edit = memnew(Button); + edit->set_flat(true); + edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit->set_clip_text(true); + edit->connect("pressed", this, "_edit_pressed"); + edit->set_toggle_mode(true); + add_child(edit); + add_focusable(edit); + vbox = NULL; + page = NULL; + updating = false; + change_type = memnew(PopupMenu); + add_child(change_type); + change_type->connect("id_pressed", this, "_change_type_menu"); + changing_type_idx = -1; + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + String type = Variant::get_type_name(Variant::Type(i)); + change_type->add_item(type, i); + } + change_type->add_separator(); + change_type->add_item(TTR("Remove Item"), Variant::VARIANT_MAX); + changing_type_idx = -1; +} diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h new file mode 100644 index 0000000000..7f6203ee88 --- /dev/null +++ b/editor/editor_properties_array_dict.h @@ -0,0 +1,115 @@ +#ifndef EDITOR_PROPERTIES_ARRAY_DICT_H +#define EDITOR_PROPERTIES_ARRAY_DICT_H + +#include "editor/editor_inspector.h" +#include "editor/editor_spin_slider.h" +#include "scene/gui/button.h" + +class EditorPropertyArrayObject : public Reference { + + GDCLASS(EditorPropertyArrayObject, Reference); + + Variant array; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + +public: + void set_array(const Variant &p_array); + Variant get_array(); + + EditorPropertyArrayObject(); +}; + +class EditorPropertyDictionaryObject : public Reference { + + GDCLASS(EditorPropertyDictionaryObject, Reference); + + Variant new_item_key; + Variant new_item_value; + Dictionary dict; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + +public: + void set_dict(const Dictionary &p_dict); + Dictionary get_dict(); + + void set_new_item_key(const Variant &p_new_item); + Variant get_new_item_key(); + + void set_new_item_value(const Variant &p_new_item); + Variant get_new_item_value(); + + EditorPropertyDictionaryObject(); +}; + +class EditorPropertyArray : public EditorProperty { + GDCLASS(EditorPropertyArray, EditorProperty) + + PopupMenu *change_type; + bool updating; + + Ref<EditorPropertyArrayObject> object; + int page_len; + int page_idx; + int changing_type_idx; + Button *edit; + VBoxContainer *vbox; + EditorSpinSlider *length; + EditorSpinSlider *page; + HBoxContainer *page_hb; + + void _page_changed(double p_page); + void _length_changed(double p_page); + void _edit_pressed(); + void _property_changed(const String &p_prop, Variant p_value); + void _change_type(Object *p_button, int p_index); + void _change_type_menu(int p_index); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + virtual void update_property(); + EditorPropertyArray(); +}; + +class EditorPropertyDictionary : public EditorProperty { + GDCLASS(EditorPropertyDictionary, EditorProperty) + + PopupMenu *change_type; + bool updating; + + Ref<EditorPropertyDictionaryObject> object; + int page_len; + int page_idx; + int changing_type_idx; + Button *edit; + VBoxContainer *vbox; + EditorSpinSlider *length; + EditorSpinSlider *page; + HBoxContainer *page_hb; + + void _page_changed(double p_page); + void _edit_pressed(); + void _property_changed(const String &p_prop, Variant p_value); + void _change_type(Object *p_button, int p_index); + void _change_type_menu(int p_index); + + void _add_key_value(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + virtual void update_property(); + EditorPropertyDictionary(); +}; + +#endif // EDITOR_PROPERTIES_ARRAY_DICT_H diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index e7741c7926..297373d299 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -383,11 +383,11 @@ void FileSystemDock::_update_file_display_toggle_button() { if (button_display_mode->is_pressed()) { display_mode = DISPLAY_LIST; button_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons")); - button_display_mode->set_tooltip(TTR("View items as a grid of thumbnails")); + button_display_mode->set_tooltip(TTR("View items as a grid of thumbnails.")); } else { display_mode = DISPLAY_THUMBNAILS; button_display_mode->set_icon(get_icon("FileList", "EditorIcons")); - button_display_mode->set_tooltip(TTR("View items as a list")); + button_display_mode->set_tooltip(TTR("View items as a list.")); } } @@ -1860,7 +1860,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_favorite->set_flat(true); button_favorite->set_toggle_mode(true); button_favorite->connect("pressed", this, "_favorites_pressed"); - button_favorite->set_tooltip(TTR("Toggle folder status as Favorite")); + button_favorite->set_tooltip(TTR("Toggle folder status as Favorite.")); button_favorite->set_focus_mode(FOCUS_NONE); toolbar_hbc->add_child(button_favorite); @@ -1916,11 +1916,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { file_list_vb->add_child(path_hb); button_tree = memnew(ToolButton); + button_tree->set_tooltip(TTR("Enter tree-view.")); button_tree->hide(); path_hb->add_child(button_tree); search_box = memnew(LineEdit); search_box->set_h_size_flags(SIZE_EXPAND_FILL); + search_box->set_placeholder(TTR("Search files")); search_box->connect("text_changed", this, "_search_changed"); path_hb->add_child(search_box); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 74ea46838b..ddf619866d 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -428,6 +428,7 @@ FindInFilesDialog::FindInFilesDialog() { void FindInFilesDialog::set_search_text(String text) { _search_text_line_edit->set_text(text); + _on_search_text_modified(text); } String FindInFilesDialog::get_search_text() const { diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 1d7545f182..2fb3bf7b1e 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -339,7 +339,7 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { NodeMap nm; nm.node = node; node_map[p_node->id] = nm; - node_name_map[p_node->name] = p_node->id; + node_name_map[node->get_name()] = p_node->id; Transform xf = p_node->default_transform; xf = collada.fix_transform(xf) * p_node->post_transform; diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 29720b3cdb..49c54ad67d 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -794,13 +794,13 @@ Ref<Texture> CurvePreviewGenerator::generate(const Ref<Resource> &p_from) { img_ref.instance(); Image &im = **img_ref; - im.create(thumbnail_size, thumbnail_size, 0, Image::FORMAT_RGBA8); + im.create(thumbnail_size, thumbnail_size / 2, 0, Image::FORMAT_RGBA8); im.lock(); Color bg_color(0.1, 0.1, 0.1, 1.0); for (int i = 0; i < thumbnail_size; i++) { - for (int j = 0; j < thumbnail_size; j++) { + for (int j = 0; j < thumbnail_size / 2; j++) { im.set_pixel(i, j, bg_color); } } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 422d19c0e4..94dcbd8e18 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1404,6 +1404,7 @@ void ScriptEditor::_update_members_overview_visibility() { ScriptEditorBase *se = _get_current_editor(); if (!se) { + members_overview_buttons_hbox->set_visible(false); members_overview->set_visible(false); return; } @@ -2681,7 +2682,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { members_overview_vbox->add_child(members_overview_buttons_hbox); members_overview_alphabeta_sort_button = memnew(ToolButton); - members_overview_alphabeta_sort_button->set_tooltip(TTR("Sort alphabetically")); + members_overview_alphabeta_sort_button->set_tooltip(TTR("Toggle alphabetical sorting of the method list.")); members_overview_alphabeta_sort_button->set_toggle_mode(true); members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically")); members_overview_alphabeta_sort_button->connect("toggled", this, "_toggle_members_overview_alpha_sort"); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 97d3a070ab..0d06b71420 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -199,6 +199,7 @@ private: sp = TTR("Imported Project"); project_name->set_text(sp); + _text_changed(sp); } } @@ -222,6 +223,7 @@ private: } String sp = p.simplify_path(); project_path->set_text(sp); + _path_text_changed(sp); get_ok()->call_deferred("grab_focus"); } @@ -230,6 +232,7 @@ private: String p = p_path; String sp = p.simplify_path(); project_path->set_text(sp); + _path_text_changed(sp); get_ok()->call_deferred("grab_focus"); } @@ -263,7 +266,9 @@ private: if (d->make_dir(project_name->get_text()) == OK) { d->change_dir(project_name->get_text()); - project_path->set_text(d->get_current_dir()); + String dir_str = d->get_current_dir(); + project_path->set_text(dir_str); + _path_text_changed(dir_str); created_folder_path = d->get_current_dir(); create_dir->set_disabled(true); } else { @@ -475,7 +480,9 @@ private: _remove_created_folder(); project_path->clear(); + _path_text_changed(""); project_name->clear(); + _text_changed(""); if (status_rect->get_texture() == get_icon("StatusError", "EditorIcons")) msg->show(); @@ -540,7 +547,9 @@ public: msg->show(); get_ok()->set_disabled(true); } else if (current->has_setting("application/config/name")) { - project_name->set_text(current->get("application/config/name")); + String proj = current->get("application/config/name"); + project_name->set_text(proj); + _text_changed(proj); } project_name->call_deferred("grab_focus"); @@ -559,7 +568,9 @@ public: fdialog->set_current_dir(d->get_current_dir()); memdelete(d); } - project_name->set_text(TTR("New Game Project")); + String proj = TTR("New Game Project"); + project_name->set_text(proj); + _text_changed(proj); project_path->set_editable(true); browse->set_disabled(false); diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs index 929b13d70c..270f3b80a2 100644 --- a/modules/mono/glue/cs_files/Basis.cs +++ b/modules/mono/glue/cs_files/Basis.cs @@ -446,6 +446,26 @@ namespace Godot _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); } + public Basis(Vector3 euler) + { + real_t c; + real_t s; + + c = Mathf.Cos(euler.x); + s = Mathf.Sin(euler.x); + var xmat = new Basis((real_t)1.0, (real_t)0.0, (real_t)0.0, (real_t)0.0, c, -s, (real_t)0.0, s, c); + + c = Mathf.Cos(euler.y); + s = Mathf.Sin(euler.y); + var ymat = new Basis(c, (real_t)0.0, s, (real_t)0.0, (real_t)1.0, (real_t)0.0, -s, (real_t)0.0, c); + + c = Mathf.Cos(euler.z); + s = Mathf.Sin(euler.z); + var zmat = new Basis(c, -s, (real_t)0.0, s, c, (real_t)0.0, (real_t)0.0, (real_t)0.0, (real_t)1.0); + + this = ymat * xmat * zmat; + } + public Basis(Vector3 axis, real_t phi) { var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs index af94bb616e..e0d6d27840 100644 --- a/modules/mono/glue/cs_files/Color.cs +++ b/modules/mono/glue/cs_files/Color.cs @@ -249,6 +249,15 @@ namespace Godot ); } + public Color Darkened(float amount) + { + Color res = this; + res.r = res.r * (1.0f - amount); + res.g = res.g * (1.0f - amount); + res.b = res.b * (1.0f - amount); + return res; + } + public float Gray() { return (r + g + b) / 3.0f; @@ -263,6 +272,15 @@ namespace Godot ); } + public Color Lightened(float amount) + { + Color res = this; + res.r = res.r + (1.0f - res.r) * amount; + res.g = res.g + (1.0f - res.g) * amount; + res.b = res.b + (1.0f - res.b) * amount; + return res; + } + public Color LinearInterpolate(Color c, float t) { var res = this; @@ -275,15 +293,15 @@ namespace Godot return res; } - public int To32() + public int ToRgba32() { - int c = (byte)(a * 255); - c <<= 8; - c |= (byte)(r * 255); + int c = (byte)(r * 255); c <<= 8; c |= (byte)(g * 255); c <<= 8; c |= (byte)(b * 255); + c <<= 8; + c |= (byte)(a * 255); return c; } diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs index 8b92522029..1020f06bf5 100644 --- a/modules/mono/glue/cs_files/Plane.cs +++ b/modules/mono/glue/cs_files/Plane.cs @@ -9,17 +9,23 @@ namespace Godot { public struct Plane : IEquatable<Plane> { - Vector3 normal; + private Vector3 _normal; + + public Vector3 Normal + { + get { return _normal; } + set { _normal = value; } + } public real_t x { get { - return normal.x; + return _normal.x; } set { - normal.x = value; + _normal.x = value; } } @@ -27,11 +33,11 @@ namespace Godot { get { - return normal.y; + return _normal.y; } set { - normal.y = value; + _normal.y = value; } } @@ -39,62 +45,62 @@ namespace Godot { get { - return normal.z; + return _normal.z; } set { - normal.z = value; + _normal.z = value; } } - real_t d; + public real_t D { get; set; } public Vector3 Center { get { - return normal * d; + return _normal * D; } } public real_t DistanceTo(Vector3 point) { - return normal.Dot(point) - d; + return _normal.Dot(point) - D; } public Vector3 GetAnyPoint() { - return normal * d; + return _normal * D; } public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) { - real_t dist = normal.Dot(point) - d; + real_t dist = _normal.Dot(point) - D; return Mathf.Abs(dist) <= epsilon; } public Vector3 Intersect3(Plane b, Plane c) { - real_t denom = normal.Cross(b.normal).Dot(c.normal); + real_t denom = _normal.Cross(b._normal).Dot(c._normal); if (Mathf.Abs(denom) <= Mathf.Epsilon) return new Vector3(); - Vector3 result = b.normal.Cross(c.normal) * d + - c.normal.Cross(normal) * b.d + - normal.Cross(b.normal) * c.d; + Vector3 result = b._normal.Cross(c._normal) * D + + c._normal.Cross(_normal) * b.D + + _normal.Cross(b._normal) * c.D; return result / denom; } public Vector3 IntersectRay(Vector3 from, Vector3 dir) { - real_t den = normal.Dot(dir); + real_t den = _normal.Dot(dir); if (Mathf.Abs(den) <= Mathf.Epsilon) return new Vector3(); - real_t dist = (normal.Dot(from) - d) / den; + real_t dist = (_normal.Dot(from) - D) / den; // This is a ray, before the emitting pos (from) does not exist if (dist > Mathf.Epsilon) @@ -106,12 +112,12 @@ namespace Godot public Vector3 IntersectSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; - real_t den = normal.Dot(segment); + real_t den = _normal.Dot(segment); if (Mathf.Abs(den) <= Mathf.Epsilon) return new Vector3(); - real_t dist = (normal.Dot(begin) - d) / den; + real_t dist = (_normal.Dot(begin) - D) / den; if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) return new Vector3(); @@ -121,46 +127,46 @@ namespace Godot public bool IsPointOver(Vector3 point) { - return normal.Dot(point) > d; + return _normal.Dot(point) > D; } public Plane Normalized() { - real_t len = normal.Length(); + real_t len = _normal.Length(); if (len == 0) return new Plane(0, 0, 0, 0); - return new Plane(normal / len, d / len); + return new Plane(_normal / len, D / len); } public Vector3 Project(Vector3 point) { - return point - normal * DistanceTo(point); + return point - _normal * DistanceTo(point); } // Constructors public Plane(real_t a, real_t b, real_t c, real_t d) { - normal = new Vector3(a, b, c); - this.d = d; + _normal = new Vector3(a, b, c); + this.D = d; } public Plane(Vector3 normal, real_t d) { - this.normal = normal; - this.d = d; + this._normal = normal; + this.D = d; } public Plane(Vector3 v1, Vector3 v2, Vector3 v3) { - normal = (v1 - v3).Cross(v1 - v2); - normal.Normalize(); - d = normal.Dot(v1); + _normal = (v1 - v3).Cross(v1 - v2); + _normal.Normalize(); + D = _normal.Dot(v1); } public static Plane operator -(Plane plane) { - return new Plane(-plane.normal, -plane.d); + return new Plane(-plane._normal, -plane.D); } public static bool operator ==(Plane left, Plane right) @@ -185,20 +191,20 @@ namespace Godot public bool Equals(Plane other) { - return normal == other.normal && d == other.d; + return _normal == other._normal && D == other.D; } public override int GetHashCode() { - return normal.GetHashCode() ^ d.GetHashCode(); + return _normal.GetHashCode() ^ D.GetHashCode(); } public override string ToString() { return String.Format("({0}, {1})", new object[] { - normal.ToString(), - d.ToString() + _normal.ToString(), + D.ToString() }); } @@ -206,8 +212,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - normal.ToString(format), - d.ToString(format) + _normal.ToString(format), + D.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs index 21090fb68d..eaeed7b37b 100644 --- a/modules/mono/glue/cs_files/StringExtensions.cs +++ b/modules/mono/glue/cs_files/StringExtensions.cs @@ -225,7 +225,7 @@ namespace Godot if (pos < 0) return instance; - return instance.Substring(pos + 1, instance.Length); + return instance.Substring(pos + 1); } // <summary> diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index bde0b4e898..b97ccc19a7 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -913,7 +913,7 @@ static int remapKey(unsigned int key) { CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); if (!layoutData) - return nil; + return 0; const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); @@ -1480,7 +1480,7 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) { if (cursor_shape == p_shape) return; - if (mouse_mode != MOUSE_MODE_VISIBLE) { + if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { cursor_shape = p_shape; return; } @@ -1740,7 +1740,8 @@ String OS_OSX::get_godot_dir_name() const { String OS_OSX::get_system_dir(SystemDir p_dir) const { - NSSearchPathDirectory id = 0; + NSSearchPathDirectory id; + bool found = true; switch (p_dir) { case SYSTEM_DIR_DESKTOP: { @@ -1761,10 +1762,13 @@ String OS_OSX::get_system_dir(SystemDir p_dir) const { case SYSTEM_DIR_PICTURES: { id = NSPicturesDirectory; } break; + default: { + found = false; + } } String ret; - if (id) { + if (found) { NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES); if (paths && [paths count] >= 1) { diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index d6cdea7b88..09e3936a68 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -623,9 +623,26 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_SIZE: { int window_w = LOWORD(lParam); int window_h = HIWORD(lParam); - if (window_w > 0 && window_h > 0) { + if (window_w > 0 && window_h > 0 && !preserve_window_size) { video_mode.width = window_w; video_mode.height = window_h; + } else { + preserve_window_size = false; + int w = video_mode.width; + int h = video_mode.height; + + RECT rect; + GetWindowRect(hWnd, &rect); + + if (video_mode.borderless_window == false) { + RECT crect; + GetClientRect(hWnd, &crect); + + w += (rect.right - rect.left) - (crect.right - crect.left); + h += (rect.bottom - rect.top) - (crect.bottom - crect.top); + } + + MoveWindow(hWnd, rect.left, rect.top, w, h, TRUE); } if (wParam == SIZE_MAXIMIZED) { maximized = true; @@ -777,7 +794,9 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) SetCursor(NULL); } else { if (hCursor != NULL) { - SetCursor(hCursor); + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + set_cursor_shape(c); hCursor = NULL; } } @@ -1561,6 +1580,15 @@ void OS_Windows::set_window_size(const Size2 p_size) { } MoveWindow(hWnd, rect.left, rect.top, w, h, TRUE); + + // Don't let the mouse leave the window when resizing to a smaller resolution + if (mouse_mode == MOUSE_MODE_CONFINED) { + RECT rect; + GetClientRect(hWnd, &rect); + ClientToScreen(hWnd, (POINT *)&rect.left); + ClientToScreen(hWnd, (POINT *)&rect.right); + ClipCursor(&rect); + } } void OS_Windows::set_window_fullscreen(bool p_enabled) { @@ -1767,6 +1795,7 @@ void OS_Windows::set_borderless_window(bool p_borderless) { video_mode.borderless_window = p_borderless; + preserve_window_size = true; _update_window_style(); } @@ -1785,7 +1814,7 @@ void OS_Windows::_update_window_style(bool repaint) { } } - SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); if (repaint) { RECT rect; @@ -1996,7 +2025,7 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) { if (cursor_shape == p_shape) return; - if (mouse_mode != MOUSE_MODE_VISIBLE) { + if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { cursor_shape = p_shape; return; } diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 221109318e..81849497ee 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -105,6 +105,7 @@ class OS_Windows : public OS { Size2 window_rect; VideoMode video_mode; + bool preserve_window_size = false; MainLoop *main_loop; diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index eec371865e..50cc209dca 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -2407,7 +2407,7 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { if (p_shape == current_cursor) return; - if (mouse_mode == MOUSE_MODE_VISIBLE) { + if (mouse_mode == MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { if (cursors[p_shape] != None) XDefineCursor(x11_display, x11_window, cursors[p_shape]); else if (cursors[CURSOR_ARROW] != None) diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index e57af0a4c0..2b644e7f96 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1005,7 +1005,6 @@ void LineEdit::set_text(String p_text) { update(); cursor_pos = 0; window_pos = 0; - _text_changed(); } void LineEdit::clear() { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index c6ff8489c0..55a650ff12 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2950,13 +2950,13 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { case KEY_A: { #ifndef APPLE_STYLE_KEYS - if (!k->get_command() || k->get_shift() || k->get_alt()) { + if (!k->get_control() || k->get_shift() || k->get_alt()) { scancode_handled = false; break; } select_all(); #else - if (k->get_alt() || (!k->get_shift() && !k->get_command() && !k->get_control())) { + if ((!k->get_command() && !k->get_control())) { scancode_handled = false; break; } @@ -4110,7 +4110,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { void TextEdit::set_text(String p_text) { setting_text = true; - clear(); + _clear(); _insert_text_at_cursor(p_text); clear_undo_history(); cursor.column = 0; @@ -4123,7 +4123,7 @@ void TextEdit::set_text(String p_text) { cursor_set_column(0); update(); setting_text = false; - _text_changed_emit(); + //get_range()->set(0); }; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 56a2e7afba..54f5aea160 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -76,7 +76,9 @@ void Texture::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map", "clip_uv"), &Texture::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_data"), &Texture::get_data); + ADD_GROUP("Flags", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_flags", "get_flags"); + ADD_GROUP("", ""); BIND_ENUM_CONSTANT(FLAGS_DEFAULT); BIND_ENUM_CONSTANT(FLAG_MIPMAPS); |