diff options
106 files changed, 4121 insertions, 1044 deletions
diff --git a/SConstruct b/SConstruct index 08c5b6d34f..1135f7a3bd 100644 --- a/SConstruct +++ b/SConstruct @@ -409,6 +409,11 @@ if selected_platform in platform_list: env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"] env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] + # (SH)LIBSUFFIX will be used for our own built libraries + # LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default, + # so we need to append the default suffixes to keep the ability + # to link against thirdparty libraries (.a, .so, .dll, etc.). + env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]] env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"] env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"] diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 7a14e85f20..af1d49ae8c 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -221,6 +221,10 @@ String _OS::get_audio_driver_name(int p_driver) const { return OS::get_singleton()->get_audio_driver_name(p_driver); } +PoolStringArray _OS::get_connected_midi_inputs() { + return OS::get_singleton()->get_connected_midi_inputs(); +} + void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) { OS::VideoMode vm; @@ -1058,6 +1062,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name); ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count); ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name); + ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count); ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 48b7b74005..1729c23779 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -152,6 +152,8 @@ public: virtual int get_audio_driver_count() const; virtual String get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const; virtual int get_current_screen() const; virtual void set_current_screen(int p_screen); diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 5b4dd05dbf..187813f9d0 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -89,6 +89,7 @@ VARIANT_ENUM_CAST(KeyList); VARIANT_ENUM_CAST(KeyModifierMask); VARIANT_ENUM_CAST(ButtonList); VARIANT_ENUM_CAST(JoystickList); +VARIANT_ENUM_CAST(MidiMessageList); void register_global_constants() { @@ -458,6 +459,15 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2); BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); + // midi + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); + // error list BIND_GLOBAL_ENUM_CONSTANT(OK); diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 16d5e3c282..85c1fc5ddf 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -54,32 +54,25 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S int line = 1; bool skip_this = false; bool skip_next = false; + bool is_eof = false; - while (true) { + while (!is_eof) { - String l = f->get_line(); + String l = f->get_line().strip_edges(); + is_eof = f->eof_reached(); - if (f->eof_reached()) { + // If we reached last line and it's not a content line, break, otherwise let processing that last loop + if (is_eof && l.empty()) { - if (status == STATUS_READING_STRING) { - - if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") - config = msg_str; - break; - - } else if (status == STATUS_NONE) + if (status == STATUS_READING_ID) { + memdelete(f); + ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V(RES()); + } else { break; - - memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); - ERR_FAIL_V(RES()); + } } - l = l.strip_edges(); - if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { @@ -160,6 +153,15 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S f->close(); memdelete(f); + if (status == STATUS_READING_STRING) { + + if (msg_id != "") { + if (!skip_this) + translation->add_message(msg_id, msg_str); + } else if (config == "") + config = msg_str; + } + if (config == "") { ERR_EXPLAIN("No config found in file: " + p_path); ERR_FAIL_V(RES()); diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index cff19f990c..e2e71dda92 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -245,7 +245,6 @@ Vector3 AABB::get_longest_axis() const { if (size.z > max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -262,7 +261,6 @@ int AABB::get_longest_axis_index() const { if (size.z > max_size) { axis = 2; - max_size = size.z; } return axis; @@ -280,7 +278,6 @@ Vector3 AABB::get_shortest_axis() const { if (size.z < max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -297,7 +294,6 @@ int AABB::get_shortest_axis_index() const { if (size.z < max_size) { axis = 2; - max_size = size.z; } return axis; diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index b1424e1d78..2e184f7a88 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -244,10 +244,8 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { const Node *nodesptr = &nodes[0]; const Plane *planesptr = &planes[0]; - int plane_count = planes.size(); int idx = node_count - 1; - int steps = 0; while (true) { @@ -259,21 +257,19 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { return true; } - uint16_t plane = nodesptr[idx].plane; #ifdef DEBUG_ENABLED - + int plane_count = planes.size(); + uint16_t plane = nodesptr[idx].plane; ERR_FAIL_INDEX_V(plane, plane_count, false); #endif + bool over = planesptr[nodesptr[idx].plane].is_point_over(p_point); idx = over ? nodes[idx].over : nodes[idx].under; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false); #endif - - steps++; } return false; diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index ca6446d015..e94ccb4f48 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -1080,3 +1080,122 @@ InputEventPanGesture::InputEventPanGesture() { delta = Vector2(0, 0); } +///////////////////////////// + +void InputEventMIDI::set_channel(const int p_channel) { + + channel = p_channel; +} + +int InputEventMIDI::get_channel() const { + return channel; +} + +void InputEventMIDI::set_message(const int p_message) { + + message = p_message; +} + +int InputEventMIDI::get_message() const { + return message; +} + +void InputEventMIDI::set_pitch(const int p_pitch) { + + pitch = p_pitch; +} + +int InputEventMIDI::get_pitch() const { + return pitch; +} + +void InputEventMIDI::set_velocity(const int p_velocity) { + + velocity = p_velocity; +} + +int InputEventMIDI::get_velocity() const { + return velocity; +} + +void InputEventMIDI::set_instrument(const int p_instrument) { + + instrument = p_instrument; +} + +int InputEventMIDI::get_instrument() const { + return instrument; +} + +void InputEventMIDI::set_pressure(const int p_pressure) { + + pressure = p_pressure; +} + +int InputEventMIDI::get_pressure() const { + return pressure; +} + +void InputEventMIDI::set_controller_number(const int p_controller_number) { + + controller_number = p_controller_number; +} + +int InputEventMIDI::get_controller_number() const { + return controller_number; +} + +void InputEventMIDI::set_controller_value(const int p_controller_value) { + + controller_value = p_controller_value; +} + +int InputEventMIDI::get_controller_value() const { + return controller_value; +} + +String InputEventMIDI::as_text() const { + + return "InputEventMIDI : channel=(" + itos(get_channel()) + "), message=(" + itos(get_message()) + ")"; +} + +void InputEventMIDI::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_channel", "channel"), &InputEventMIDI::set_channel); + ClassDB::bind_method(D_METHOD("get_channel"), &InputEventMIDI::get_channel); + ClassDB::bind_method(D_METHOD("set_message", "message"), &InputEventMIDI::set_message); + ClassDB::bind_method(D_METHOD("get_message"), &InputEventMIDI::get_message); + ClassDB::bind_method(D_METHOD("set_pitch", "pitch"), &InputEventMIDI::set_pitch); + ClassDB::bind_method(D_METHOD("get_pitch"), &InputEventMIDI::get_pitch); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventMIDI::set_velocity); + ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventMIDI::get_velocity); + ClassDB::bind_method(D_METHOD("set_instrument", "instrument"), &InputEventMIDI::set_instrument); + ClassDB::bind_method(D_METHOD("get_instrument"), &InputEventMIDI::get_instrument); + ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventMIDI::set_pressure); + ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventMIDI::get_pressure); + ClassDB::bind_method(D_METHOD("set_controller_number", "controller_number"), &InputEventMIDI::set_controller_number); + ClassDB::bind_method(D_METHOD("get_controller_number"), &InputEventMIDI::get_controller_number); + ClassDB::bind_method(D_METHOD("set_controller_value", "controller_value"), &InputEventMIDI::set_controller_value); + ClassDB::bind_method(D_METHOD("get_controller_value"), &InputEventMIDI::get_controller_value); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "channel"), "set_channel", "get_channel"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "message"), "set_message", "get_message"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pitch"), "set_pitch", "get_pitch"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "velocity"), "set_velocity", "get_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "instrument"), "set_instrument", "get_instrument"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pressure"), "set_pressure", "get_pressure"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); +} + +InputEventMIDI::InputEventMIDI() { + + channel = 0; + message = 0; + pitch = 0; + velocity = 0; + instrument = 0; + pressure = 0; + controller_number = 0; + controller_value = 0; +} diff --git a/core/os/input_event.h b/core/os/input_event.h index bd1a85ce29..04126fee77 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -140,6 +140,16 @@ enum JoystickList { JOY_ANALOG_R2 = JOY_AXIS_7, }; +enum MidiMessageList { + MIDI_MESSAGE_NOTE_OFF = 0x8, + MIDI_MESSAGE_NOTE_ON = 0x9, + MIDI_MESSAGE_AFTERTOUCH = 0xA, + MIDI_MESSAGE_CONTROL_CHANGE = 0xB, + MIDI_MESSAGE_PROGRAM_CHANGE = 0xC, + MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD, + MIDI_MESSAGE_PITCH_BEND = 0xE, +}; + /** * Input Modifier Status * for keyboard/mouse events. @@ -525,4 +535,50 @@ public: InputEventPanGesture(); }; + +class InputEventMIDI : public InputEvent { + GDCLASS(InputEventMIDI, InputEvent) + + int channel; + int message; + int pitch; + int velocity; + int instrument; + int pressure; + int controller_number; + int controller_value; + +protected: + static void _bind_methods(); + +public: + void set_channel(const int p_channel); + int get_channel() const; + + void set_message(const int p_message); + int get_message() const; + + void set_pitch(const int p_pitch); + int get_pitch() const; + + void set_velocity(const int p_velocity); + int get_velocity() const; + + void set_instrument(const int p_instrument); + int get_instrument() const; + + void set_pressure(const int p_pressure); + int get_pressure() const; + + void set_controller_number(const int p_controller_number); + int get_controller_number() const; + + void set_controller_value(const int p_controller_value); + int get_controller_value() const; + + virtual String as_text() const; + + InputEventMIDI(); +}; + #endif diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp new file mode 100644 index 0000000000..7b4f84473c --- /dev/null +++ b/core/os/midi_driver.cpp @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* midi_driver.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "midi_driver.h" + +#include "main/input_default.h" +#include "os/os.h" + +MIDIDriver *MIDIDriver::singleton = NULL; +MIDIDriver *MIDIDriver::get_singleton() { + + return singleton; +} + +void MIDIDriver::set_singleton() { + + singleton = this; +} + +void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) { + + Ref<InputEventMIDI> event; + event.instance(); + + if (length >= 1) { + event->set_channel(data[0] & 0xF); + event->set_message(data[0] >> 4); + } + + switch (event->get_message()) { + case MIDI_MESSAGE_AFTERTOUCH: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_pressure(data[2]); + } + break; + + case MIDI_MESSAGE_CONTROL_CHANGE: + if (length >= 3) { + event->set_controller_number(data[1]); + event->set_controller_value(data[2]); + } + break; + + case MIDI_MESSAGE_NOTE_ON: + case MIDI_MESSAGE_NOTE_OFF: + case MIDI_MESSAGE_PITCH_BEND: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_velocity(data[2]); + } + break; + + case MIDI_MESSAGE_PROGRAM_CHANGE: + if (length >= 2) { + event->set_instrument(data[1]); + } + break; + + case MIDI_MESSAGE_CHANNEL_PRESSURE: + if (length >= 2) { + event->set_pressure(data[1]); + } + break; + } + + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + id->parse_input_event(event); +} + +PoolStringArray MIDIDriver::get_connected_inputs() { + + PoolStringArray list; + return list; +} + +MIDIDriver::MIDIDriver() { + + set_singleton(); +} diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h new file mode 100644 index 0000000000..1a3a67a411 --- /dev/null +++ b/core/os/midi_driver.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* midi_driver.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MIDI_DRIVER_H +#define MIDI_DRIVER_H + +#include "core/variant.h" +#include "typedefs.h" +/** + * Multi-Platform abstraction for accessing to MIDI. + */ + +class MIDIDriver { + + static MIDIDriver *singleton; + +public: + static MIDIDriver *get_singleton(); + void set_singleton(); + + virtual Error open() = 0; + virtual void close() = 0; + + virtual PoolStringArray get_connected_inputs(); + + static void receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length); + + MIDIDriver(); + virtual ~MIDIDriver() {} +}; + +#endif diff --git a/core/os/os.cpp b/core/os/os.cpp index 89866e4044..8dcf0990fc 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -33,6 +33,7 @@ #include "dir_access.h" #include "input.h" #include "os/file_access.h" +#include "os/midi_driver.h" #include "project_settings.h" #include "servers/audio_server.h" #include "version_generated.gen.h" @@ -672,6 +673,15 @@ List<String> OS::get_restart_on_exit_arguments() const { return restart_commandline; } +PoolStringArray OS::get_connected_midi_inputs() { + + if (MIDIDriver::get_singleton()) + return MIDIDriver::get_singleton()->get_connected_inputs(); + + PoolStringArray list; + return list; +} + OS::OS() { void *volatile stack_bottom; diff --git a/core/os/os.h b/core/os/os.h index 4e0cb003fb..dd783408e8 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -189,6 +189,8 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const { return 1; } virtual int get_current_screen() const { return 0; } virtual void set_current_screen(int p_screen) {} diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp index 3b203f6977..692ff722f3 100644 --- a/core/safe_refcount.cpp +++ b/core/safe_refcount.cpp @@ -57,113 +57,113 @@ return m_val; \ } -_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(register uint32_t *pw){ +_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(volatile uint32_t *pw) { return InterlockedDecrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(volatile uint32_t *pw) { return InterlockedIncrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val; } -_ALWAYS_INLINE_ uint32_t _atomic_add_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_add_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedAdd((LONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(register uint32_t *pw, register uint32_t val){ +_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(register uint64_t *pw){ +_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t) } -_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(volatile uint64_t *pw) { return InterlockedDecrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(volatile uint64_t *pw) { return InterlockedIncrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val; } -_ALWAYS_INLINE_ uint64_t _atomic_add_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_add_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedAdd64((LONGLONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(register uint64_t *pw, register uint64_t val){ +_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t) } // The actual advertised functions; they'll call the right implementation -uint32_t atomic_conditional_increment(register uint32_t *pw) { +uint32_t atomic_conditional_increment(volatile uint32_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint32_t atomic_decrement(register uint32_t *pw) { +uint32_t atomic_decrement(volatile uint32_t *pw) { return _atomic_decrement_impl(pw); } -uint32_t atomic_increment(register uint32_t *pw) { +uint32_t atomic_increment(volatile uint32_t *pw) { return _atomic_increment_impl(pw); } -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_sub_impl(pw, val); } -uint32_t atomic_add(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_add_impl(pw, val); } -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_exchange_if_greater_impl(pw, val); } -uint64_t atomic_conditional_increment(register uint64_t *pw) { +uint64_t atomic_conditional_increment(volatile uint64_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint64_t atomic_decrement(register uint64_t *pw) { +uint64_t atomic_decrement(volatile uint64_t *pw) { return _atomic_decrement_impl(pw); } -uint64_t atomic_increment(register uint64_t *pw) { +uint64_t atomic_increment(volatile uint64_t *pw) { return _atomic_increment_impl(pw); } -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_sub_impl(pw, val); } -uint64_t atomic_add(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_add_impl(pw, val); } -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_exchange_if_greater_impl(pw, val); } #endif diff --git a/core/safe_refcount.h b/core/safe_refcount.h index eff209c2db..36bcf5e576 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -44,7 +44,7 @@ /* Bogus implementation unaware of multiprocessing */ template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { if (*pw == 0) return 0; @@ -55,7 +55,7 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { (*pw)--; @@ -63,7 +63,7 @@ static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { (*pw)++; @@ -71,7 +71,7 @@ static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { (*pw) -= val; @@ -79,7 +79,7 @@ static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { (*pw) += val; @@ -87,7 +87,7 @@ static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { if (val > *pw) *pw = val; @@ -103,7 +103,7 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v // Clang states it supports GCC atomic builtins. template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -115,31 +115,31 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { return __sync_sub_and_fetch(pw, 1); } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { return __sync_add_and_fetch(pw, 1); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { return __sync_sub_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { return __sync_add_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -153,19 +153,19 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v #elif defined(_MSC_VER) // For MSVC use a separate compilation unit to prevent windows.h from polluting // the global namespace. -uint32_t atomic_conditional_increment(register uint32_t *pw); -uint32_t atomic_decrement(register uint32_t *pw); -uint32_t atomic_increment(register uint32_t *pw); -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val); -uint32_t atomic_add(register uint32_t *pw, register uint32_t val); -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val); - -uint64_t atomic_conditional_increment(register uint64_t *pw); -uint64_t atomic_decrement(register uint64_t *pw); -uint64_t atomic_increment(register uint64_t *pw); -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val); -uint64_t atomic_add(register uint64_t *pw, register uint64_t val); -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val); +uint32_t atomic_conditional_increment(volatile uint32_t *pw); +uint32_t atomic_decrement(volatile uint32_t *pw); +uint32_t atomic_increment(volatile uint32_t *pw); +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val); + +uint64_t atomic_conditional_increment(volatile uint64_t *pw); +uint64_t atomic_decrement(volatile uint64_t *pw); +uint64_t atomic_increment(volatile uint64_t *pw); +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val); #else //no threads supported? diff --git a/core/translation.cpp b/core/translation.cpp index aaa4de5912..78115c3749 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -1171,13 +1171,11 @@ void TranslationServer::_bind_methods() { void TranslationServer::load_translations() { String locale = get_locale(); - bool found = _load_translations("locale/translations"); //all + _load_translations("locale/translations"); //all + _load_translations("locale/translations_" + locale.substr(0, 2)); - if (_load_translations("locale/translations_" + locale.substr(0, 2))) - found = true; if (locale.substr(0, 2) != locale) { - if (_load_translations("locale/translations_" + locale)) - found = true; + _load_translations("locale/translations_" + locale); } } diff --git a/core/ustring.cpp b/core/ustring.cpp index bb08dd13c4..f552cb13ac 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -161,14 +161,21 @@ void String::copy_from(const CharType *p_cstr, int p_clip_to) { return; } - resize(len + 1); - set(len, 0); + copy_from_unchecked(p_cstr, len); +} - CharType *dst = &operator[](0); +// assumes the following have already been validated: +// p_char != NULL +// p_length > 0 +// p_length <= p_char strlen +void String::copy_from_unchecked(const CharType *p_char, int p_length) { + resize(p_length + 1); + set(p_length, 0); - for (int i = 0; i < len; i++) { + CharType *dst = &operator[](0); - dst[i] = p_cstr[i]; + for (int i = 0; i < p_length; i++) { + dst[i] = p_char[i]; } } @@ -2246,7 +2253,9 @@ String String::substr(int p_from, int p_chars) const { return String(*this); } - return String(&c_str()[p_from], p_chars); + String s = String(); + s.copy_from_unchecked(&c_str()[p_from], p_chars); + return s; } int String::find_last(const String &p_str) const { diff --git a/core/ustring.h b/core/ustring.h index b57e9629d9..001d111d64 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -65,6 +65,7 @@ class String : public Vector<CharType> { void copy_from(const char *p_cstr); void copy_from(const CharType *p_cstr, int p_clip_to = -1); void copy_from(const CharType &p_char); + void copy_from_unchecked(const CharType *p_char, int p_length); bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; public: diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index e48eb82691..b21d402468 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -35,19 +35,20 @@ func get_import_options(i): return [{"name": "my_option", "default_value": false}] - func load(src, dst, opts, r_platform_variants, r_gen_files): + func import(source_file, save_path, options, r_platform_variants, r_gen_files): var file = File.new() - if file.open(src, File.READ) != OK: + if file.open(source_file, File.READ) != OK: return FAILED var mesh = Mesh.new() - var save = dst + "." + get_save_extension() - ResourceSaver.save(file, mesh) + var filename = save_path + "." + get_save_extension() + ResourceSaver.save(filename, mesh) return OK [/codeblock] </description> <tutorials> + <link>http://docs.godotengine.org/en/3.0/tutorials/plugins/editor/import_plugins.html</link> </tutorials> <demos> </demos> @@ -119,7 +120,7 @@ <return type="String"> </return> <description> - Get the godot resource type associated with this loader. e.g. "Mesh" or "Animation". + Get the Godot resource type associated with this loader. e.g. "Mesh" or "Animation". </description> </method> <method name="get_save_extension" qualifiers="virtual"> diff --git a/drivers/SCsub b/drivers/SCsub index 2c5e9434e8..f9cfa3fb05 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -21,6 +21,11 @@ if (env["platform"] == "windows"): if env['xaudio2']: SConscript("xaudio2/SCsub") +# Midi drivers +SConscript('alsamidi/SCsub') +SConscript('coremidi/SCsub') +SConscript('winmidi/SCsub') + # Graphics drivers if (env["platform"] != "server"): SConscript('gles3/SCsub') diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/alsamidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/alsamidi/alsa_midi.cpp b/drivers/alsamidi/alsa_midi.cpp new file mode 100644 index 0000000000..599470d7e0 --- /dev/null +++ b/drivers/alsamidi/alsa_midi.cpp @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* alsa_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef ALSAMIDI_ENABLED + +#include <errno.h> + +#include "alsa_midi.h" +#include "os/os.h" +#include "print_string.h" + +static int get_message_size(uint8_t message) { + switch (message & 0xF0) { + case 0x80: // note off + case 0x90: // note on + case 0xA0: // aftertouch + case 0xB0: // continuous controller + return 3; + + case 0xC0: // patch change + case 0xD0: // channel pressure + case 0xE0: // pitch bend + return 2; + } + + return 256; +} + +void MIDIDriverALSAMidi::thread_func(void *p_udata) { + MIDIDriverALSAMidi *md = (MIDIDriverALSAMidi *)p_udata; + uint64_t timestamp = 0; + uint8_t buffer[256]; + int expected_size = 255; + int bytes = 0; + + while (!md->exit_thread) { + int ret; + + md->lock(); + + for (int i = 0; i < md->connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = md->connected_inputs[i]; + do { + uint8_t byte = 0; + ret = snd_rawmidi_read(midi_in, &byte, 1); + if (ret < 0) { + if (ret != -EAGAIN) { + ERR_PRINTS("snd_rawmidi_read error: " + String(snd_strerror(ret))); + } + } else { + if (byte & 0x80) { + // Flush previous packet if there is any + if (bytes) { + md->receive_input_packet(timestamp, buffer, bytes); + bytes = 0; + } + expected_size = get_message_size(byte); + } + + if (bytes < 256) { + buffer[bytes++] = byte; + // If we know the size of the current packet receive it if it reached the expected size + if (bytes >= expected_size) { + md->receive_input_packet(timestamp, buffer, bytes); + bytes = 0; + } + } + } + } while (ret > 0); + } + + md->unlock(); + + OS::get_singleton()->delay_usec(1000); + } +} + +Error MIDIDriverALSAMidi::open() { + + void **hints; + + if (snd_device_name_hint(-1, "rawmidi", &hints) < 0) + return ERR_CANT_OPEN; + + int i = 0; + for (void **n = hints; *n != NULL; n++) { + char *name = snd_device_name_get_hint(*n, "NAME"); + + if (name != NULL) { + snd_rawmidi_t *midi_in; + int ret = snd_rawmidi_open(&midi_in, NULL, name, SND_RAWMIDI_NONBLOCK); + if (ret >= 0) { + connected_inputs.insert(i++, midi_in); + } + } + + if (name != NULL) + free(name); + } + snd_device_name_free_hint(hints); + + mutex = Mutex::create(); + thread = Thread::create(MIDIDriverALSAMidi::thread_func, this); + + return OK; +} + +void MIDIDriverALSAMidi::close() { + + if (thread) { + exit_thread = true; + Thread::wait_to_finish(thread); + + memdelete(thread); + thread = NULL; + } + + if (mutex) { + memdelete(mutex); + mutex = NULL; + } + + for (int i = 0; i < connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_close(midi_in); + } + connected_inputs.clear(); +} + +void MIDIDriverALSAMidi::lock() const { + + if (mutex) + mutex->lock(); +} + +void MIDIDriverALSAMidi::unlock() const { + + if (mutex) + mutex->unlock(); +} + +PoolStringArray MIDIDriverALSAMidi::get_connected_inputs() { + + PoolStringArray list; + + lock(); + for (int i = 0; i < connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_info_t *info; + + snd_rawmidi_info_malloc(&info); + snd_rawmidi_info(midi_in, info); + list.push_back(snd_rawmidi_info_get_name(info)); + snd_rawmidi_info_free(info); + } + unlock(); + + return list; +} + +MIDIDriverALSAMidi::MIDIDriverALSAMidi() { + + mutex = NULL; + thread = NULL; + + exit_thread = false; +} + +MIDIDriverALSAMidi::~MIDIDriverALSAMidi() { + + close(); +} + +#endif diff --git a/drivers/alsamidi/alsa_midi.h b/drivers/alsamidi/alsa_midi.h new file mode 100644 index 0000000000..90e458a365 --- /dev/null +++ b/drivers/alsamidi/alsa_midi.h @@ -0,0 +1,69 @@ +/*************************************************************************/ +/* alsa_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef ALSAMIDI_ENABLED + +#ifndef ALSA_MIDI_H +#define ALSA_MIDI_H + +#include <alsa/asoundlib.h> +#include <stdio.h> + +#include "core/os/mutex.h" +#include "core/os/thread.h" +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverALSAMidi : public MIDIDriver { + + Thread *thread; + Mutex *mutex; + + Vector<snd_rawmidi_t *> connected_inputs; + + bool exit_thread; + + static void thread_func(void *p_udata); + + void lock() const; + void unlock() const; + +public: + virtual Error open(); + virtual void close(); + + virtual PoolStringArray get_connected_inputs(); + + MIDIDriverALSAMidi(); + virtual ~MIDIDriverALSAMidi(); +}; + +#endif +#endif diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/coremidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/coremidi/core_midi.cpp b/drivers/coremidi/core_midi.cpp new file mode 100644 index 0000000000..3619be4a8e --- /dev/null +++ b/drivers/coremidi/core_midi.cpp @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* core_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef COREMIDI_ENABLED + +#include "core_midi.h" +#include "print_string.h" + +#include <CoreAudio/HostTime.h> +#include <CoreServices/CoreServices.h> + +void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) { + MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet); + for (int i = 0; i < packet_list->numPackets; i++) { + receive_input_packet(packet->timeStamp, packet->data, packet->length); + packet = MIDIPacketNext(packet); + } +} + +Error MIDIDriverCoreMidi::open() { + + CFStringRef name = CFStringCreateWithCString(NULL, "Godot", kCFStringEncodingASCII); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client); + CFRelease(name); + if (result != noErr) { + ERR_PRINTS("MIDIClientCreate failed: " + String(GetMacOSStatusErrorString(result))); + return ERR_CANT_OPEN; + } + + result = MIDIInputPortCreate(client, CFSTR("Godot Input"), MIDIDriverCoreMidi::read, (void *)this, &port_in); + if (result != noErr) { + ERR_PRINTS("MIDIInputPortCreate failed: " + String(GetMacOSStatusErrorString(result))); + return ERR_CANT_OPEN; + } + + int sources = MIDIGetNumberOfSources(); + for (int i = 0; i < sources; i++) { + + MIDIEndpointRef source = MIDIGetSource(i); + if (source != NULL) { + MIDIPortConnectSource(port_in, source, (void *)this); + connected_sources.insert(i, source); + } + } + + return OK; +} + +void MIDIDriverCoreMidi::close() { + + for (int i = 0; i < connected_sources.size(); i++) { + MIDIEndpointRef source = connected_sources[i]; + MIDIPortDisconnectSource(port_in, source); + } + connected_sources.clear(); + + if (port_in != 0) { + MIDIPortDispose(port_in); + port_in = 0; + } + + if (client != 0) { + MIDIClientDispose(client); + client = 0; + } +} + +MIDIDriverCoreMidi::MIDIDriverCoreMidi() { + + client = 0; +} + +MIDIDriverCoreMidi::~MIDIDriverCoreMidi() { + + close(); +} + +#endif diff --git a/drivers/coremidi/core_midi.h b/drivers/coremidi/core_midi.h new file mode 100644 index 0000000000..fd35e12f4b --- /dev/null +++ b/drivers/coremidi/core_midi.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* core_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef COREMIDI_ENABLED + +#ifndef CORE_MIDI_H +#define CORE_MIDI_H + +#include <stdio.h> + +#include <CoreMIDI/CoreMIDI.h> + +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverCoreMidi : public MIDIDriver { + + MIDIClientRef client; + MIDIPortRef port_in; + + Vector<MIDIEndpointRef> connected_sources; + + static void read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con); + +public: + virtual Error open(); + virtual void close(); + + MIDIDriverCoreMidi(); + virtual ~MIDIDriverCoreMidi(); +}; + +#endif +#endif diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/dummy/texture_loader_dummy.cpp index 6d3e176bbb..b099019d17 100644 --- a/drivers/dummy/texture_loader_dummy.cpp +++ b/drivers/dummy/texture_loader_dummy.cpp @@ -45,10 +45,6 @@ RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_origi dstbuff.resize(rowsize * height); - PoolVector<uint8_t>::Write dstbuff_write = dstbuff.write(); - - uint8_t *data = dstbuff_write.ptr(); - uint8_t **row_p = memnew_arr(uint8_t *, height); for (unsigned int i = 0; i < height; i++) { diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 3622e48ecd..864b9714a9 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -403,7 +403,6 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; - int ctr = 0; // If eol is set to a positive number, you're at the end of the list if (eol > 0) { diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/winmidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/winmidi/win_midi.cpp b/drivers/winmidi/win_midi.cpp new file mode 100644 index 0000000000..6da6e31b2b --- /dev/null +++ b/drivers/winmidi/win_midi.cpp @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* win_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef WINMIDI_ENABLED + +#include "win_midi.h" +#include "print_string.h" + +void MIDIDriverWinMidi::read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { + + if (wMsg == MIM_DATA) { + receive_input_packet((uint64_t)dwParam2, (uint8_t *)&dwParam1, 3); + } +} + +Error MIDIDriverWinMidi::open() { + + for (UINT i = 0; i < midiInGetNumDevs(); i++) { + HMIDIIN midi_in; + + MMRESULT res = midiInOpen(&midi_in, i, (DWORD_PTR)read, (DWORD_PTR)this, CALLBACK_FUNCTION); + if (res == MMSYSERR_NOERROR) { + midiInStart(midi_in); + connected_sources.insert(i, midi_in); + } else { + char err[256]; + midiInGetErrorText(res, err, 256); + ERR_PRINTS("midiInOpen error: " + String(err)); + } + } + + return OK; +} + +PoolStringArray MIDIDriverWinMidi::get_connected_inputs() { + + PoolStringArray list; + + for (int i = 0; i < connected_sources.size(); i++) { + HMIDIIN midi_in = connected_sources[i]; + UINT id = 0; + MMRESULT res = midiInGetID(midi_in, &id); + if (res == MMSYSERR_NOERROR) { + MIDIINCAPS caps; + res = midiInGetDevCaps(i, &caps, sizeof(MIDIINCAPS)); + if (res == MMSYSERR_NOERROR) { + list.push_back(caps.szPname); + } + } + } + + return list; +} + +void MIDIDriverWinMidi::close() { + + for (int i = 0; i < connected_sources.size(); i++) { + HMIDIIN midi_in = connected_sources[i]; + midiInStop(midi_in); + midiInClose(midi_in); + } + connected_sources.clear(); +} + +MIDIDriverWinMidi::MIDIDriverWinMidi() { +} + +MIDIDriverWinMidi::~MIDIDriverWinMidi() { + + close(); +} + +#endif diff --git a/drivers/winmidi/win_midi.h b/drivers/winmidi/win_midi.h new file mode 100644 index 0000000000..1cf9b19b5d --- /dev/null +++ b/drivers/winmidi/win_midi.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* win_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifdef WINMIDI_ENABLED + +#ifndef WIN_MIDI_H +#define WIN_MIDI_H + +#include <stdio.h> +#include <windows.h> + +#include <mmsystem.h> + +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverWinMidi : public MIDIDriver { + + Vector<HMIDIIN> connected_sources; + + static void CALLBACK read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); + +public: + virtual Error open(); + virtual void close(); + + virtual PoolStringArray get_connected_inputs(); + + MIDIDriverWinMidi(); + virtual ~MIDIDriverWinMidi(); +}; + +#endif +#endif diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index db108f9c6b..a1002ef4f9 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -97,8 +97,6 @@ void AudioDriverXAudio2::thread_func(void *p_udata) { AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata; - uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate)) * 1000000; - while (!ad->exit_thread) { if (!ad->active) { diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 6aec6135f1..925f97de8e 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -784,6 +784,345 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/open_scripts/v_scroll_speed")); } +void CodeTextEditor::trim_trailing_whitespace() { + bool trimed_whitespace = false; + for (int i = 0; i < text_editor->get_line_count(); i++) { + String line = text_editor->get_line(i); + if (line.ends_with(" ") || line.ends_with("\t")) { + + if (!trimed_whitespace) { + text_editor->begin_complex_operation(); + trimed_whitespace = true; + } + + int end = 0; + for (int j = line.length() - 1; j > -1; j--) { + if (line[j] != ' ' && line[j] != '\t') { + end = j + 1; + break; + } + } + text_editor->set_line(i, line.substr(0, end)); + } + } + + if (trimed_whitespace) { + text_editor->end_complex_operation(); + text_editor->update(); + } +} + +void CodeTextEditor::convert_indent_to_spaces() { + int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); + String indent = ""; + + for (int i = 0; i < indent_size; i++) { + indent += " "; + } + + int cursor_line = text_editor->cursor_get_line(); + int cursor_column = text_editor->cursor_get_column(); + + bool changed_indentation = false; + for (int i = 0; i < text_editor->get_line_count(); i++) { + String line = text_editor->get_line(i); + + if (line.length() <= 0) { + continue; + } + + int j = 0; + while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { + if (line[j] == '\t') { + if (!changed_indentation) { + text_editor->begin_complex_operation(); + changed_indentation = true; + } + if (cursor_line == i && cursor_column > j) { + cursor_column += indent_size - 1; + } + line = line.left(j) + indent + line.right(j + 1); + } + j++; + } + if (changed_indentation) { + text_editor->set_line(i, line); + } + } + if (changed_indentation) { + text_editor->cursor_set_column(cursor_column); + text_editor->end_complex_operation(); + text_editor->update(); + } +} + +void CodeTextEditor::convert_indent_to_tabs() { + int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); + indent_size -= 1; + + int cursor_line = text_editor->cursor_get_line(); + int cursor_column = text_editor->cursor_get_column(); + + bool changed_indentation = false; + for (int i = 0; i < text_editor->get_line_count(); i++) { + String line = text_editor->get_line(i); + + if (line.length() <= 0) { + continue; + } + + int j = 0; + int space_count = -1; + while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { + if (line[j] != '\t') { + space_count++; + + if (space_count == indent_size) { + if (!changed_indentation) { + text_editor->begin_complex_operation(); + changed_indentation = true; + } + if (cursor_line == i && cursor_column > j) { + cursor_column -= indent_size; + } + line = line.left(j - indent_size) + "\t" + line.right(j + 1); + j = 0; + space_count = -1; + } + } else { + space_count = -1; + } + j++; + } + if (changed_indentation) { + text_editor->set_line(i, line); + } + } + if (changed_indentation) { + text_editor->cursor_set_column(cursor_column); + text_editor->end_complex_operation(); + text_editor->update(); + } +} + +void CodeTextEditor::convert_case(CaseStyle p_case) { + if (!text_editor->is_selection_active()) { + return; + } + + text_editor->begin_complex_operation(); + + int begin = text_editor->get_selection_from_line(); + int end = text_editor->get_selection_to_line(); + int begin_col = text_editor->get_selection_from_column(); + int end_col = text_editor->get_selection_to_column(); + + for (int i = begin; i <= end; i++) { + int len = text_editor->get_line(i).length(); + if (i == end) + len -= len - end_col; + if (i == begin) + len -= begin_col; + String new_line = text_editor->get_line(i).substr(i == begin ? begin_col : 0, len); + + switch (p_case) { + case UPPER: { + new_line = new_line.to_upper(); + } break; + case LOWER: { + new_line = new_line.to_lower(); + } break; + case CAPITALIZE: { + new_line = new_line.capitalize(); + } break; + } + + if (i == begin) { + new_line = text_editor->get_line(i).left(begin_col) + new_line; + } + if (i == end) { + new_line = new_line + text_editor->get_line(i).right(end_col); + } + text_editor->set_line(i, new_line); + } + text_editor->end_complex_operation(); +} + +void CodeTextEditor::move_lines_up() { + text_editor->begin_complex_operation(); + if (text_editor->is_selection_active()) { + int from_line = text_editor->get_selection_from_line(); + int from_col = text_editor->get_selection_from_column(); + int to_line = text_editor->get_selection_to_line(); + int to_column = text_editor->get_selection_to_column(); + + for (int i = from_line; i <= to_line; i++) { + int line_id = i; + int next_id = i - 1; + + if (line_id == 0 || next_id < 0) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + int from_line_up = from_line > 0 ? from_line - 1 : from_line; + int to_line_up = to_line > 0 ? to_line - 1 : to_line; + text_editor->select(from_line_up, from_col, to_line_up, to_column); + } else { + int line_id = text_editor->cursor_get_line(); + int next_id = line_id - 1; + + if (line_id == 0 || next_id < 0) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + text_editor->end_complex_operation(); + text_editor->update(); +} + +void CodeTextEditor::move_lines_down() { + text_editor->begin_complex_operation(); + if (text_editor->is_selection_active()) { + int from_line = text_editor->get_selection_from_line(); + int from_col = text_editor->get_selection_from_column(); + int to_line = text_editor->get_selection_to_line(); + int to_column = text_editor->get_selection_to_column(); + + for (int i = to_line; i >= from_line; i--) { + int line_id = i; + int next_id = i + 1; + + if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + int from_line_down = from_line < text_editor->get_line_count() ? from_line + 1 : from_line; + int to_line_down = to_line < text_editor->get_line_count() ? to_line + 1 : to_line; + text_editor->select(from_line_down, from_col, to_line_down, to_column); + } else { + int line_id = text_editor->cursor_get_line(); + int next_id = line_id + 1; + + if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + text_editor->end_complex_operation(); + text_editor->update(); +} + +void CodeTextEditor::delete_lines() { + text_editor->begin_complex_operation(); + if (text_editor->is_selection_active()) { + int to_line = text_editor->get_selection_to_line(); + int from_line = text_editor->get_selection_from_line(); + int count = Math::abs(to_line - from_line) + 1; + while (count) { + text_editor->set_line(text_editor->cursor_get_line(), ""); + text_editor->backspace_at_cursor(); + count--; + if (count) + text_editor->unfold_line(from_line); + } + text_editor->cursor_set_line(from_line - 1); + text_editor->deselect(); + } else { + int line = text_editor->cursor_get_line(); + text_editor->set_line(text_editor->cursor_get_line(), ""); + text_editor->backspace_at_cursor(); + text_editor->unfold_line(line); + text_editor->cursor_set_line(line); + } + text_editor->end_complex_operation(); +} + +void CodeTextEditor::code_lines_down() { + int from_line = text_editor->cursor_get_line(); + int to_line = text_editor->cursor_get_line(); + int column = text_editor->cursor_get_column(); + + if (text_editor->is_selection_active()) { + from_line = text_editor->get_selection_from_line(); + to_line = text_editor->get_selection_to_line(); + column = text_editor->cursor_get_column(); + } + int next_line = to_line + 1; + + if (to_line >= text_editor->get_line_count() - 1) { + text_editor->set_line(to_line, text_editor->get_line(to_line) + "\n"); + } + + text_editor->begin_complex_operation(); + for (int i = from_line; i <= to_line; i++) { + + text_editor->unfold_line(i); + if (i >= text_editor->get_line_count() - 1) { + text_editor->set_line(i, text_editor->get_line(i) + "\n"); + } + String line_clone = text_editor->get_line(i); + text_editor->insert_at(line_clone, next_line); + next_line++; + } + + text_editor->cursor_set_column(column); + if (text_editor->is_selection_active()) { + text_editor->select(to_line + 1, text_editor->get_selection_from_column(), next_line - 1, text_editor->get_selection_to_column()); + } + + text_editor->end_complex_operation(); + text_editor->update(); +} + +void CodeTextEditor::goto_line(int p_line) { + text_editor->deselect(); + text_editor->unfold_line(p_line); + text_editor->call_deferred("cursor_set_line", p_line); +} + +void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { + text_editor->unfold_line(p_line); + text_editor->call_deferred("cursor_set_line", p_line); + text_editor->call_deferred("cursor_set_column", p_begin); + text_editor->select(p_line, p_begin, p_line, p_end); +} + +Variant CodeTextEditor::get_edit_state() { + Dictionary state; + + state["scroll_position"] = text_editor->get_v_scroll(); + state["column"] = text_editor->cursor_get_column(); + state["row"] = text_editor->cursor_get_line(); + + return state; +} + +void CodeTextEditor::set_edit_state(const Variant &p_state) { + Dictionary state = p_state; + text_editor->cursor_set_column(state["column"]); + text_editor->cursor_set_line(state["row"]); + text_editor->set_v_scroll(state["scroll_position"]); + text_editor->grab_focus(); +} + void CodeTextEditor::set_error(const String &p_error) { error->set_text(p_error); diff --git a/editor/code_editor.h b/editor/code_editor.h index 2a3bb1ba76..903f61d87d 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -186,6 +186,29 @@ protected: static void _bind_methods(); public: + void trim_trailing_whitespace(); + + void convert_indent_to_spaces(); + void convert_indent_to_tabs(); + + enum CaseStyle { + UPPER, + LOWER, + CAPITALIZE, + }; + void convert_case(CaseStyle p_case); + + void move_lines_up(); + void move_lines_down(); + void delete_lines(); + void code_lines_down(); + + void goto_line(int p_line); + void goto_line_selection(int p_line, int p_begin, int p_end); + + Variant get_edit_state(); + void set_edit_state(const Variant &p_state); + void update_editor_settings(); void set_error(const String &p_error); void update_line_and_column() { _line_col_changed(); } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ff97878e71..b366ebd911 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -112,6 +112,7 @@ #include "editor/plugins/sprite_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h" #include "editor/plugins/style_box_editor_plugin.h" +#include "editor/plugins/text_editor.h" #include "editor/plugins/texture_editor_plugin.h" #include "editor/plugins/texture_region_editor_plugin.h" #include "editor/plugins/theme_editor_plugin.h" @@ -156,7 +157,6 @@ void EditorNode::_update_scene_tabs() { scene_tabs->set_current_tab(editor_data.get_edited_scene()); - int current = editor_data.get_edited_scene(); if (scene_tabs->get_offset_buttons_visible()) { // move add button to fixed position on the tabbar if (scene_tab_add->get_parent() == scene_tabs) { @@ -5469,6 +5469,7 @@ EditorNode::EditorNode() { EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor(); ScriptTextEditor::register_editor(); //register one for text scripts + TextEditor::register_editor(); if (StreamPeerSSL::is_available()) { add_editor_plugin(memnew(AssetLibraryEditorPlugin(this))); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 466b12157d..c8e97b071f 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -298,17 +298,17 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("interface/editor/main_font_size", 14); hints["interface/editor/main_font_size"] = PropertyInfo(Variant::INT, "interface/editor/main_font_size", PROPERTY_HINT_RANGE, "10,40,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/code_font_size", 14); - hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/main_font_hinting", 2); - hints["interface/editor/main_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/main_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/main_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/main_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/code_font_hinting", 2); - hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/main_font", ""); - hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/main_font_bold", ""); - hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/code_font", ""); - hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/dim_editor_on_dialog_popup", true); _initial_set("interface/editor/dim_amount", 0.6f); hints["interface/editor/dim_amount"] = PropertyInfo(Variant::REAL, "interface/editor/dim_amount", PROPERTY_HINT_RANGE, "0,1,0.01", PROPERTY_USAGE_DEFAULT); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index ea9f6db61a..18cc52a5c6 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -237,8 +237,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = ImageLoaderSVG::set_convert_colors(NULL); clock_t end_time = clock(); - - double time_d = (double)(end_time - begin_time) / CLOCKS_PER_SEC; #else print_line("Sorry no icons for you"); #endif @@ -257,7 +255,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { String preset = EDITOR_DEF("interface/theme/preset", "Default"); - int icon_font_color_setting = EDITOR_DEF("interface/theme/icon_and_font_color", 0); bool highlight_tabs = EDITOR_DEF("interface/theme/highlight_tabs", false); int border_size = EDITOR_DEF("interface/theme/border_size", 1); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index f65fb5365b..37f86cc912 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1319,6 +1319,9 @@ void FileSystemDock::_file_option(int p_option) { String fpath = files->get_item_metadata(idx); OS::get_singleton()->set_clipboard(fpath); } break; + case FILE_NEW_RESOURCE: { + new_resource_dialog->popup_create(true); + } break; } } @@ -1393,6 +1396,21 @@ void FileSystemDock::_folder_option(int p_option) { } } +void FileSystemDock::_resource_created() const { + Object *c = new_resource_dialog->instance_selected(); + + ERR_FAIL_COND(!c); + Resource *r = Object::cast_to<Resource>(c); + ERR_FAIL_COND(!r); + + REF res(r); + editor->push_item(c); + + RES current_res = RES(r); + + editor->save_resource_as(current_res); +} + void FileSystemDock::_go_to_file_list() { if (low_height_mode) { @@ -1738,6 +1756,7 @@ void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) { file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); + file_options->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER); file_options->set_position(files->get_global_position() + p_pos); @@ -1750,6 +1769,7 @@ void FileSystemDock::_rmb_pressed(const Vector2 &p_pos) { file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); + file_options->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER); file_options->set_position(files->get_global_position() + p_pos); file_options->popup(); @@ -1862,6 +1882,7 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_file_option"), &FileSystemDock::_file_option); ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option); ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm); + ClassDB::bind_method(D_METHOD("_resource_created"), &FileSystemDock::_resource_created); ClassDB::bind_method(D_METHOD("_move_operation_confirm", "to_path", "overwrite"), &FileSystemDock::_move_operation_confirm, DEFVAL(false)); ClassDB::bind_method(D_METHOD("_move_with_overwrite"), &FileSystemDock::_move_with_overwrite); ClassDB::bind_method(D_METHOD("_rename_operation_confirm"), &FileSystemDock::_rename_operation_confirm); @@ -2087,6 +2108,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { make_script_dialog_text->set_title(TTR("Create Script")); add_child(make_script_dialog_text); + new_resource_dialog = memnew(CreateDialog); + add_child(new_resource_dialog); + new_resource_dialog->set_base_type("Resource"); + new_resource_dialog->connect("create", this, "_resource_created"); + updating_tree = false; initialized = false; import_dock_needs_update = false; diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index e8ab803cca..6a0c73d52e 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -47,6 +47,8 @@ #include "os/dir_access.h" #include "os/thread.h" +#include "create_dialog.h" + #include "dependency_editor.h" #include "editor_dir_dialog.h" #include "editor_file_system.h" @@ -78,7 +80,8 @@ private: FILE_NEW_FOLDER, FILE_NEW_SCRIPT, FILE_SHOW_IN_EXPLORER, - FILE_COPY_PATH + FILE_COPY_PATH, + FILE_NEW_RESOURCE }; enum FolderMenu { @@ -131,6 +134,7 @@ private: LineEdit *make_dir_dialog_text; ConfirmationDialog *overwrite_dialog; ScriptCreateDialog *make_script_dialog_text; + CreateDialog *new_resource_dialog; class FileOrFolder { public: @@ -191,6 +195,7 @@ private: void _update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const; void _update_project_settings_after_move(const Map<String, String> &p_renames) const; + void _resource_created() const; void _make_dir_confirm(); void _rename_operation_confirm(); void _duplicate_operation_confirm(); diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index eb0bc0f782..f2f4328cd2 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1711,14 +1711,14 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node #endif for (int i = 0; i < n->children.size(); i++) { if (state.nodes[n->children[i]]->joints.size()) { - _generate_bone(state, n->children[i], skeletons, Vector<int>(), node); + _generate_bone(state, n->children[i], skeletons, node); } else { _generate_node(state, n->children[i], node, p_owner, skeletons); } } } -void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, const Vector<int> &p_parent_bones, Node *p_parent_node) { +void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node) { ERR_FAIL_INDEX(p_node, state.nodes.size()); if (state.skeleton_nodes.has(p_node)) { @@ -1733,30 +1733,28 @@ void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vecto } GLTFNode *n = state.nodes[p_node]; - Vector<int> parent_bones; for (int i = 0; i < n->joints.size(); i++) { - ERR_FAIL_COND(n->joints[i].skin < 0); + const int skin = n->joints[i].skin; + ERR_FAIL_COND(skin < 0); - int bone_index = n->joints[i].bone; + Skeleton *s = skeletons[skin]; + const GLTFNode *gltf_bone_node = state.nodes[state.skins[skin].bones[n->joints[i].bone].node]; + const String bone_name = gltf_bone_node->name; + const int parent = gltf_bone_node->parent; + const int parent_index = s->find_bone(state.nodes[parent]->name); - Skeleton *s = skeletons[n->joints[i].skin]; - while (s->get_bone_count() <= bone_index) { - s->add_bone("Bone " + itos(s->get_bone_count())); - } - - if (p_parent_bones.size()) { - s->set_bone_parent(bone_index, p_parent_bones[i]); - } - s->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse()); + s->add_bone(bone_name); + const int bone_index = s->find_bone(bone_name); + s->set_bone_parent(bone_index, parent_index); + s->set_bone_rest(bone_index, state.skins[skin].bones[n->joints[i].bone].inverse_bind.affine_inverse()); n->godot_nodes.push_back(s); n->joints[i].godot_bone_index = bone_index; - parent_bones.push_back(bone_index); } for (int i = 0; i < n->children.size(); i++) { - _generate_bone(state, n->children[i], skeletons, parent_bones, p_parent_node); + _generate_bone(state, n->children[i], skeletons, p_parent_node); } } @@ -2070,7 +2068,7 @@ Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, int p_bake_f } for (int i = 0; i < state.root_nodes.size(); i++) { if (state.nodes[state.root_nodes[i]]->joints.size()) { - _generate_bone(state, state.root_nodes[i], skeletons, Vector<int>(), root); + _generate_bone(state, state.root_nodes[i], skeletons, root); } else { _generate_node(state, state.root_nodes[i], root, root, skeletons); } diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index 088036ce75..e8f3bdff62 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -311,7 +311,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<Basis> _decode_accessor_as_basis(GLTFState &state, int p_accessor, bool p_for_vertex); Vector<Transform> _decode_accessor_as_xform(GLTFState &state, int p_accessor, bool p_for_vertex); - void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, const Vector<int> &p_parent_bones, Node *p_parent_node); + void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node); void _generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons); void _import_animation(GLTFState &state, AnimationPlayer *ap, int index, int bake_fps, Vector<Skeleton *> skeletons); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 0d0b12c911..43baabe2f5 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -140,6 +140,7 @@ void InspectorDock::_load_resource(const String &p_type) { void InspectorDock::_resource_file_selected(String p_file) { RES res = ResourceLoader::load(p_file); + if (res.is_null()) { warning_dialog->get_ok()->set_text("Ugh"); warning_dialog->set_text(TTR("Failed to load resource.")); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 876da7f61a..a1dc746702 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -31,7 +31,6 @@ #include "script_editor_plugin.h" #include "core/io/resource_loader.h" -#include "core/io/resource_saver.h" #include "core/os/file_access.h" #include "core/os/input.h" #include "core/os/keyboard.h" @@ -283,7 +282,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (!se) { - continue; } @@ -402,7 +400,10 @@ void ScriptEditor::_go_to_tab(int p_idx) { if (is_visible_in_tree()) Object::cast_to<ScriptEditorBase>(c)->ensure_focus(); - notify_script_changed(Object::cast_to<ScriptEditorBase>(c)->get_edited_script()); + Ref<Script> script = Object::cast_to<ScriptEditorBase>(c)->get_edited_resource(); + if (script != NULL) { + notify_script_changed(script); + } } if (Object::cast_to<EditorHelp>(c)) { @@ -482,12 +483,23 @@ void ScriptEditor::_open_recent_script(int p_idx) { String path = rc[p_idx]; // if its not on disk its a help file or deleted if (FileAccess::exists(path)) { - Ref<Script> script = ResourceLoader::load(path); - if (script.is_valid()) { - edit(script, true); - return; + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + + if (extensions.find(path.get_extension())) { + Ref<Script> script = ResourceLoader::load(path); + if (script.is_valid()) { + edit(script, true); + return; + } } + Error err; + Ref<TextFile> text_file = _load_text_file(path, &err); + if (text_file.is_valid()) { + edit(text_file, true); + return; + } // if it's a path then its most likely a deleted file not help } else if (!path.is_resource_file()) { _help_class_open(path); @@ -513,12 +525,17 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { return; Node *tselected = tab_container->get_child(selected); + ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); if (current) { if (p_save) { apply_scripts(); } - notify_script_close(current->get_edited_script()); + + Ref<Script> script = current->get_edited_resource(); + if (script != NULL) { + notify_script_close(script); + } } // roll back to previous tab @@ -589,7 +606,7 @@ void ScriptEditor::_close_docs_tab() { void ScriptEditor::_copy_script_path() { ScriptEditorBase *se = _get_current_editor(); - Ref<Script> script = se->get_edited_script(); + RES script = se->get_edited_resource(); OS::get_singleton()->set_clipboard(script->get_path()); } @@ -655,7 +672,7 @@ void ScriptEditor::_resave_scripts(const String &p_str) { if (!se) continue; - Ref<Script> script = se->get_edited_script(); + RES script = se->get_edited_resource(); if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) continue; //internal script, who cares @@ -672,7 +689,14 @@ void ScriptEditor::_resave_scripts(const String &p_str) { } } - editor->save_resource(script); + Ref<TextFile> text_file = script; + if (text_file != NULL) { + se->apply_code(); + _save_text_file(text_file, text_file->get_path()); + break; + } else { + editor->save_resource(script); + } se->tag_saved_version(); } @@ -689,25 +713,37 @@ void ScriptEditor::_reload_scripts() { continue; } - Ref<Script> script = se->get_edited_script(); + RES edited_res = se->get_edited_resource(); - if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) { + if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) { continue; //internal script, who cares } - uint64_t last_date = script->get_last_modified_time(); - uint64_t date = FileAccess::get_modified_time(script->get_path()); + uint64_t last_date = edited_res->get_last_modified_time(); + uint64_t date = FileAccess::get_modified_time(edited_res->get_path()); if (last_date == date) { continue; } - Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true); - ERR_CONTINUE(!rel_script.is_valid()); - script->set_source_code(rel_script->get_source_code()); - script->set_last_modified_time(rel_script->get_last_modified_time()); - script->reload(); + Ref<Script> script = edited_res; + if (script != NULL) { + Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true); + ERR_CONTINUE(!rel_script.is_valid()); + script->set_source_code(rel_script->get_source_code()); + script->set_last_modified_time(rel_script->get_last_modified_time()); + script->reload(); + } + + Ref<TextFile> text_file = edited_res; + if (text_file != NULL) { + Error err; + Ref<TextFile> rel_text_file = _load_text_file(text_file->get_path(), &err); + ERR_CONTINUE(!rel_text_file.is_valid()); + text_file->set_text(rel_text_file->get_text()); + text_file->set_last_modified_time(rel_text_file->get_last_modified_time()); + } se->reload_text(); } @@ -725,7 +761,7 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) { continue; } - Ref<Script> script = se->get_edited_script(); + RES script = se->get_edited_resource(); if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) { continue; //internal script, who cares @@ -750,7 +786,7 @@ void ScriptEditor::_live_auto_reload_running_scripts() { debugger->reload_scripts(); } -bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) { +bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) { disk_changed_list->clear(); TreeItem *r = disk_changed_list->create_item(); @@ -765,21 +801,20 @@ bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (se) { - Ref<Script> script = se->get_edited_script(); - - if (p_for_script.is_valid() && p_for_script != script) + RES edited_res = se->get_edited_resource(); + if (edited_res.is_valid() && p_for_script != edited_res) continue; - if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) + if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) continue; //internal script, who cares - uint64_t last_date = script->get_last_modified_time(); - uint64_t date = FileAccess::get_modified_time(script->get_path()); + uint64_t last_date = edited_res->get_last_modified_time(); + uint64_t date = FileAccess::get_modified_time(edited_res->get_path()); if (last_date != date) { TreeItem *ti = disk_changed_list->create_item(r); - ti->set_text(0, script->get_path().get_file()); + ti->set_text(0, edited_res->get_path().get_file()); if (!use_autoreload || se->is_unsaved()) { need_ask = true; @@ -804,6 +839,49 @@ bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) { void ScriptEditor::_file_dialog_action(String p_file) { switch (file_dialog_option) { + case FILE_OPEN: { + + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + if (extensions.find(p_file.get_extension())) { + Ref<Script> scr = ResourceLoader::load(p_file); + if (!scr.is_valid()) { + editor->show_warning(TTR("Error could not load file."), TTR("Error!")); + file_dialog_option = -1; + return; + } + + edit(scr); + file_dialog_option = -1; + return; + } + + Error error; + Ref<TextFile> text_file = _load_text_file(p_file, &error); + if (error != OK) { + editor->show_warning(TTR("Error could not load file."), TTR("Error!")); + } + + if (text_file.is_valid()) { + edit(text_file); + file_dialog_option = -1; + return; + } + } + case FILE_SAVE_AS: { + ScriptEditorBase *current = _get_current_editor(); + + String path = ProjectSettings::get_singleton()->localize_path(p_file); + Error err = _save_text_file(current->get_edited_resource(), path); + + if (err != OK) { + editor->show_accept(TTR("Error saving file!"), TTR("OK")); + return; + } + + ((Resource *)current->get_edited_resource().ptr())->set_path(path); + _update_script_names(); + } break; case THEME_SAVE_AS: { if (!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) { editor->show_warning(TTR("Error while saving theme"), TTR("Error saving")); @@ -823,7 +901,8 @@ Ref<Script> ScriptEditor::_get_current_script() { ScriptEditorBase *current = _get_current_editor(); if (current) { - return current->get_edited_script(); + Ref<Script> script = current->get_edited_resource(); + return script != NULL ? script : NULL; } else { return NULL; } @@ -848,8 +927,19 @@ void ScriptEditor::_menu_option(int p_option) { script_create_dialog->popup_centered(Size2(300, 300) * EDSCALE); } break; case FILE_OPEN: { + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog_option = FILE_OPEN; - editor->open_resource("Script"); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + file_dialog->clear_filters(); + for (int i = 0; i < extensions.size(); i++) { + file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + } + + file_dialog->popup_centered_ratio(); + file_dialog->set_title(TTR("Open File")); return; } break; case FILE_SAVE_ALL: { @@ -929,7 +1019,14 @@ void ScriptEditor::_menu_option(int p_option) { current->convert_indent_to_tabs(); } } - editor->save_resource(current->get_edited_script()); + + Ref<TextFile> text_file = current->get_edited_resource(); + if (text_file != NULL) { + current->apply_code(); + _save_text_file(text_file, text_file->get_path()); + break; + } + editor->save_resource(current->get_edited_resource()); } break; case FILE_SAVE_AS: { @@ -943,8 +1040,25 @@ void ScriptEditor::_menu_option(int p_option) { current->convert_indent_to_tabs(); } } - editor->push_item(Object::cast_to<Object>(current->get_edited_script().ptr())); - editor->save_resource_as(current->get_edited_script()); + + Ref<TextFile> text_file = current->get_edited_resource(); + if (text_file != NULL) { + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog_option = FILE_SAVE_AS; + + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + file_dialog->clear_filters(); + file_dialog->set_current_dir(text_file->get_path().get_base_dir()); + file_dialog->set_current_file(text_file->get_path().get_file()); + file_dialog->popup_centered_ratio(); + file_dialog->set_title(TTR("Save File As...")); + break; + } + + editor->push_item(Object::cast_to<Object>(current->get_edited_resource().ptr())); + editor->save_resource_as(current->get_edited_resource()); } break; @@ -956,8 +1070,8 @@ void ScriptEditor::_menu_option(int p_option) { } break; case FILE_RUN: { - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) { + Ref<Script> scr = current->get_edited_resource(); + if (scr == NULL || scr.is_null()) { EditorNode::get_singleton()->show_warning("Can't obtain the script for running"); break; } @@ -1000,8 +1114,7 @@ void ScriptEditor::_menu_option(int p_option) { _copy_script_path(); } break; case SHOW_IN_FILE_SYSTEM: { - ScriptEditorBase *se = _get_current_editor(); - Ref<Script> script = se->get_edited_script(); + RES script = current->get_edited_resource(); FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock(); file_system_dock->navigate_to_path(script->get_path()); // Ensure that the FileSystem dock is visible. @@ -1259,8 +1372,8 @@ void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) { if (se) { - Ref<Script> script = se->get_edited_script(); - if (!script.is_valid()) + Ref<Script> script = se->get_edited_resource(); + if (script == NULL || !script.is_valid()) continue; if (script->get_path().find("::") != -1 && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed @@ -1307,9 +1420,13 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { if (!se) continue; + Ref<Script> script = se->get_edited_resource(); + if (script == NULL) { + continue; + } + List<int> bpoints; se->get_breakpoints(&bpoints); - Ref<Script> script = se->get_edited_script(); String base = script->get_path(); ERR_CONTINUE(base.begins_with("local://") || base == ""); @@ -1452,7 +1569,7 @@ void ScriptEditor::_update_members_overview() { members_overview->set_item_metadata(i, functions[i].get_slice(":", 1).to_int() - 1); } - String path = se->get_edited_script()->get_path(); + String path = se->get_edited_resource()->get_path(); bool built_in = !path.is_resource_file(); String name = built_in ? path.get_file() : se->get_name(); filename->set_text(name); @@ -1570,7 +1687,7 @@ void ScriptEditor::_update_script_names() { if (se) { Ref<Texture> icon = se->get_icon(); - String path = se->get_edited_script()->get_path(); + String path = se->get_edited_resource()->get_path(); bool built_in = !path.is_resource_file(); String name = built_in ? path.get_file() : se->get_name(); @@ -1579,7 +1696,7 @@ void ScriptEditor::_update_script_names() { sd.name = name; sd.tooltip = path; sd.index = i; - sd.used = used.has(se->get_edited_script()); + sd.used = used.has(se->get_edited_resource()); sd.category = 0; sd.ref = se; @@ -1681,11 +1798,65 @@ void ScriptEditor::_update_script_names() { _update_script_colors(); } -bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool p_grab_focus) { +Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) { + if (r_error) { + *r_error = ERR_FILE_CANT_OPEN; + } + + String local_path = ProjectSettings::get_singleton()->localize_path(p_path); + String path = ResourceLoader::path_remap(local_path); + + TextFile *text_file = memnew(TextFile); + Ref<TextFile> text_res(text_file); + Error err = text_file->load_text(path); + + if (err != OK) { + ERR_FAIL_COND_V(err != OK, RES()); + } + + text_file->set_file_path(local_path); + text_file->set_path(local_path, true); + + if (r_error) { + *r_error = OK; + } + + return text_res; +} + +Error ScriptEditor::_save_text_file(Ref<TextFile> p_text_file, const String &p_path) { + Ref<TextFile> sqscr = p_text_file; + ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); + + String source = sqscr->get_text(); + + Error err; + FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + + if (err) { + + ERR_FAIL_COND_V(err, err); + } + + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + memdelete(file); + return ERR_CANT_CREATE; + } + file->close(); + memdelete(file); + + _res_saved_callback(sqscr); + return OK; +} + +bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus) { - if (p_script.is_null()) + if (p_resource.is_null()) return false; + Ref<Script> script = p_resource; + // refuse to open built-in if scene is not loaded // see if already has it @@ -1694,17 +1865,17 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool const bool should_open = open_dominant || !EditorNode::get_singleton()->is_changing_scene(); - if (p_script->get_language()->overrides_external_editor()) { + if (script != NULL && script->get_language()->overrides_external_editor()) { if (should_open) { - Error err = p_script->get_language()->open_in_external_editor(p_script, p_line >= 0 ? p_line : 0, p_col); + Error err = script->get_language()->open_in_external_editor(script, p_line >= 0 ? p_line : 0, p_col); if (err != OK) ERR_PRINT("Couldn't open script in the overridden external text editor"); } return false; } - if ((debugger->get_dump_stack_script() != p_script || debugger->get_debug_with_external_editor()) && - p_script->get_path().is_resource_file() && + if ((debugger->get_dump_stack_script() != p_resource || debugger->get_debug_with_external_editor()) && + p_resource->get_path().is_resource_file() && bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) { String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path"); @@ -1714,7 +1885,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool if (flags.size()) { String project_path = ProjectSettings::get_singleton()->get_resource_path(); - String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); + String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path()); flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0)); flags = flags.replacen("{col}", itos(p_col)); @@ -1762,7 +1933,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool if (!se) continue; - if (se->get_edited_script() == p_script) { + if (se->get_edited_resource() == p_resource) { if (should_open) { if (tab_container->get_current_tab() != i) { @@ -1784,7 +1955,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool ScriptEditorBase *se; for (int i = script_editor_func_count - 1; i >= 0; i--) { - se = script_editor_funcs[i](p_script); + se = script_editor_funcs[i](p_resource); if (se) break; } @@ -1795,9 +1966,9 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i](); se->add_syntax_highlighter(highlighter); - if (!highlighter_set) { + if (script != NULL && !highlighter_set) { List<String> languages = highlighter->get_supported_languages(); - if (languages.find(p_script->get_language()->get_name())) { + if (languages.find(script->get_language()->get_name())) { se->set_syntax_highlighter(highlighter); highlighter_set = true; } @@ -1805,7 +1976,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool } tab_container->add_child(se); - se->set_edited_script(p_script); + se->set_edited_resource(p_resource); se->set_tooltip_request_func("_get_debug_tooltip", this); if (se->get_edit_menu()) { se->get_edit_menu()->hide(); @@ -1829,14 +2000,14 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool //test for modification, maybe the script was not edited but was loaded - _test_script_times_on_disk(p_script); - _update_modified_scripts_for_external_editor(p_script); + _test_script_times_on_disk(p_resource); + _update_modified_scripts_for_external_editor(p_resource); if (p_line >= 0) se->goto_line(p_line - 1); - notify_script_changed(p_script); - _add_recent_script(p_script->get_path()); + notify_script_changed(p_resource); + _add_recent_script(p_resource->get_path()); return true; } @@ -1863,12 +2034,19 @@ void ScriptEditor::save_all_scripts() { if (!se->is_unsaved()) continue; - Ref<Script> script = se->get_edited_script(); - if (script.is_valid()) + RES edited_res = se->get_edited_resource(); + if (edited_res.is_valid()) { se->apply_code(); + } - if (script->get_path() != "" && script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) - editor->save_resource(script); //external script, save it + if (edited_res->get_path() != "" && edited_res->get_path().find("local://") == -1 && edited_res->get_path().find("::") == -1) { + Ref<TextFile> text_file = edited_res; + if (text_file != NULL) { + _save_text_file(text_file, text_file->get_path()); + continue; + } + editor->save_resource(edited_res); //external script, save it + } } _update_script_names(); @@ -1938,7 +2116,7 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (!se) continue; - if (se->get_edited_script() != script) + if (se->get_edited_resource() != script) continue; se->add_callback(p_function, p_args); @@ -2228,9 +2406,12 @@ void ScriptEditor::_make_script_list_context_menu() { context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS); context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/copy_path"), FILE_COPY_PATH); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/show_in_file_system"), SHOW_IN_FILE_SYSTEM); - Ref<Script> scr = se->get_edited_script(); + } + + Ref<Script> scr = se->get_edited_resource(); + if (scr != NULL) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); if (!scr.is_null() && scr->is_tool()) { context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN); @@ -2239,8 +2420,6 @@ void ScriptEditor::_make_script_list_context_menu() { context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); } - EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(selected)); - context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_up"), WINDOW_MOVE_UP); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_down"), WINDOW_MOVE_DOWN); @@ -2268,14 +2447,28 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { restoring_layout = true; + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + for (int i = 0; i < scripts.size(); i++) { String path = scripts[i]; if (!FileAccess::exists(path)) continue; - Ref<Script> scr = ResourceLoader::load(path); - if (scr.is_valid()) { - edit(scr); + + if (extensions.find(path.get_extension())) { + Ref<Script> scr = ResourceLoader::load(path); + if (scr.is_valid()) { + edit(scr); + continue; + } + } + + Error error; + Ref<TextFile> text_file = _load_text_file(path, &error); + if (error == OK && text_file.is_valid()) { + edit(text_file); + continue; } } @@ -2311,7 +2504,7 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (se) { - String path = se->get_edited_script()->get_path(); + String path = se->get_edited_resource()->get_path(); if (!path.is_resource_file()) continue; @@ -2436,7 +2629,10 @@ void ScriptEditor::_update_history_pos(int p_new_pos) { Object::cast_to<ScriptEditorBase>(n)->set_edit_state(history[history_pos].state); Object::cast_to<ScriptEditorBase>(n)->ensure_focus(); - notify_script_changed(Object::cast_to<ScriptEditorBase>(n)->get_edited_script()); + Ref<Script> script = Object::cast_to<ScriptEditorBase>(n)->get_edited_resource(); + if (script != NULL) { + notify_script_changed(script); + } } if (Object::cast_to<EditorHelp>(n)) { @@ -2473,7 +2669,11 @@ Vector<Ref<Script> > ScriptEditor::get_open_scripts() const { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (!se) continue; - out_scripts.push_back(se->get_edited_script()); + + Ref<Script> script = se->get_edited_resource(); + if (script != NULL) { + out_scripts.push_back(script); + } } return out_scripts; @@ -2519,6 +2719,14 @@ void ScriptEditor::_open_script_request(const String &p_path) { Ref<Script> script = ResourceLoader::load(p_path); if (script.is_valid()) { script_editor->edit(script, false); + return; + } + + Error err; + Ref<TextFile> text_file = script_editor->_load_text_file(p_path, &err); + if (text_file.is_valid()) { + script_editor->edit(text_file, false); + return; } } @@ -2552,7 +2760,7 @@ void ScriptEditor::_on_find_in_files_requested(String text) { void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) { - Ref<Resource> res = ResourceLoader::load(fpath); + RES res = ResourceLoader::load(fpath); edit(res); ScriptEditorBase *seb = _get_current_editor(); @@ -2968,14 +3176,21 @@ ScriptEditor::~ScriptEditor() { void ScriptEditorPlugin::edit(Object *p_object) { - if (!Object::cast_to<Script>(p_object)) - return; + if (Object::cast_to<Script>(p_object)) { + script_editor->edit(Object::cast_to<Script>(p_object)); + } - script_editor->edit(Object::cast_to<Script>(p_object)); + if (Object::cast_to<TextFile>(p_object)) { + script_editor->edit(Object::cast_to<TextFile>(p_object)); + } } bool ScriptEditorPlugin::handles(Object *p_object) const { + if (Object::cast_to<TextFile>(p_object)) { + return true; + } + if (Object::cast_to<Script>(p_object)) { bool valid = _can_open_in_editor(Object::cast_to<Script>(p_object)); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index ad12add53f..186c80a5f9 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -43,6 +43,7 @@ #include "scene/gui/tool_button.h" #include "scene/gui/tree.h" #include "scene/main/timer.h" +#include "scene/resources/text_file.h" #include "script_language.h" class ScriptEditorQuickOpen : public ConfirmationDialog { @@ -74,7 +75,7 @@ class ScriptEditorDebugger; class ScriptEditorBase : public VBoxContainer { - GDCLASS(ScriptEditorBase, VBoxContainer); + GDCLASS(ScriptEditorBase, VBoxContainer) protected: static void _bind_methods(); @@ -84,9 +85,9 @@ public: virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0; virtual void apply_code() = 0; - virtual Ref<Script> get_edited_script() const = 0; + virtual RES get_edited_resource() const = 0; virtual Vector<String> get_functions() = 0; - virtual void set_edited_script(const Ref<Script> &p_script) = 0; + virtual void set_edited_resource(const RES &p_res) = 0; virtual void reload_text() = 0; virtual String get_name() = 0; virtual Ref<Texture> get_icon() = 0; @@ -99,7 +100,7 @@ public: virtual void convert_indent_to_tabs() = 0; virtual void ensure_focus() = 0; virtual void tag_saved_version() = 0; - virtual void reload(bool p_soft) = 0; + virtual void reload(bool p_soft) {} virtual void get_breakpoints(List<int> *p_breakpoints) = 0; virtual void add_callback(const String &p_function, PoolStringArray p_args) = 0; virtual void update_settings() = 0; @@ -116,7 +117,7 @@ public: }; typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)(); -typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script); +typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const RES &p_resource); class EditorScriptCodeCompletionCache; class FindInFilesDialog; @@ -268,7 +269,7 @@ class ScriptEditor : public PanelContainer { void _resave_scripts(const String &p_str); void _reload_scripts(); - bool _test_script_times_on_disk(Ref<Script> p_for_script = Ref<Script>()); + bool _test_script_times_on_disk(RES p_for_script = Ref<Resource>()); void _add_recent_script(String p_path); void _update_recent_scripts(); @@ -378,6 +379,9 @@ class ScriptEditor : public PanelContainer { Ref<Script> _get_current_script(); Array _get_open_scripts() const; + Ref<TextFile> _load_text_file(const String &p_path, Error *r_error); + Error _save_text_file(Ref<TextFile> p_text_file, const String &p_path); + void _on_find_in_files_requested(String text); void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end); void _start_find_in_files(bool with_replace); @@ -400,8 +404,8 @@ public: void ensure_select_current(); - _FORCE_INLINE_ bool edit(const Ref<Script> &p_script, bool p_grab_focus = true) { return edit(p_script, -1, 0, p_grab_focus); } - bool edit(const Ref<Script> &p_script, int p_line, int p_col, bool p_grab_focus = true); + _FORCE_INLINE_ bool edit(const RES &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); } + bool edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus = true); void get_breakpoints(List<String> *p_breakpoints); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 2263d782d9..165d7e32b9 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -66,11 +66,24 @@ void ScriptTextEditor::apply_code() { _update_member_keywords(); } -Ref<Script> ScriptTextEditor::get_edited_script() const { - +RES ScriptTextEditor::get_edited_resource() const { return script; } +void ScriptTextEditor::set_edited_resource(const RES &p_res) { + ERR_FAIL_COND(!script.is_null()); + + script = p_res; + _set_theme_for_script(); + + code_editor->get_text_edit()->set_text(script->get_source_code()); + code_editor->get_text_edit()->clear_undo_history(); + code_editor->get_text_edit()->tag_saved_version(); + + emit_signal("name_changed"); + code_editor->update_line_and_column(); +} + void ScriptTextEditor::_update_member_keywords() { member_keywords.clear(); code_editor->get_text_edit()->clear_member_keywords(); @@ -190,6 +203,7 @@ void ScriptTextEditor::_set_theme_for_script() { List<String> keywords; script->get_language()->get_reserved_words(&keywords); + for (List<String>::Element *E = keywords.front(); E; E = E->next()) { text_edit->add_keyword_color(E->get(), colors_cache.keyword_color); @@ -251,7 +265,6 @@ void ScriptTextEditor::_set_theme_for_script() { //colorize strings List<String> strings; script->get_language()->get_string_delimiters(&strings); - for (List<String>::Element *E = strings.front(); E; E = E->next()) { String string = E->get(); @@ -326,197 +339,32 @@ bool ScriptTextEditor::is_unsaved() { Variant ScriptTextEditor::get_edit_state() { - Dictionary state; + return code_editor->get_edit_state(); +} - state["scroll_position"] = code_editor->get_text_edit()->get_v_scroll(); - state["column"] = code_editor->get_text_edit()->cursor_get_column(); - state["row"] = code_editor->get_text_edit()->cursor_get_line(); +void ScriptTextEditor::set_edit_state(const Variant &p_state) { - return state; + code_editor->set_edit_state(p_state); } -void ScriptTextEditor::_convert_case(CaseStyle p_case) { - TextEdit *te = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) { - return; - } - - if (te->is_selection_active()) { - te->begin_complex_operation(); - - int begin = te->get_selection_from_line(); - int end = te->get_selection_to_line(); - int begin_col = te->get_selection_from_column(); - int end_col = te->get_selection_to_column(); - - for (int i = begin; i <= end; i++) { - int len = te->get_line(i).length(); - if (i == end) - len -= len - end_col; - if (i == begin) - len -= begin_col; - String new_line = te->get_line(i).substr(i == begin ? begin_col : 0, len); - - switch (p_case) { - case UPPER: { - new_line = new_line.to_upper(); - } break; - case LOWER: { - new_line = new_line.to_lower(); - } break; - case CAPITALIZE: { - new_line = new_line.capitalize(); - } break; - } +void ScriptTextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { - if (i == begin) { - new_line = te->get_line(i).left(begin_col) + new_line; - } - if (i == end) { - new_line = new_line + te->get_line(i).right(end_col); - } - te->set_line(i, new_line); - } - te->end_complex_operation(); - } + code_editor->convert_case(p_case); } void ScriptTextEditor::trim_trailing_whitespace() { - TextEdit *tx = code_editor->get_text_edit(); - - bool trimed_whitespace = false; - for (int i = 0; i < tx->get_line_count(); i++) { - String line = tx->get_line(i); - if (line.ends_with(" ") || line.ends_with("\t")) { - - if (!trimed_whitespace) { - tx->begin_complex_operation(); - trimed_whitespace = true; - } - - int end = 0; - for (int j = line.length() - 1; j > -1; j--) { - if (line[j] != ' ' && line[j] != '\t') { - end = j + 1; - break; - } - } - tx->set_line(i, line.substr(0, end)); - } - } - if (trimed_whitespace) { - tx->end_complex_operation(); - tx->update(); - } + code_editor->trim_trailing_whitespace(); } void ScriptTextEditor::convert_indent_to_spaces() { - TextEdit *tx = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) { - return; - } - - int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); - String indent = ""; - - for (int i = 0; i < indent_size; i++) { - indent += " "; - } - - int cursor_line = tx->cursor_get_line(); - int cursor_column = tx->cursor_get_column(); - - bool changed_indentation = false; - for (int i = 0; i < tx->get_line_count(); i++) { - String line = tx->get_line(i); - - if (line.length() <= 0) { - continue; - } - - int j = 0; - while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { - if (line[j] == '\t') { - if (!changed_indentation) { - tx->begin_complex_operation(); - changed_indentation = true; - } - if (cursor_line == i && cursor_column > j) { - cursor_column += indent_size - 1; - } - line = line.left(j) + indent + line.right(j + 1); - } - j++; - } - if (changed_indentation) { - tx->set_line(i, line); - } - } - if (changed_indentation) { - tx->cursor_set_column(cursor_column); - tx->end_complex_operation(); - tx->update(); - } + code_editor->convert_indent_to_spaces(); } void ScriptTextEditor::convert_indent_to_tabs() { - TextEdit *tx = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); - - if (scr.is_null()) { - return; - } - - int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); - indent_size -= 1; - - int cursor_line = tx->cursor_get_line(); - int cursor_column = tx->cursor_get_column(); - - bool changed_indentation = false; - for (int i = 0; i < tx->get_line_count(); i++) { - String line = tx->get_line(i); - - if (line.length() <= 0) { - continue; - } - - int j = 0; - int space_count = -1; - while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { - if (line[j] != '\t') { - space_count++; - if (space_count == indent_size) { - if (!changed_indentation) { - tx->begin_complex_operation(); - changed_indentation = true; - } - if (cursor_line == i && cursor_column > j) { - cursor_column -= indent_size; - } - line = line.left(j - indent_size) + "\t" + line.right(j + 1); - j = 0; - space_count = -1; - } - } else { - space_count = -1; - } - j++; - } - if (changed_indentation) { - tx->set_line(i, line); - } - } - if (changed_indentation) { - tx->cursor_set_column(cursor_column); - tx->end_complex_operation(); - tx->update(); - } + code_editor->convert_indent_to_tabs(); } void ScriptTextEditor::tag_saved_version() { @@ -525,31 +373,17 @@ void ScriptTextEditor::tag_saved_version() { } void ScriptTextEditor::goto_line(int p_line, bool p_with_error) { - TextEdit *tx = code_editor->get_text_edit(); - tx->deselect(); - tx->unfold_line(p_line); - tx->call_deferred("cursor_set_line", p_line); -} -void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { - TextEdit *tx = code_editor->get_text_edit(); - tx->unfold_line(p_line); - tx->call_deferred("cursor_set_line", p_line); - tx->call_deferred("cursor_set_column", p_begin); - tx->select(p_line, p_begin, p_line, p_end); + code_editor->goto_line(p_line); } -void ScriptTextEditor::ensure_focus() { +void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { - code_editor->get_text_edit()->grab_focus(); + code_editor->goto_line_selection(p_line, p_begin, p_end); } -void ScriptTextEditor::set_edit_state(const Variant &p_state) { +void ScriptTextEditor::ensure_focus() { - Dictionary state = p_state; - code_editor->get_text_edit()->cursor_set_column(state["column"]); - code_editor->get_text_edit()->cursor_set_line(state["row"]); - code_editor->get_text_edit()->set_v_scroll(state["scroll_position"]); code_editor->get_text_edit()->grab_focus(); } @@ -578,22 +412,6 @@ Ref<Texture> ScriptTextEditor::get_icon() { return Ref<Texture>(); } -void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) { - - ERR_FAIL_COND(!script.is_null()); - - script = p_script; - _set_theme_for_script(); - - code_editor->get_text_edit()->set_text(script->get_source_code()); - code_editor->get_text_edit()->clear_undo_history(); - code_editor->get_text_edit()->tag_saved_version(); - - emit_signal("name_changed"); - code_editor->update_line_and_column(); - call_deferred("_validate_script"); -} - void ScriptTextEditor::_validate_script() { String errortxt; @@ -878,98 +696,15 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_MOVE_LINE_UP: { - Ref<Script> scr = script; - if (scr.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = from_line; i <= to_line; i++) { - int line_id = i; - int next_id = i - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_up = from_line > 0 ? from_line - 1 : from_line; - int to_line_up = to_line > 0 ? to_line - 1 : to_line; - tx->select(from_line_up, from_col, to_line_up, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); + code_editor->move_lines_up(); } break; case EDIT_MOVE_LINE_DOWN: { - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = to_line; i >= from_line; i--) { - int line_id = i; - int next_id = i + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_down = from_line < tx->get_line_count() ? from_line + 1 : from_line; - int to_line_down = to_line < tx->get_line_count() ? to_line + 1 : to_line; - tx->select(from_line_down, from_col, to_line_down, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); - + code_editor->move_lines_down(); } break; case EDIT_INDENT_LEFT: { - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -977,7 +712,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_INDENT_RIGHT: { - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -985,72 +720,11 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_DELETE_LINE: { - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) - return; - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int to_line = tx->get_selection_to_line(); - int from_line = tx->get_selection_from_line(); - int count = Math::abs(to_line - from_line) + 1; - while (count) { - tx->set_line(tx->cursor_get_line(), ""); - tx->backspace_at_cursor(); - count--; - if (count) - tx->unfold_line(from_line); - } - tx->cursor_set_line(from_line - 1); - tx->deselect(); - } else { - int line = tx->cursor_get_line(); - tx->set_line(tx->cursor_get_line(), ""); - tx->backspace_at_cursor(); - tx->unfold_line(line); - tx->cursor_set_line(line); - } - tx->end_complex_operation(); + code_editor->delete_lines(); } break; case EDIT_CLONE_DOWN: { - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) - return; - - int from_line = tx->cursor_get_line(); - int to_line = tx->cursor_get_line(); - int column = tx->cursor_get_column(); - - if (tx->is_selection_active()) { - from_line = tx->get_selection_from_line(); - to_line = tx->get_selection_to_line(); - column = tx->cursor_get_column(); - } - int next_line = to_line + 1; - - if (to_line >= tx->get_line_count() - 1) { - tx->set_line(to_line, tx->get_line(to_line) + "\n"); - } - - tx->begin_complex_operation(); - for (int i = from_line; i <= to_line; i++) { - - tx->unfold_line(i); - if (i >= tx->get_line_count() - 1) { - tx->set_line(i, tx->get_line(i) + "\n"); - } - String line_clone = tx->get_line(i); - tx->insert_at(line_clone, next_line); - next_line++; - } - - tx->cursor_set_column(column); - if (tx->is_selection_active()) { - tx->select(to_line + 1, tx->get_selection_from_column(), next_line - 1, tx->get_selection_to_column()); - } - - tx->end_complex_operation(); - tx->update(); + code_editor->code_lines_down(); } break; case EDIT_TOGGLE_FOLD_LINE: { @@ -1069,7 +743,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_TOGGLE_COMMENT: { - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -1137,7 +811,7 @@ void ScriptTextEditor::_edit_option(int p_op) { case EDIT_AUTO_INDENT: { String text = tx->get_text(); - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -1180,15 +854,15 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_TO_UPPERCASE: { - _convert_case(UPPER); + _convert_case(CodeTextEditor::UPPER); } break; case EDIT_TO_LOWERCASE: { - _convert_case(LOWER); + _convert_case(CodeTextEditor::LOWER); } break; case EDIT_CAPITALIZE: { - _convert_case(CAPITALIZE); + _convert_case(CodeTextEditor::CAPITALIZE); } break; case SEARCH_FIND: { @@ -1228,7 +902,7 @@ void ScriptTextEditor::_edit_option(int p_op) { int line = tx->cursor_get_line(); bool dobreak = !tx->is_line_set_as_breakpoint(line); tx->set_line_as_breakpoint(line, dobreak); - ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak); + ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); } break; case DEBUG_REMOVE_ALL_BREAKPOINTS: { @@ -1239,7 +913,7 @@ void ScriptTextEditor::_edit_option(int p_op) { int line = E->get(); bool dobreak = !tx->is_line_set_as_breakpoint(line); tx->set_line_as_breakpoint(line, dobreak); - ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak); + ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); } } case DEBUG_GOTO_NEXT_BREAKPOINT: { @@ -1359,7 +1033,7 @@ void ScriptTextEditor::clear_edit_menu() { void ScriptTextEditor::reload(bool p_soft) { TextEdit *te = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; scr->set_source_code(te->get_text()); @@ -1743,9 +1417,12 @@ ScriptTextEditor::ScriptTextEditor() { code_editor->get_text_edit()->set_drag_forwarding(this); } -static ScriptEditorBase *create_editor(const Ref<Script> &p_script) { +static ScriptEditorBase *create_editor(const RES &p_resource) { - return memnew(ScriptTextEditor); + if (Object::cast_to<Script>(*p_resource)) { + return memnew(ScriptTextEditor); + } + return NULL; } void ScriptTextEditor::register_editor() { diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index a415f478e8..f0b00a9117 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -138,12 +138,7 @@ protected: void _goto_line(int p_line) { goto_line(p_line); } void _lookup_symbol(const String &p_symbol, int p_row, int p_column); - enum CaseStyle { - UPPER, - LOWER, - CAPITALIZE, - }; - void _convert_case(CaseStyle p_case); + void _convert_case(CodeTextEditor::CaseStyle p_case); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; @@ -154,14 +149,13 @@ public: virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); virtual void apply_code(); - virtual Ref<Script> get_edited_script() const; + virtual RES get_edited_resource() const; + virtual void set_edited_resource(const RES &p_res); virtual Vector<String> get_functions(); - virtual void set_edited_script(const Ref<Script> &p_script); virtual void reload_text(); virtual String get_name(); virtual Ref<Texture> get_icon(); virtual bool is_unsaved(); - virtual Variant get_edit_state(); virtual void set_edit_state(const Variant &p_state); virtual void ensure_focus(); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 4b7f27c0c1..7650cd6ae7 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -258,84 +258,10 @@ void ShaderEditor::_menu_option(int p_option) { shader_editor->get_text_edit()->select_all(); } break; case EDIT_MOVE_LINE_UP: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = from_line; i <= to_line; i++) { - int line_id = i; - int next_id = i - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_up = from_line > 0 ? from_line - 1 : from_line; - int to_line_up = to_line > 0 ? to_line - 1 : to_line; - tx->select(from_line_up, from_col, to_line_up, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); - + shader_editor->move_lines_up(); } break; case EDIT_MOVE_LINE_DOWN: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = to_line; i >= from_line; i--) { - int line_id = i; - int next_id = i + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_down = from_line < tx->get_line_count() ? from_line + 1 : from_line; - int to_line_down = to_line < tx->get_line_count() ? to_line + 1 : to_line; - tx->select(from_line_down, from_col, to_line_down, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); - + shader_editor->move_lines_down(); } break; case EDIT_INDENT_LEFT: { @@ -356,55 +282,10 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_DELETE_LINE: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - tx->begin_complex_operation(); - int line = tx->cursor_get_line(); - tx->set_line(tx->cursor_get_line(), ""); - tx->backspace_at_cursor(); - tx->cursor_set_line(line); - tx->end_complex_operation(); - + shader_editor->delete_lines(); } break; case EDIT_CLONE_DOWN: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - int from_line = tx->cursor_get_line(); - int to_line = tx->cursor_get_line(); - int column = tx->cursor_get_column(); - - if (tx->is_selection_active()) { - from_line = tx->get_selection_from_line(); - to_line = tx->get_selection_to_line(); - column = tx->cursor_get_column(); - } - int next_line = to_line + 1; - - tx->begin_complex_operation(); - for (int i = from_line; i <= to_line; i++) { - - if (i >= tx->get_line_count() - 1) { - tx->set_line(i, tx->get_line(i) + "\n"); - } - String line_clone = tx->get_line(i); - tx->insert_at(line_clone, next_line); - next_line++; - } - - tx->cursor_set_column(column); - if (tx->is_selection_active()) { - tx->select(to_line + 1, tx->get_selection_from_column(), next_line - 1, tx->get_selection_to_column()); - } - - tx->end_complex_operation(); - tx->update(); - + shader_editor->code_lines_down(); } break; case EDIT_TOGGLE_COMMENT: { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 37b8562e96..f0c874a150 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -4641,9 +4641,7 @@ void SpatialEditor::_init_grid() { Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); Color line_color = secondary_grid_color; - if (j == 0) { - continue; - } else if (j % primary_grid_steps == 0) { + if (j % primary_grid_steps == 0) { line_color = primary_grid_color; } diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp new file mode 100644 index 0000000000..16c25f3074 --- /dev/null +++ b/editor/plugins/text_editor.cpp @@ -0,0 +1,607 @@ +/*************************************************************************/ +/* text_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "text_editor.h" + +void TextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + highlighters[p_highlighter->get_name()] = p_highlighter; + highlighter_menu->add_radio_check_item(p_highlighter->get_name()); +} + +void TextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + TextEdit *te = code_editor->get_text_edit(); + te->_set_syntax_highlighting(p_highlighter); + if (p_highlighter != NULL) { + highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->get_name()), true); + } else { + highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text("Standard"), true); + } + + // little work around. GDScript highlighter goes through text_edit for colours, + // so to remove all colours we need to set and unset them here. + if (p_highlighter == NULL) { // standard + TextEdit *text_edit = code_editor->get_text_edit(); + text_edit->add_color_override("number_color", colors_cache.font_color); + text_edit->add_color_override("function_color", colors_cache.font_color); + text_edit->add_color_override("number_color", colors_cache.font_color); + text_edit->add_color_override("member_variable_color", colors_cache.font_color); + } else { + _load_theme_settings(); + } +} + +void TextEditor::_change_syntax_highlighter(int p_idx) { + Map<String, SyntaxHighlighter *>::Element *el = highlighters.front(); + while (el != NULL) { + highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(el->key()), false); + el = el->next(); + } + set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); +} + +void TextEditor::_load_theme_settings() { + + TextEdit *text_edit = code_editor->get_text_edit(); + text_edit->clear_colors(); + + Color background_color = EDITOR_GET("text_editor/highlighting/background_color"); + Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color"); + Color completion_selected_color = EDITOR_GET("text_editor/highlighting/completion_selected_color"); + Color completion_existing_color = EDITOR_GET("text_editor/highlighting/completion_existing_color"); + Color completion_scroll_color = EDITOR_GET("text_editor/highlighting/completion_scroll_color"); + Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color"); + Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); + Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color"); + Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color"); + Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color"); + Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color"); + Color selection_color = EDITOR_GET("text_editor/highlighting/selection_color"); + Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); + Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); + Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); + Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); + Color number_color = EDITOR_GET("text_editor/highlighting/number_color"); + Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); + Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); + Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); + Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); + Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); + Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color"); + Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); + Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color"); + Color type_color = EDITOR_GET("text_editor/highlighting/engine_type_color"); + Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color"); + Color string_color = EDITOR_GET("text_editor/highlighting/string_color"); + + text_edit->add_color_override("background_color", background_color); + text_edit->add_color_override("completion_background_color", completion_background_color); + text_edit->add_color_override("completion_selected_color", completion_selected_color); + text_edit->add_color_override("completion_existing_color", completion_existing_color); + text_edit->add_color_override("completion_scroll_color", completion_scroll_color); + text_edit->add_color_override("completion_font_color", completion_font_color); + text_edit->add_color_override("font_color", text_color); + text_edit->add_color_override("line_number_color", line_number_color); + text_edit->add_color_override("caret_color", caret_color); + text_edit->add_color_override("caret_background_color", caret_background_color); + text_edit->add_color_override("font_selected_color", text_selected_color); + text_edit->add_color_override("selection_color", selection_color); + text_edit->add_color_override("brace_mismatch_color", brace_mismatch_color); + text_edit->add_color_override("current_line_color", current_line_color); + text_edit->add_color_override("line_length_guideline_color", line_length_guideline_color); + text_edit->add_color_override("word_highlighted_color", word_highlighted_color); + text_edit->add_color_override("number_color", number_color); + text_edit->add_color_override("function_color", function_color); + text_edit->add_color_override("member_variable_color", member_variable_color); + text_edit->add_color_override("breakpoint_color", breakpoint_color); + text_edit->add_color_override("mark_color", mark_color); + text_edit->add_color_override("code_folding_color", code_folding_color); + text_edit->add_color_override("search_result_color", search_result_color); + text_edit->add_color_override("search_result_border_color", search_result_border_color); + text_edit->add_color_override("symbol_color", symbol_color); + + text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 4)); + + colors_cache.font_color = text_color; + colors_cache.symbol_color = symbol_color; + colors_cache.keyword_color = keyword_color; + colors_cache.basetype_color = basetype_color; + colors_cache.type_color = type_color; + colors_cache.comment_color = comment_color; + colors_cache.string_color = string_color; +} + +String TextEditor::get_name() { + String name; + + if (text_file->get_path().find("local://") == -1 && text_file->get_path().find("::") == -1) { + name = text_file->get_path().get_file(); + if (is_unsaved()) { + name += "(*)"; + } + } else if (text_file->get_name() != "") { + name = text_file->get_name(); + } else { + name = text_file->get_class() + "(" + itos(text_file->get_instance_id()) + ")"; + } + + return name; +} + +Ref<Texture> TextEditor::get_icon() { + + if (get_parent_control() && get_parent_control()->has_icon(text_file->get_class(), "EditorIcons")) { + return get_parent_control()->get_icon(text_file->get_class(), "EditorIcons"); + } + return Ref<Texture>(); +} + +RES TextEditor::get_edited_resource() const { + return text_file; +} + +void TextEditor::set_edited_resource(const RES &p_res) { + ERR_FAIL_COND(!text_file.is_null()); + + text_file = p_res; + + code_editor->get_text_edit()->set_text(text_file->get_text()); + code_editor->get_text_edit()->clear_undo_history(); + code_editor->get_text_edit()->tag_saved_version(); + + emit_signal("name_changed"); + code_editor->update_line_and_column(); +} + +void TextEditor::add_callback(const String &p_function, PoolStringArray p_args) { +} + +void TextEditor::set_debugger_active(bool p_active) { +} + +void TextEditor::get_breakpoints(List<int> *p_breakpoints) { +} + +void TextEditor::reload_text() { + + ERR_FAIL_COND(text_file.is_null()); + + TextEdit *te = code_editor->get_text_edit(); + int column = te->cursor_get_column(); + int row = te->cursor_get_line(); + int h = te->get_h_scroll(); + int v = te->get_v_scroll(); + + te->set_text(text_file->get_text()); + te->clear_undo_history(); + te->cursor_set_line(row); + te->cursor_set_column(column); + te->set_h_scroll(h); + te->set_v_scroll(v); + + te->tag_saved_version(); + + code_editor->update_line_and_column(); +} + +void TextEditor::_validate_script() { + emit_signal("name_changed"); + emit_signal("edited_script_changed"); +} + +void TextEditor::apply_code() { + text_file->set_text(code_editor->get_text_edit()->get_text()); +} + +bool TextEditor::is_unsaved() { + + return code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version(); +} + +Variant TextEditor::get_edit_state() { + + return code_editor->get_edit_state(); +} + +void TextEditor::set_edit_state(const Variant &p_state) { + + code_editor->set_edit_state(p_state); +} + +void TextEditor::trim_trailing_whitespace() { + + code_editor->trim_trailing_whitespace(); +} + +void TextEditor::convert_indent_to_spaces() { + + code_editor->convert_indent_to_spaces(); +} + +void TextEditor::convert_indent_to_tabs() { + + code_editor->convert_indent_to_tabs(); +} + +void TextEditor::tag_saved_version() { + + code_editor->get_text_edit()->tag_saved_version(); +} + +void TextEditor::goto_line(int p_line, bool p_with_error) { + + code_editor->goto_line(p_line); +} + +void TextEditor::ensure_focus() { + + code_editor->get_text_edit()->grab_focus(); +} + +Vector<String> TextEditor::get_functions() { + + return Vector<String>(); +} + +bool TextEditor::show_members_overview() { + return true; +} + +void TextEditor::update_settings() { + + code_editor->update_editor_settings(); +} + +void TextEditor::set_tooltip_request_func(String p_method, Object *p_obj) { + + code_editor->get_text_edit()->set_tooltip_request_func(p_obj, p_method, this); +} + +Control *TextEditor::get_edit_menu() { + + return edit_hb; +} + +void TextEditor::clear_edit_menu() { + memdelete(edit_hb); +} + +void TextEditor::_notification(int p_what) { + + switch (p_what) { + case NOTIFICATION_READY: + _load_theme_settings(); + set_syntax_highlighter(NULL); + break; + } +} + +void TextEditor::_edit_option(int p_op) { + TextEdit *tx = code_editor->get_text_edit(); + + switch (p_op) { + case EDIT_UNDO: { + + tx->undo(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_REDO: { + + tx->redo(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_CUT: { + + tx->cut(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_COPY: { + + tx->copy(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_PASTE: { + + tx->paste(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_SELECT_ALL: { + + tx->select_all(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_MOVE_LINE_UP: { + + code_editor->move_lines_up(); + } break; + case EDIT_MOVE_LINE_DOWN: { + + code_editor->move_lines_down(); + } break; + case EDIT_INDENT_LEFT: { + + tx->indent_left(); + } break; + case EDIT_INDENT_RIGHT: { + + tx->indent_right(); + } break; + case EDIT_DELETE_LINE: { + + code_editor->delete_lines(); + } break; + case EDIT_CLONE_DOWN: { + + code_editor->code_lines_down(); + } break; + case EDIT_TOGGLE_FOLD_LINE: { + + tx->toggle_fold_line(tx->cursor_get_line()); + tx->update(); + } break; + case EDIT_FOLD_ALL_LINES: { + + tx->fold_all_lines(); + tx->update(); + } break; + case EDIT_UNFOLD_ALL_LINES: { + + tx->unhide_all_lines(); + tx->update(); + } break; + case EDIT_TRIM_TRAILING_WHITESAPCE: { + + trim_trailing_whitespace(); + } break; + case EDIT_CONVERT_INDENT_TO_SPACES: { + + convert_indent_to_spaces(); + } break; + case EDIT_CONVERT_INDENT_TO_TABS: { + + convert_indent_to_tabs(); + } break; + case EDIT_TO_UPPERCASE: { + + _convert_case(CodeTextEditor::UPPER); + } break; + case EDIT_TO_LOWERCASE: { + + _convert_case(CodeTextEditor::LOWER); + } break; + case EDIT_CAPITALIZE: { + + _convert_case(CodeTextEditor::CAPITALIZE); + } break; + case SEARCH_FIND: { + + code_editor->get_find_replace_bar()->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + + code_editor->get_find_replace_bar()->search_next(); + } break; + case SEARCH_FIND_PREV: { + + code_editor->get_find_replace_bar()->search_prev(); + } break; + case SEARCH_REPLACE: { + + code_editor->get_find_replace_bar()->popup_replace(); + } break; + case SEARCH_GOTO_LINE: { + + goto_line_dialog->popup_find_line(tx); + } break; + } +} + +void TextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { + + code_editor->convert_case(p_case); +} + +void TextEditor::_bind_methods() { + + ClassDB::bind_method("_validate_script", &TextEditor::_validate_script); + ClassDB::bind_method("_load_theme_settings", &TextEditor::_load_theme_settings); + ClassDB::bind_method("_edit_option", &TextEditor::_edit_option); + ClassDB::bind_method("_change_syntax_highlighter", &TextEditor::_change_syntax_highlighter); + ClassDB::bind_method("_text_edit_gui_input", &TextEditor::_text_edit_gui_input); +} + +static ScriptEditorBase *create_editor(const RES &p_resource) { + + if (Object::cast_to<TextFile>(*p_resource)) { + return memnew(TextEditor); + } + return NULL; +} + +void TextEditor::register_editor() { + + ScriptEditor::register_create_script_editor_function(create_editor); +} + +void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { + + Ref<InputEventMouseButton> mb = ev; + + if (mb.is_valid()) { + if (mb->get_button_index() == BUTTON_RIGHT) { + + int col, row; + TextEdit *tx = code_editor->get_text_edit(); + tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); + + tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); + bool can_fold = tx->can_fold(row); + bool is_folded = tx->is_folded(row); + + if (tx->is_right_click_moving_caret()) { + if (tx->is_selection_active()) { + + int from_line = tx->get_selection_from_line(); + int to_line = tx->get_selection_to_line(); + int from_column = tx->get_selection_from_column(); + int to_column = tx->get_selection_to_column(); + + if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) { + // Right click is outside the seleted text + tx->deselect(); + } + } + if (!tx->is_selection_active()) { + tx->cursor_set_line(row, true, false); + tx->cursor_set_column(col); + } + } + + if (!mb->is_pressed()) { + _make_context_menu(tx->is_selection_active(), can_fold, is_folded); + } + } + } +} + +void TextEditor::_make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded) { + + context_menu->clear(); + if (p_selection) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + } + + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); + + if (p_selection) { + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); + } + if (p_can_fold || p_is_folded) + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); + + context_menu->set_position(get_global_transform().xform(get_local_mouse_position())); + context_menu->set_size(Vector2(1, 1)); + context_menu->popup(); +} + +TextEditor::TextEditor() { + code_editor = memnew(CodeTextEditor); + add_child(code_editor); + code_editor->add_constant_override("separation", 0); + code_editor->connect("load_theme_settings", this, "_load_theme_settings"); + code_editor->connect("validate_script", this, "_validate_script"); + code_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); + code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + + update_settings(); + + code_editor->get_text_edit()->set_context_menu_enabled(false); + code_editor->get_text_edit()->connect("gui_input", this, "_text_edit_gui_input"); + + context_menu = memnew(PopupMenu); + add_child(context_menu); + context_menu->connect("id_pressed", this, "_edit_option"); + + edit_hb = memnew(HBoxContainer); + + search_menu = memnew(MenuButton); + edit_hb->add_child(search_menu); + search_menu->set_text(TTR("Search")); + search_menu->get_popup()->connect("id_pressed", this, "_edit_option"); + + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); + search_menu->get_popup()->add_separator(); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); + + goto_line_dialog = memnew(GotoLineDialog); + add_child(goto_line_dialog); + + edit_menu = memnew(MenuButton); + edit_menu->set_text(TTR("Edit")); + edit_menu->get_popup()->connect("id_pressed", this, "_edit_option"); + + edit_hb->add_child(edit_menu); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS); + + edit_menu->get_popup()->add_separator(); + PopupMenu *convert_case = memnew(PopupMenu); + convert_case->set_name("convert_case"); + edit_menu->get_popup()->add_child(convert_case); + edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "convert_case"); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase")), EDIT_TO_UPPERCASE); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase")), EDIT_TO_LOWERCASE); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE); + convert_case->connect("id_pressed", this, "_edit_option"); + + highlighters["Standard"] = NULL; + highlighter_menu = memnew(PopupMenu); + highlighter_menu->set_name("highlighter_menu"); + edit_menu->get_popup()->add_child(highlighter_menu); + edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "highlighter_menu"); + highlighter_menu->add_radio_check_item(TTR("Standard")); + highlighter_menu->connect("id_pressed", this, "_change_syntax_highlighter"); + + code_editor->get_text_edit()->set_drag_forwarding(this); +} diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h new file mode 100644 index 0000000000..8b1983d891 --- /dev/null +++ b/editor/plugins/text_editor.h @@ -0,0 +1,146 @@ +/*************************************************************************/ +/* text_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXT_EDITOR_H +#define TEXT_EDITOR_H + +#include "script_editor_plugin.h" + +class TextEditor : public ScriptEditorBase { + + GDCLASS(TextEditor, ScriptEditorBase) + +private: + CodeTextEditor *code_editor; + + Ref<TextFile> text_file; + + HBoxContainer *edit_hb; + MenuButton *edit_menu; + PopupMenu *highlighter_menu; + MenuButton *search_menu; + PopupMenu *context_menu; + + GotoLineDialog *goto_line_dialog; + + struct ColorsCache { + Color font_color; + Color symbol_color; + Color keyword_color; + Color basetype_color; + Color type_color; + Color comment_color; + Color string_color; + } colors_cache; + + enum { + EDIT_UNDO, + EDIT_REDO, + EDIT_CUT, + EDIT_COPY, + EDIT_PASTE, + EDIT_SELECT_ALL, + EDIT_TRIM_TRAILING_WHITESAPCE, + EDIT_CONVERT_INDENT_TO_SPACES, + EDIT_CONVERT_INDENT_TO_TABS, + EDIT_MOVE_LINE_UP, + EDIT_MOVE_LINE_DOWN, + EDIT_INDENT_RIGHT, + EDIT_INDENT_LEFT, + EDIT_DELETE_LINE, + EDIT_CLONE_DOWN, + EDIT_TO_UPPERCASE, + EDIT_TO_LOWERCASE, + EDIT_CAPITALIZE, + EDIT_TOGGLE_FOLD_LINE, + EDIT_FOLD_ALL_LINES, + EDIT_UNFOLD_ALL_LINES, + SEARCH_FIND, + SEARCH_FIND_NEXT, + SEARCH_FIND_PREV, + SEARCH_REPLACE, + SEARCH_GOTO_LINE, + }; + +protected: + static void _bind_methods(); + + void _notification(int p_what); + + void _edit_option(int p_op); + void _make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded); + void _text_edit_gui_input(const Ref<InputEvent> &ev); + + Map<String, SyntaxHighlighter *> highlighters; + void _change_syntax_highlighter(int p_idx); + void _load_theme_settings(); + + void _convert_case(CodeTextEditor::CaseStyle p_case); + + void _validate_script(); + +public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); + + virtual String get_name(); + virtual Ref<Texture> get_icon(); + virtual RES get_edited_resource() const; + virtual void set_edited_resource(const RES &p_res); + void set_edited_file(const Ref<TextFile> &p_file); + virtual void reload_text(); + virtual void apply_code(); + virtual bool is_unsaved(); + virtual Variant get_edit_state(); + virtual void set_edit_state(const Variant &p_state); + virtual Vector<String> get_functions(); + virtual void get_breakpoints(List<int> *p_breakpoints); + virtual void goto_line(int p_line, bool p_with_error = false); + virtual void trim_trailing_whitespace(); + virtual void convert_indent_to_spaces(); + virtual void convert_indent_to_tabs(); + virtual void ensure_focus(); + virtual void tag_saved_version(); + virtual void update_settings(); + virtual bool show_members_overview(); + virtual bool can_lose_focus_on_node_selection() { return true; } + virtual void set_debugger_active(bool p_active); + virtual void set_tooltip_request_func(String p_method, Object *p_obj); + virtual void add_callback(const String &p_function, PoolStringArray p_args); + + virtual Control *get_edit_menu(); + virtual void clear_edit_menu(); + + static void register_editor(); + + TextEditor(); +}; + +#endif // TEXT_EDITOR_H diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 19646f37b5..484da3b4f3 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -526,7 +526,7 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era if (!erase) { ids = get_selected_tiles(); - if (ids.size() == 0 && ids[0] == TileMap::INVALID_CELL) + if (ids.size() == 0 || ids[0] == TileMap::INVALID_CELL) return PoolVector<Vector2>(); } else if (prev_id == TileMap::INVALID_CELL) { return PoolVector<Vector2>(); @@ -538,9 +538,7 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era } } - Rect2i r = node->_edit_get_rect(); - r.position = r.position / node->get_cell_size(); - r.size = r.size / node->get_cell_size(); + Rect2i r = node->get_used_rect(); int area = r.get_area(); if (preview) { @@ -1029,7 +1027,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (points.size() == 0) return false; - undo_redo->create_action(TTR("Bucket Fill")); + _start_undo(TTR("Bucket Fill")); Dictionary op; op["id"] = get_selected_tiles(); @@ -1039,7 +1037,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _fill_points(points, op); - undo_redo->commit_action(); + _finish_undo(); // We want to keep the bucket-tool active return true; diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 7f46844f6c..576227344b 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -4394,7 +4394,7 @@ PropertyEditor::PropertyEditor() { use_filter = false; subsection_selectable = false; property_selectable = false; - show_type_icons = EDITOR_DEF("interface/editor/show_type_icons", false); + show_type_icons = false; // TODO: need to reimplement it to work with the new inspector } PropertyEditor::~PropertyEditor() { diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 2ffaa0ca12..8d38bf39b5 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -724,7 +724,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } } break; - case TOOL_SCENE_CLEAR_INSTANCING: { + case TOOL_SCENE_MAKE_LOCAL: { List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -736,7 +736,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; ERR_FAIL_COND(node->get_filename() == String()); - undo_redo->create_action(TTR("Discard Instancing")); + undo_redo->create_action(TTR("Make Local")); undo_redo->add_do_method(node, "set_filename", ""); undo_redo->add_undo_method(node, "set_filename", node->get_filename()); _node_replace_owner(node, node, root); @@ -2028,7 +2028,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { bool placeholder = selection[0]->get_scene_instance_load_placeholder(); menu->add_check_item(TTR("Editable Children"), TOOL_SCENE_EDITABLE_CHILDREN); menu->add_check_item(TTR("Load As Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); - menu->add_item(TTR("Discard Instancing"), TOOL_SCENE_CLEAR_INSTANCING); + menu->add_item(TTR("Make Local"), TOOL_SCENE_MAKE_LOCAL); menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Open in Editor"), TOOL_SCENE_OPEN); menu->set_item_checked(menu->get_item_idx_from_text(TTR("Editable Children")), editable); menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index fd74611bde..57f4759747 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -77,7 +77,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_BUTTON_MAX, TOOL_SCENE_EDITABLE_CHILDREN, TOOL_SCENE_USE_PLACEHOLDER, - TOOL_SCENE_CLEAR_INSTANCING, + TOOL_SCENE_MAKE_LOCAL, TOOL_SCENE_OPEN, TOOL_SCENE_CLEAR_INHERITANCE, TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM, diff --git a/misc/dist/linux/godot.appdata.xml b/misc/dist/linux/org.godotengine.Godot.appdata.xml index a381556025..8278f1f6f5 100644 --- a/misc/dist/linux/godot.appdata.xml +++ b/misc/dist/linux/org.godotengine.Godot.appdata.xml @@ -1,11 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 2017-2018 Rémi Verschelde <akien@godotengine.org> --> <component type="desktop"> - <id>godot.desktop</id> + <id>org.godotengine.Godot.desktop</id> <metadata_license>CC0-1.0</metadata_license> <project_license>MIT</project_license> <name>Godot Engine</name> <summary>Multi-platform 2D and 3D game engine with a feature-rich editor</summary> + <icon type="remote">https://raw.githubusercontent.com/godotengine/godot/master/icon.png</icon> + ​<launchable type="desktop-id">org.godotengine.Godot.desktop</launchable> <description> <p> Godot is an advanced, feature-packed, multi-platform 2D and 3D game @@ -24,8 +26,12 @@ <image>https://download.tuxfamily.org/godotengine/media/screenshots/editor_3d_fracteed-720p.jpg</image> </screenshot> </screenshots> + <categories> + <category>Development</category> + </categories> <url type="homepage">https://godotengine.org</url> <url type="bugtracker">https://github.com/godotengine/godot/issues</url> + <url type="faq">http://docs.godotengine.org/en/latest/about/faq.html</url> <url type="help">http://docs.godotengine.org</url> <url type="donation">https://godotengine.org/donate</url> <url type="translate">https://hosted.weblate.org/projects/godot-engine/godot</url> diff --git a/misc/dist/linux/godot.desktop b/misc/dist/linux/org.godotengine.Godot.desktop index 974352b117..439b1d87b8 100644 --- a/misc/dist/linux/godot.desktop +++ b/misc/dist/linux/org.godotengine.Godot.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Name=Godot Engine GenericName=Libre game engine -Comment=Multi-platform 2D and 3D game engine with a feature rich editor +Comment=Multi-platform 2D and 3D game engine with a feature-rich editor Exec=godot -p Icon=godot Terminal=false diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 4012e821bb..70ca8d68b8 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -110,7 +110,6 @@ struct ClassAPI { bool is_singleton; bool is_instanciable; // @Unclear - bool is_creatable; bool is_reference; List<MethodAPI> methods; @@ -385,7 +384,6 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { source.push_back(String("\t\t\"instanciable\": ") + (api.is_instanciable ? "true" : "false") + ",\n"); source.push_back(String("\t\t\"is_reference\": ") + (api.is_reference ? "true" : "false") + ",\n"); // @Unclear - // source.push_back(String("\t\t\"createable\": ") + (api.is_creatable ? "true" : "false") + ",\n"); source.push_back("\t\t\"constants\": {\n"); for (List<ConstantAPI>::Element *e = api.constants.front(); e; e = e->next()) { diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index af9fea6681..9dea7a9c9e 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -1333,6 +1333,19 @@ VisualScript::VisualScript() { base_type = "Object"; } +Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) { + List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>); + get_sequence_connection_list(edited_func, sc); + Set<int> connected; + for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) { + if (E->get().from_node == from_node) { + connected.insert(E->get().from_output); + } + } + memdelete(sc); + return connected; +} + VisualScript::~VisualScript() { while (!functions.empty()) { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index b163203a3a..2ad72a40c0 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -319,6 +319,7 @@ public: void custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx); void remove_custom_signal(const StringName &p_name); void rename_custom_signal(const StringName &p_name, const StringName &p_new_name); + Set<int> get_output_sequence_ports_connected(const String &edited_func, int from_node); void get_custom_signal_list(List<StringName> *r_custom_signals) const; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 873cc293c9..ad6d32b567 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -33,8 +33,10 @@ #include "core/script_language.h" #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" +#include "object.h" #include "os/input.h" #include "os/keyboard.h" +#include "variant.h" #include "visual_script_expression.h" #include "visual_script_flow_control.h" #include "visual_script_func_nodes.h" @@ -1325,6 +1327,12 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { revert_on_drag = String(); //so we can still drag functions } + + Ref<InputEventKey> k = p_event; + if (k.is_valid() && k->get_scancode() == KEY_A && k->get_shift() && k->is_pressed()) { + new_connect_node_select->select_from_visual_script(String("")); + accept_event(); + } } void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { @@ -1780,7 +1788,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da call->set_base_type(node->get_class()); n = call; - method_select->select_method_from_instance(node); + method_select->select_from_instance(node); selecting_method_id = base_id; } @@ -1917,7 +1925,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } } -void VisualScriptEditor::_selected_method(const String &p_method) { +void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type) { Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id); if (!vsfc.is_valid()) @@ -1962,22 +1970,16 @@ void VisualScriptEditor::_button_resource_previewed(const String &p_path, const void VisualScriptEditor::apply_code() { } -Ref<Script> VisualScriptEditor::get_edited_script() const { - +RES VisualScriptEditor::get_edited_resource() const { return script; } -Vector<String> VisualScriptEditor::get_functions() { - - return Vector<String>(); -} +void VisualScriptEditor::set_edited_resource(const RES &p_res) { -void VisualScriptEditor::set_edited_script(const Ref<Script> &p_script) { - - script = p_script; - signal_editor->script = p_script; + script = p_res; + signal_editor->script = script; signal_editor->undo_redo = undo_redo; - variable_editor->script = p_script; + variable_editor->script = script; variable_editor->undo_redo = undo_redo; script->connect("node_ports_changed", this, "_node_ports_changed"); @@ -1986,6 +1988,11 @@ void VisualScriptEditor::set_edited_script(const Ref<Script> &p_script) { _update_available_nodes(); } +Vector<String> VisualScriptEditor::get_functions() { + + return Vector<String>(); +} + void VisualScriptEditor::reload_text() { } @@ -2436,33 +2443,19 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro if (!vsn.is_valid()) return; - if (p_from_slot < vsn->get_output_sequence_port_count()) { + port_action_pos = p_release_pos; - port_action_popup->clear(); - port_action_popup->add_item(TTR("Condition"), CREATE_COND); - port_action_popup->add_item(TTR("Sequence"), CREATE_SEQUENCE); - port_action_popup->add_item(TTR("Switch"), CREATE_SWITCH); - port_action_popup->add_item(TTR("Iterator"), CREATE_ITERATOR); - port_action_popup->add_item(TTR("While"), CREATE_WHILE); - port_action_popup->add_item(TTR("Return"), CREATE_RETURN); + if (p_from_slot < vsn->get_output_sequence_port_count()) { port_action_node = p_from.to_int(); port_action_output = p_from_slot; - + _port_action_menu(CREATE_ACTION); } else { - port_action_popup->clear(); - port_action_popup->add_item(TTR("Call"), CREATE_CALL); - port_action_popup->add_item(TTR("Get"), CREATE_GET); - port_action_popup->add_item(TTR("Set"), CREATE_SET); port_action_output = p_from_slot - vsn->get_output_sequence_port_count(); port_action_node = p_from.to_int(); + _port_action_menu(CREATE_CALL_SET_GET); } - - port_action_pos = p_release_pos; - port_action_popup->set_size(Size2(1, 1)); - port_action_popup->set_position(graph->get_global_position() + p_release_pos); - port_action_popup->popup(); } VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes) { @@ -2530,168 +2523,202 @@ void VisualScriptEditor::_port_action_menu(int p_option) { bool seq_connect = false; - Ref<VisualScriptNode> vnode; Set<int> vn; switch (p_option) { - case CREATE_CALL: { - + case CREATE_CALL_SET_GET: { Ref<VisualScriptFunctionCall> n; n.instance(); - vnode = n; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); - - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } + if (tg.gdclass != StringName()) { + n->set_base_type(tg.gdclass); + } else { + n->set_base_type("Object"); + } + String type_string = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (tg.type == Variant::OBJECT) { if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_method_from_script(tg.script); + new_connect_node_select->select_from_script(tg.script, ""); + } else if (type_string != String()) { + new_connect_node_select->select_from_base_type(type_string); } else { - new_connect_node_select->select_method_from_base_type(n->get_base_type()); + new_connect_node_select->select_from_base_type(n->get_base_type()); } - + } else if (tg.type == Variant::NIL) { + new_connect_node_select->select_from_base_type(""); } else { - n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_method_from_basic_type(tg.type); + new_connect_node_select->select_from_basic_type(tg.type); } - } break; - case CREATE_SET: { - - Ref<VisualScriptPropertySet> n; - n.instance(); - vnode = n; - + case CREATE_ACTION: { + seq_connect = true; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - + PropertyInfo property_info = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output); if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); - - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); + if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) { + new_connect_node_select->select_from_action(property_info.hint_string); } else { - n->set_base_type("Object"); + new_connect_node_select->select_from_action(""); } - - if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_property_from_script(tg.script); - } else { - new_connect_node_select->select_property_from_base_type(n->get_base_type()); - } - + } else if (tg.type == Variant::NIL) { + new_connect_node_select->select_from_action(""); } else { - n->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_property_from_basic_type(tg.type); + new_connect_node_select->select_from_action(Variant::get_type_name(tg.type)); } } break; - case CREATE_GET: { + } +} - Ref<VisualScriptPropertyGet> n; - n.instance(); - vnode = n; +void VisualScriptEditor::new_node(Ref<VisualScriptNode> vnode, Vector2 ofs) { + Set<int> vn; + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph", new_id); + undo_redo->add_undo_method(this, "_update_graph", new_id); + undo_redo->commit_action(); - VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + port_action_new_node = new_id; +} - if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); +void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) { + undo_redo->create_action(TTR("Connect Node Data")); + VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr()); + if (vnode_return != NULL && vnode_old->get_output_value_port_count() > 0) { + vnode_return->set_enable_return_value(true); + } + if (vnode_old->get_output_value_port_count() <= 0) { + undo_redo->commit_action(); + return; + } + if (vnode->get_input_value_port_count() <= 0) { + undo_redo->commit_action(); + return; + } + int port = port_action_output; + int value_count = vnode_old->get_output_value_port_count(); + if (port >= value_count) { + port = 0; + } + int count = vnode_old->get_output_value_port_count() + vnode_old->get_output_sequence_port_count(); + undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, port_action_node, port, new_id, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, port_action_node, port, new_id, 0); + undo_redo->commit_action(); +} - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } +void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category) { + Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + ofs /= EDSCALE; - if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_property_from_script(tg.script); - } else { - new_connect_node_select->select_property_from_base_type(n->get_base_type()); - } + Set<int> vn; + if (p_category == "visualscript") { + Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text); + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + int new_id = script->get_available_id(); + + if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr())) { + Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type); + } + + if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())) { + Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + String hint_name = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (type == Variant::OBJECT) { + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name); + } else if (type == Variant::NIL) { + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(""); } else { - n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_property_from_basic_type(tg.type); + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type)); } + } + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode_new, ofs); + connect_seq(vnode_old, vnode_new, new_id); + connect_data(vnode_old, vnode_new, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + return; + } - } break; - case CREATE_COND: { + Ref<VisualScriptNode> vnode; + + seq_connect = false; + if (p_category == String("method")) { + + Ref<VisualScriptFunctionCall> n; + n.instance(); + vnode = n; + } else if (p_category == String("set")) { + + Ref<VisualScriptPropertySet> n; + n.instance(); + n->set_property(p_text); + vnode = n; + } else if (p_category == String("get")) { + + Ref<VisualScriptPropertyGet> n; + n.instance(); + n->set_property(p_text); + vnode = n; + } + + if (p_category == String("action")) { + if (p_text == "VisualScriptCondition") { Ref<VisualScriptCondition> n; n.instance(); vnode = n; seq_connect = true; + } + if (p_text == "VisualScriptSwitch") { - } break; - case CREATE_SEQUENCE: { - - Ref<VisualScriptSequence> n; + Ref<VisualScriptSwitch> n; n.instance(); vnode = n; seq_connect = true; + } else if (p_text == "VisualScriptSequence") { - } break; - case CREATE_SWITCH: { - - Ref<VisualScriptSwitch> n; + Ref<VisualScriptSequence> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_ITERATOR: { + } else if (p_text == "VisualScriptIterator") { Ref<VisualScriptIterator> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_WHILE: { + } else if (p_text == "VisualScriptWhile") { Ref<VisualScriptWhile> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_RETURN: { + } else if (p_text == "VisualScriptReturn") { Ref<VisualScriptReturn> n; n.instance(); vnode = n; seq_connect = true; - - } break; - } - - int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - if (seq_connect) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); + } } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph", new_id); - undo_redo->add_undo_method(this, "_update_graph", new_id); - undo_redo->commit_action(); - port_action_new_node = new_id; -} - -void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p_text) { + new_node(vnode, ofs); Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node); @@ -2699,28 +2726,152 @@ void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + + if (tg.gdclass != StringName()) { + vsfc->set_base_type(tg.gdclass); + + } else { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsfc->set_base_type(base_type); + } + if (p_text == "call" || p_text == "call_deferred") { + vsfc->set_function(""); + } + } + if (tg.script.is_valid()) { + vsfc->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + vsfc->set_base_type(script->get_instance_base_type()); + } else { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); + vsfc->set_basic_type(tg.type); + } } if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { Ref<VisualScriptPropertySet> vsp = vsn; - vsp->set_property(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); + + } else { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } + } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + vsp->set_base_type(script->get_instance_base_type()); + } else { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); + } } if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) { - Ref<VisualScriptPropertyGet> vsp = vsn; - vsp->set_property(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); + + } else { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } + } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + vsp->set_base_type(script->get_instance_base_type()); + } else { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); + } } + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + connect_seq(vnode_old, vnode, port_action_new_node); + connect_data(vnode_old, vnode, port_action_new_node); _update_graph(port_action_new_node); _update_graph_connections(); } -void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { +void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) { + int seq_count = vnode_old->get_output_sequence_port_count(); + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr()); + if (vnode_operator != NULL && vnode_operator->has_input_sequence_port() == false) { + return; + } + VisualScriptConstructor *vnode_constructor = Object::cast_to<VisualScriptConstructor>(vnode_new.ptr()); + if (vnode_constructor != NULL) { + return; + } + if (vnode_old->get_output_sequence_port_count() <= 0) { + return; + } + if (vnode_new->has_input_sequence_port() == false) { + return; + } + VisualScriptFunction *vnode_function = Object::cast_to<VisualScriptFunction>(vnode_old.ptr()); + undo_redo->create_action(TTR("Connect Node Sequence")); + int pass_port = -vnode_old->get_output_sequence_port_count() + 1; + int return_port = port_action_output - 1; + if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") && + !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(pass_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, pass_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, pass_port, new_id); + } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") && + !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(return_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, return_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, return_port, new_id); + } else { + for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) { + int count = vnode_old->get_output_sequence_port_count(); + if (port_action_output < count && !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port_action_output)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port_action_output, new_id); + break; + } else if (!script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port, new_id); + break; + } + } + } + + undo_redo->commit_action(); +} + +void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category) { String name = p_text; if (script->has_function(name)) { @@ -2777,12 +2928,29 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { _update_graph(); } -void VisualScriptEditor::_cancel_connect_node_method_or_setget() { - - script->remove_node(edited_func, port_action_new_node); +void VisualScriptEditor::_cancel_connect_node() { + // Causes crashes + //script->remove_node(edited_func, port_action_new_node); _update_graph(); } +void VisualScriptEditor::_create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point) { + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + ofs /= EDSCALE; + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + void VisualScriptEditor::_default_value_changed() { Ref<VisualScriptNode> vsn = script->get_node(edited_func, editing_id); @@ -3286,10 +3454,11 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_comment_node_resized", &VisualScriptEditor::_comment_node_resized); ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed); ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu); - ClassDB::bind_method("_selected_connect_node_method_or_setget", &VisualScriptEditor::_selected_connect_node_method_or_setget); + ClassDB::bind_method("_selected_connect_node", &VisualScriptEditor::_selected_connect_node); ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method); - ClassDB::bind_method("_cancel_connect_node_method_or_setget", &VisualScriptEditor::_cancel_connect_node_method_or_setget); + ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node); + ClassDB::bind_method("_create_new_node", &VisualScriptEditor::_create_new_node); ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed); ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw); @@ -3480,25 +3649,21 @@ VisualScriptEditor::VisualScriptEditor() { add_child(default_value_edit); default_value_edit->connect("variant_changed", this, "_default_value_changed"); - method_select = memnew(PropertySelector); + method_select = memnew(VisualScriptPropertySelector); add_child(method_select); method_select->connect("selected", this, "_selected_method"); error_line = -1; - new_connect_node_select = memnew(PropertySelector); + new_connect_node_select = memnew(VisualScriptPropertySelector); add_child(new_connect_node_select); - new_connect_node_select->connect("selected", this, "_selected_connect_node_method_or_setget"); - new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node_method_or_setget"); + new_connect_node_select->connect("selected", this, "_selected_connect_node"); + new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node"); - new_virtual_method_select = memnew(PropertySelector); + new_virtual_method_select = memnew(VisualScriptPropertySelector); add_child(new_virtual_method_select); new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method"); new_virtual_method_select->get_cancel()->connect("pressed", this, "_selected_new_virtual_method"); - port_action_popup = memnew(PopupMenu); - add_child(port_action_popup); - port_action_popup->connect("id_pressed", this, "_port_action_menu"); - member_popup = memnew(PopupMenu); add_child(member_popup); members->connect("item_rmb_selected", this, "_member_rmb_selected"); @@ -3515,9 +3680,9 @@ VisualScriptEditor::~VisualScriptEditor() { memdelete(variable_editor); } -static ScriptEditorBase *create_editor(const Ref<Script> &p_script) { +static ScriptEditorBase *create_editor(const RES &p_resource) { - if (Object::cast_to<VisualScript>(*p_script)) { + if (Object::cast_to<VisualScript>(*p_resource)) { return memnew(VisualScriptEditor); } diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 0bd64d6a1d..0283f7b162 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -34,9 +34,9 @@ #include "editor/create_dialog.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/property_editor.h" -#include "editor/property_selector.h" #include "scene/gui/graph_edit.h" #include "visual_script.h" +#include "visual_script_property_selector.h" class VisualScriptEditorSignalEdit; class VisualScriptEditorVariableEdit; @@ -62,15 +62,8 @@ class VisualScriptEditor : public ScriptEditorBase { enum PortAction { - CREATE_CALL, - CREATE_SET, - CREATE_GET, - CREATE_COND, - CREATE_SEQUENCE, - CREATE_SWITCH, - CREATE_ITERATOR, - CREATE_WHILE, - CREATE_RETURN, + CREATE_CALL_SET_GET, + CREATE_ACTION, }; enum MemberAction { @@ -102,9 +95,9 @@ class VisualScriptEditor : public ScriptEditorBase { AcceptDialog *edit_signal_dialog; PropertyEditor *edit_signal_edit; - PropertySelector *method_select; - PropertySelector *new_connect_node_select; - PropertySelector *new_virtual_method_select; + VisualScriptPropertySelector *method_select; + VisualScriptPropertySelector *new_connect_node_select; + VisualScriptPropertySelector *new_virtual_method_select; VisualScriptEditorVariableEdit *variable_editor; @@ -162,21 +155,29 @@ class VisualScriptEditor : public ScriptEditorBase { static Clipboard *clipboard; - PopupMenu *port_action_popup; PopupMenu *member_popup; - MemberType member_type; String member_name; + bool seq_connect = false; + PortAction port_action; int port_action_node; int port_action_output; Vector2 port_action_pos; int port_action_new_node; void _port_action_menu(int p_option); - void _selected_connect_node_method_or_setget(const String &p_text); - void _cancel_connect_node_method_or_setget(); - void _selected_new_virtual_method(const String &p_text); + + void new_node(Ref<VisualScriptNode> vnode, Vector2 ofs); + + void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id); + + void _selected_connect_node(const String &p_text, const String &p_category); + void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id); + + void _cancel_connect_node(); + void _create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point); + void _selected_new_virtual_method(const String &p_text, const String &p_category); int error_line; @@ -231,7 +232,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _comment_node_resized(const Vector2 &p_new_size, int p_node); int selecting_method_id; - void _selected_method(const String &p_method); + void _selected_method(const String &p_method, const String &p_type); void _draw_color_over_button(Object *obj, Color p_color); void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud); @@ -250,9 +251,9 @@ public: virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); virtual void apply_code(); - virtual Ref<Script> get_edited_script() const; + virtual RES get_edited_resource() const; + virtual void set_edited_resource(const RES &p_res); virtual Vector<String> get_functions(); - virtual void set_edited_script(const Ref<Script> &p_script); virtual void reload_text(); virtual String get_name(); virtual Ref<Texture> get_icon(); diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index ea23ab1b2a..0f58a20c00 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -767,7 +767,7 @@ PropertyInfo VisualScriptTypeCast::get_input_value_port_info(int p_idx) const { PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const { - return PropertyInfo(Variant::OBJECT, ""); + return PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_TYPE_STRING, get_base_type()); } String VisualScriptTypeCast::get_caption() const { diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index bdf5705ecd..ad886bc758 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -43,7 +43,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { - if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) + if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return 0; else return 1; @@ -51,7 +51,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { bool VisualScriptFunctionCall::has_input_sequence_port() const { - if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) + if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return false; else return true; @@ -231,7 +231,7 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con if (call_mode == CALL_MODE_INSTANCE) { if (p_idx == 0) { - return PropertyInfo(Variant::OBJECT, "pass"); + return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { p_idx--; } @@ -1055,7 +1055,7 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons if (call_mode == CALL_MODE_BASIC_TYPE) { return PropertyInfo(basic_type, "out"); } else if (call_mode == CALL_MODE_INSTANCE) { - return PropertyInfo(Variant::OBJECT, "pass"); + return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { return PropertyInfo(); } diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 8b7b809ec0..f174300a4c 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -2198,7 +2198,7 @@ PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const { PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const { - return PropertyInfo(Variant::OBJECT, "Scene Tree"); + return PropertyInfo(Variant::OBJECT, "Scene Tree", PROPERTY_HINT_TYPE_STRING, "SceneTree"); } String VisualScriptSceneTree::get_caption() const { diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp new file mode 100644 index 0000000000..994ea1f791 --- /dev/null +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -0,0 +1,705 @@ +/*************************************************************************/ +/* visual_script_property_selector.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "visual_script_property_selector.h" + +#include "editor_scale.h" +#include "modules/visual_script/visual_script.h" +#include "modules/visual_script/visual_script_builtin_funcs.h" +#include "modules/visual_script/visual_script_flow_control.h" +#include "modules/visual_script/visual_script_func_nodes.h" +#include "modules/visual_script/visual_script_nodes.h" +#include "os/keyboard.h" +#include "scene/main/node.h" +#include "scene/main/viewport.h" + +void VisualScriptPropertySelector::_text_changed(const String &p_newtext) { + _update_search(); +} + +void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) { + + Ref<InputEventKey> k = p_ie; + + if (k.is_valid()) { + + switch (k->get_scancode()) { + case KEY_UP: + case KEY_DOWN: + case KEY_PAGEUP: + case KEY_PAGEDOWN: { + + search_options->call("_gui_input", k); + search_box->accept_event(); + + TreeItem *root = search_options->get_root(); + if (!root->get_children()) + break; + + TreeItem *current = search_options->get_selected(); + + TreeItem *item = search_options->get_next_selected(root); + while (item) { + item->deselect(0); + item = search_options->get_next_selected(item); + } + + current->select(0); + + } break; + } + } +} + +void VisualScriptPropertySelector::_update_search() { + set_title(TTR("Search VisualScript")); + + search_options->clear(); + help_bit->set_text(""); + + TreeItem *root = search_options->create_item(); + bool found = false; + + if (properties) { + + List<PropertyInfo> props; + + if (instance) { + instance->get_property_list(&props, true); + } else if (type != Variant::NIL) { + Variant v; + Variant::CallError ce; + v = Variant::construct(type, NULL, 0, ce); + + v.get_property_list(&props); + } else { + + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + + props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); + Object::cast_to<Script>(obj)->get_script_property_list(&props); + } + + StringName base = base_type; + while (base) { + props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); + ClassDB::get_property_list(base, &props, true); + base = ClassDB::get_parent_class_nocheck(base); + } + } + + TreeItem *category = NULL; + + Ref<Texture> type_icons[Variant::VARIANT_MAX] = { + Control::get_icon("Variant", "EditorIcons"), + Control::get_icon("bool", "EditorIcons"), + Control::get_icon("int", "EditorIcons"), + Control::get_icon("float", "EditorIcons"), + Control::get_icon("String", "EditorIcons"), + Control::get_icon("Vector2", "EditorIcons"), + Control::get_icon("Rect2", "EditorIcons"), + Control::get_icon("Vector3", "EditorIcons"), + Control::get_icon("Transform2D", "EditorIcons"), + Control::get_icon("Plane", "EditorIcons"), + Control::get_icon("Quat", "EditorIcons"), + Control::get_icon("AABB", "EditorIcons"), + Control::get_icon("Basis", "EditorIcons"), + Control::get_icon("Transform", "EditorIcons"), + Control::get_icon("Color", "EditorIcons"), + Control::get_icon("Path", "EditorIcons"), + Control::get_icon("RID", "EditorIcons"), + Control::get_icon("Object", "EditorIcons"), + Control::get_icon("Dictionary", "EditorIcons"), + Control::get_icon("Array", "EditorIcons"), + Control::get_icon("PoolByteArray", "EditorIcons"), + Control::get_icon("PoolIntArray", "EditorIcons"), + Control::get_icon("PoolRealArray", "EditorIcons"), + Control::get_icon("PoolStringArray", "EditorIcons"), + Control::get_icon("PoolVector2Array", "EditorIcons"), + Control::get_icon("PoolVector3Array", "EditorIcons"), + Control::get_icon("PoolColorArray", "EditorIcons") + }; + + if (!seq_connect && visual_script_generic == false) { + get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); + get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); + get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box); + get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box); + if (type == Variant::INT) { + get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL) { + get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) { + get_visual_node_names("operators/math/", Set<String>(), found, root, search_box); + } + } + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (E->get().usage == PROPERTY_USAGE_CATEGORY) { + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + category = search_options->create_item(root); + category->set_text(0, E->get().name); + category->set_selectable(0, false); + + Ref<Texture> icon; + if (E->get().name == "Script Variables") { + icon = get_icon("Script", "EditorIcons"); + } else if (has_icon(E->get().name, "EditorIcons")) { + icon = get_icon(E->get().name, "EditorIcons"); + } else { + icon = get_icon("Object", "EditorIcons"); + } + category->set_icon(0, icon); + continue; + } + + if (!(E->get().usage & PROPERTY_USAGE_EDITOR) && !(E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) + continue; + + if (type_filter.size() && type_filter.find(E->get().type) == -1) + continue; + + String get_text_raw = String(TTR("Get")) + String(" ") + E->get().name; + String get_text = get_text_raw.capitalize(); + + String set_text_raw = String(TTR("Set ")) + String(" ") + E->get().name; + String set_text = set_text_raw.capitalize(); + String input = search_box->get_text().capitalize(); + if (input == String() || + get_text_raw.findn(input) != -1 || + get_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, get_text); + item->set_metadata(0, E->get().name); + item->set_icon(0, type_icons[E->get().type]); + item->set_metadata(1, "get"); + item->set_collapsed(1); + item->set_selectable(1, false); + item->set_selectable(0, true); + } + + if (input == String() || + set_text_raw.findn(input) != -1 && + set_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, set_text); + item->set_metadata(0, E->get().name); + item->set_icon(0, type_icons[E->get().type]); + item->set_metadata(1, "set"); + item->set_selectable(1, false); + item->set_selectable(0, true); + } + } + } + + if (seq_connect == true && visual_script_generic == false) { + String text = search_box->get_text(); + create_visualscript_item(String("VisualScriptCondition"), root, text, String("Condition")); + create_visualscript_item(String("VisualScriptSwitch"), root, text, String("Switch")); + create_visualscript_item(String("VisualScriptSequence"), root, text, String("Sequence")); + create_visualscript_item(String("VisualScriptIterator"), root, text, String("Iterator")); + create_visualscript_item(String("VisualScriptWhile"), root, text, String("While")); + create_visualscript_item(String("VisualScriptReturn"), root, text, String("Return")); + get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); + get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); + } + + if (visual_script_generic) { + get_visual_node_names("", Set<String>(), found, root, search_box); + } + + List<MethodInfo> methods; + + if (type != Variant::NIL) { + Variant v; + Variant::CallError ce; + v = Variant::construct(type, NULL, 0, ce); + v.get_method_list(&methods); + } else { + + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + + methods.push_back(MethodInfo("*Script Methods")); + Object::cast_to<Script>(obj)->get_script_method_list(&methods); + } + + StringName base = base_type; + while (base) { + methods.push_back(MethodInfo("*" + String(base))); + ClassDB::get_method_list(base, &methods, true, true); + base = ClassDB::get_parent_class_nocheck(base); + } + } + TreeItem *category = NULL; + bool script_methods = false; + + for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + if (E->get().name.begins_with("*")) { + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + category = search_options->create_item(root); + category->set_text(0, E->get().name.replace_first("*", "")); + category->set_selectable(0, false); + + Ref<Texture> icon; + script_methods = false; + print_line("name: " + E->get().name); + String rep = E->get().name.replace("*", ""); + if (E->get().name == "*Script Methods") { + icon = get_icon("Script", "EditorIcons"); + script_methods = true; + } else if (has_icon(rep, "EditorIcons")) { + icon = get_icon(rep, "EditorIcons"); + } else { + icon = get_icon("Object", "EditorIcons"); + } + category->set_icon(0, icon); + + continue; + } + + String name = E->get().name.get_slice(":", 0); + if (!script_methods && name.begins_with("_") && !(E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + if (virtuals_only && !(E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + if (!virtuals_only && (E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + MethodInfo mi = E->get(); + String desc = mi.name.capitalize() + " ("; + + if (search_box->get_text() != String() && + name.findn(search_box->get_text()) == -1 && + desc.findn(search_box->get_text()) == -1) + continue; + + TreeItem *item = search_options->create_item(category ? category : root); + + for (int i = 0; i < mi.arguments.size(); i++) { + + if (i > 0) + desc += ", "; + + if (mi.arguments[i].type == Variant::NIL) + desc += "var"; + else if (mi.arguments[i].name.find(":") != -1) { + desc += mi.arguments[i].name.get_slice(":", 1); + mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0); + } else + desc += Variant::get_type_name(mi.arguments[i].type); + } + + desc += ")"; + + item->set_text(0, desc); + item->set_icon(0, get_icon("MemberMethod", "EditorIcons")); + item->set_metadata(0, name); + item->set_selectable(0, true); + + item->set_metadata(1, "method"); + item->set_collapsed(1); + item->set_selectable(1, false); + + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + } + + TreeItem *selected_item = search_options->search_item_text(search_box->get_text()); + if (!found && selected_item != NULL) { + selected_item->select(0); + found = true; + } + + get_ok()->set_disabled(root->get_children() == NULL); +} + +void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) { + if (search_input == String() || text.findn(search_input) != -1) { + TreeItem *item = search_options->create_item(root); + item->set_text(0, text); + item->set_icon(0, get_icon("VisualScript", "EditorIcons")); + item->set_metadata(0, name); + item->set_metadata(1, "action"); + item->set_selectable(0, true); + item->set_collapsed(1); + item->set_selectable(1, false); + } +} + +void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box) { + Map<String, TreeItem *> path_cache; + + List<String> fnodes; + VisualScriptLanguage::singleton->get_registered_node_names(&fnodes); + + for (List<String>::Element *E = fnodes.front(); E; E = E->next()) { + if (!E->get().begins_with(root_filter)) { + continue; + } + Vector<String> path = E->get().split("/"); + bool is_filter = false; + for (Set<String>::Element *E = filter.front(); E; E = E->next()) { + if (path.size() >= 2 && path[1].findn(E->get()) != -1) { + is_filter = true; + break; + } + } + if (is_filter == true) { + continue; + } + + if (search_box->get_text() != String() && E->get().findn(search_box->get_text()) == -1) { + continue; + } + TreeItem *item = search_options->create_item(root); + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + String type_name; + if (vnode_operator != NULL) { + String type; + if (path.size() >= 2) { + type = path[1]; + } + type_name = type.capitalize() + " "; + } + VisualScriptFunctionCall *vnode_function_call = Object::cast_to<VisualScriptFunctionCall>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + if (vnode_function_call != NULL) { + String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type()); + type_name = basic_type.capitalize() + " "; + } + VisualScriptBuiltinFunc *vnode_builtin_function_call = Object::cast_to<VisualScriptBuiltinFunc>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + if (vnode_builtin_function_call != NULL) { + type_name = "Builtin "; + } + item->set_text(0, type_name + path[path.size() - 1].capitalize()); + item->set_icon(0, get_icon("VisualScript", "EditorIcons")); + item->set_selectable(0, true); + item->set_metadata(0, E->get()); + item->set_selectable(0, true); + item->set_metadata(1, "visualscript"); + item->set_selectable(1, false); + } +} + +void VisualScriptPropertySelector::_confirmed() { + + TreeItem *ti = search_options->get_selected(); + if (!ti) + return; + emit_signal("selected", ti->get_metadata(0), ti->get_metadata(1)); + hide(); +} + +void VisualScriptPropertySelector::_item_selected() { + + help_bit->set_text(""); + + TreeItem *item = search_options->get_selected(); + if (!item) + return; + String name = item->get_metadata(0); + + String class_type; + if (type) { + class_type = Variant::get_type_name(type); + + } else { + class_type = base_type; + } + + DocData *dd = EditorHelp::get_doc_data(); + String text; + + String at_class = class_type; + + while (at_class != String()) { + + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + if (E) { + for (int i = 0; i < E->get().properties.size(); i++) { + if (E->get().properties[i].name == name) { + text = E->get().properties[i].description; + } + } + } + + at_class = ClassDB::get_parent_class_nocheck(at_class); + } + at_class = class_type; + + while (at_class != String()) { + + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + if (E) { + for (int i = 0; i < E->get().methods.size(); i++) { + if (E->get().methods[i].name == name) { + text = E->get().methods[i].description; + } + } + } + + at_class = ClassDB::get_parent_class_nocheck(at_class); + } + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type); + if (E) { + for (int i = 0; i < E->get().methods.size(); i++) { + Vector<String> functions = name.rsplit("/", false, 1); + if (E->get().methods[i].name == functions[functions.size() - 1]) { + text = E->get().methods[i].description; + } + } + } + + List<String> *names = memnew(List<String>); + VisualScriptLanguage::singleton->get_registered_node_names(names); + if (names->find(name) != NULL) { + Ref<VisualScriptOperator> operator_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (operator_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(operator_node->get_class_name()); + if (E) { + text = Variant::get_operator_name(operator_node->get_operator()); + } + } + Ref<VisualScriptTypeCast> typecast_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (typecast_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(typecast_node->get_class_name()); + if (E) { + text = E->get().description; + } + } + + Ref<VisualScriptBuiltinFunc> builtin_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (builtin_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(builtin_node->get_class_name()); + if (E) { + for (int i = 0; i < E->get().constants.size(); i++) { + if (E->get().constants[i].value.to_int() == int(builtin_node->get_func())) { + text = E->get().constants[i].description; + } + } + } + } + } + + memdelete(names); + + if (text == String()) + return; + + help_bit->set_text(text); +} + +void VisualScriptPropertySelector::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + connect("confirmed", this, "_confirmed"); + } +} + +void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only) { + + base_type = p_base; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = false; + instance = NULL; + virtuals_only = p_virtuals_only; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + _update_search(); +} + +void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) { + type_filter = p_type_filter; +} + +void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current /*= ""*/, bool p_virtuals_only /*= false*/, bool p_seq_connect /*= false*/) { + + base_type = p_base; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = true; + instance = NULL; + virtuals_only = p_virtuals_only; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = p_seq_connect; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current /*= ""*/) { + ERR_FAIL_COND(p_script.is_null()); + + base_type = p_script->get_instance_base_type(); + selected = p_current; + type = Variant::NIL; + script = p_script->get_instance_id(); + properties = true; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current /*= ""*/) { + ERR_FAIL_COND(p_type == Variant::NIL); + base_type = ""; + selected = p_current; + type = p_type; + script = 0; + properties = true; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current /*= ""*/) { + base_type = p_type; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = false; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = true; + _update_search(); +} + +void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current /*= ""*/) { + base_type = ""; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = true; + instance = p_instance; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_visual_script(const String &p_base) { + base_type = p_base; + selected = ""; + type = Variant::NIL; + script = 0; + properties = true; + visual_script_generic = true; + instance = NULL; + virtuals_only = false; + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + + _update_search(); +} + +void VisualScriptPropertySelector::show_window(float p_screen_ratio) { + Rect2 rect; + Point2 window_size = get_viewport_rect().size; + rect.size = (window_size * p_screen_ratio).floor(); + rect.size.x = rect.size.x / 1.25f; + rect.position = ((window_size - rect.size) / 2.0f).floor(); + popup(rect); +} + +void VisualScriptPropertySelector::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_text_changed"), &VisualScriptPropertySelector::_text_changed); + ClassDB::bind_method(D_METHOD("_confirmed"), &VisualScriptPropertySelector::_confirmed); + ClassDB::bind_method(D_METHOD("_sbox_input"), &VisualScriptPropertySelector::_sbox_input); + ClassDB::bind_method(D_METHOD("_item_selected"), &VisualScriptPropertySelector::_item_selected); + + ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"))); +} + +VisualScriptPropertySelector::VisualScriptPropertySelector() { + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + //set_child_rect(vbc); + search_box = memnew(LineEdit); + vbc->add_margin_child(TTR("Search:"), search_box); + search_box->connect("text_changed", this, "_text_changed"); + search_box->connect("gui_input", this, "_sbox_input"); + search_options = memnew(Tree); + vbc->add_margin_child(TTR("Matches:"), search_options, true); + get_ok()->set_text(TTR("Open")); + get_ok()->set_disabled(true); + register_text_enter(search_box); + set_hide_on_ok(false); + search_options->connect("item_activated", this, "_confirmed"); + search_options->connect("cell_selected", this, "_item_selected"); + search_options->set_hide_root(true); + search_options->set_hide_folding(true); + virtuals_only = false; + help_bit = memnew(EditorHelpBit); + vbc->add_margin_child(TTR("Description:"), help_bit); + help_bit->connect("request_hide", this, "_closed"); + search_options->set_columns(2); + search_options->set_column_expand(1, false); +} diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h new file mode 100644 index 0000000000..ec536f86a8 --- /dev/null +++ b/modules/visual_script/visual_script_property_selector.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* visual_script_property_selector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VISUALSCRIPT_PROPERTYSELECTOR_H +#define VISUALSCRIPT_PROPERTYSELECTOR_H + +#include "editor/property_editor.h" +#include "editor_help.h" +#include "scene/gui/rich_text_label.h" + +class VisualScriptPropertySelector : public ConfirmationDialog { + GDCLASS(VisualScriptPropertySelector, ConfirmationDialog) + + LineEdit *search_box; + Tree *search_options; + + void _update_search(); + + void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text); + + void get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box); + + void _sbox_input(const Ref<InputEvent> &p_ie); + + void _confirmed(); + void _text_changed(const String &p_newtext); + + EditorHelpBit *help_bit; + + bool properties; + bool visual_script_generic; + String selected; + Variant::Type type; + String base_type; + ObjectID script; + Object *instance; + bool virtuals_only; + + bool seq_connect = false; + + void _item_selected(); + + Vector<Variant::Type> type_filter; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void select_method_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false); + void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false); + void select_from_script(const Ref<Script> &p_script, const String &p_current /*= ""*/); + void select_from_basic_type(Variant::Type p_type, const String &p_current = ""); + void select_from_action(const String &p_type, const String &p_current = ""); + void select_from_instance(Object *p_instance, const String &p_current = ""); + void select_from_visual_script(const String &p_base); + + void show_window(float p_screen_ratio); + + void set_type_filter(const Vector<Variant::Type> &p_type_filter); + + VisualScriptPropertySelector(); +}; + +#endif // VISUALSCRIPT_PROPERTYSELECTOR_H diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 1405fa98b0..00c36ebb47 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -64,7 +64,6 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, String str = "ws://"; String proto_string = ""; - int i = 0; if (p_ssl) str = "wss://"; diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index fd834193dd..28e3ea962f 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -146,9 +146,6 @@ void AudioDriverOpenSL::start() { res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void *)&EngineItf); ERR_FAIL_COND(res != SL_RESULT_SUCCESS); - /* Initialize arrays required[] and iidArray[] */ - SLboolean required[MAX_NUMBER_INTERFACES]; - SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; { const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB }; @@ -188,10 +185,7 @@ void AudioDriverOpenSL::start() { //cntxt.pDataBase = (void*)&pcmData; //cntxt.pData = cntxt.pDataBase; //cntxt.size = sizeof(pcmData); - /* Set arrays required[] and iidArray[] for SEEK interface - (PlayItf is implicit) */ - required[0] = SL_BOOLEAN_TRUE; - iidArray[0] = SL_IID_BUFFERQUEUE; + /* Create the music player */ { diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 3e40b59de9..ee5ae156b7 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -153,7 +153,6 @@ String DirAccessJAndroid::get_current_dir() { bool DirAccessJAndroid::file_exists(String p_file) { - JNIEnv *env = ThreadAndroid::get_env(); String sd; if (current_dir == "") sd = p_file; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index c562a47b00..59e35884d1 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -528,11 +528,9 @@ class EditorExportAndroid : public EditorExportPlatform { bool exported = false; for (int i = 0; i < p_so.tags.size(); ++i) { // shared objects can be fat (compatible with multiple ABIs) - int start_pos = 0; int abi_index = abis.find(p_so.tags[i]); if (abi_index != -1) { exported = true; - start_pos = abi_index + 1; String abi = abis[abi_index]; String dst_path = "lib/" + abi + "/" + p_so.path.get_file(); Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index e6240ad9e9..8bb1c38345 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -880,7 +880,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo const char **cmdline = NULL; int cmdlen = 0; - bool use_apk_expansion = false; if (p_cmdline) { cmdlen = env->GetArrayLength(p_cmdline); if (cmdlen) { @@ -891,9 +890,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i); const char *rawString = env->GetStringUTFChars(string, 0); - if (rawString && strcmp(rawString, "--main-pack") == 0) { - use_apk_expansion = true; - } cmdline[i] = rawString; } diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index e85813605f..b13baf69c2 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -132,7 +132,7 @@ JNIEnv *ThreadAndroid::get_env() { } JNIEnv *env = NULL; - int status = java_vm->AttachCurrentThread(&env, NULL); + java_vm->AttachCurrentThread(&env, NULL); return env; } diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index 4c1e02baf7..5480d30e7a 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -781,7 +781,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p int ret = unzGoToFirstFile(src_pkg_zip); Vector<uint8_t> project_file_data; while (ret == UNZ_OK) { +#if defined(OSX_ENABLED) || defined(X11_ENABLED) bool is_execute = false; +#endif //get filename unz_file_info info; @@ -812,7 +814,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p continue; //ignore! } found_library = true; +#if defined(OSX_ENABLED) || defined(X11_ENABLED) is_execute = true; +#endif file = "godot_ios.a"; } if (file == project_file) { @@ -855,7 +859,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p f->close(); memdelete(f); -#ifdef OSX_ENABLED +#if defined(OSX_ENABLED) || defined(X11_ENABLED) if (is_execute) { // we need execute rights on this file chmod(file.utf8().get_data(), 0755); diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 1cc5c619fe..af96659239 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -115,8 +115,8 @@ def configure(env): ## Flags env.Append(CPPPATH=['#platform/osx']) - env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED']) - env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) + env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED', '-DCOREMIDI_ENABLED']) + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMidi', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) env.Append(LIBS=['pthread']) env.Append(CPPFLAGS=['-mmacosx-version-min=10.9']) diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 3d14a6d4fb..686e3f8c90 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -33,6 +33,7 @@ #include "crash_handler_osx.h" #include "drivers/coreaudio/audio_driver_coreaudio.h" +#include "drivers/coremidi/core_midi.h" #include "drivers/unix/os_unix.h" #include "joypad_osx.h" #include "main/input_default.h" @@ -74,6 +75,7 @@ public: IP_Unix *ip_unix; AudioDriverCoreAudio audio_driver; + MIDIDriverCoreMidi midi_driver; InputDefault *input; JoypadOSX *joypad_osx; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 282f5e2d1b..e77f8b3173 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -1351,6 +1351,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a AudioDriverManager::initialize(p_audio_driver); + midi_driver.open(); + input = memnew(InputDefault); joypad_osx = memnew(JoypadOSX); diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp index d312fbcb12..a158237418 100644 --- a/platform/windows/context_gl_win.cpp +++ b/platform/windows/context_gl_win.cpp @@ -106,9 +106,9 @@ Error ContextGL_Win::initialize() { PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, - 24, + OS::get_singleton()->is_layered_allowed() ? 32 : 24, 0, 0, 0, 0, 0, 0, // Color Bits Ignored - 0, // No Alpha Buffer + OS::get_singleton()->is_layered_allowed() ? 8 : 0, // Alpha Buffer 0, // Shift Bit Ignored 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored diff --git a/platform/windows/detect.py b/platform/windows/detect.py index cacb573236..34fc3e09b5 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -206,7 +206,8 @@ def configure_msvc(env, manual_msvc_config): env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED', 'OPENGL_ENABLED', 'RTAUDIO_ENABLED', 'WASAPI_ENABLED', - 'TYPED_METHOD_BIND', 'WIN32', 'MSVC', + 'WINMIDI_ENABLED', 'TYPED_METHOD_BIND', + 'WIN32', 'MSVC', {'WINVER' : '$target_win_version', '_WIN32_WINNT': '$target_win_version'}]) if env["bits"] == "64": diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index ac37e1246d..e083fd7323 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1219,6 +1219,10 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int AudioDriverManager::initialize(p_audio_driver); +#ifdef WINMIDI_ENABLED + driver_midi.open(); +#endif + TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; @@ -1347,6 +1351,10 @@ void OS_Windows::set_main_loop(MainLoop *p_main_loop) { void OS_Windows::finalize() { +#ifdef WINMIDI_ENABLED + driver_midi.close(); +#endif + if (main_loop) memdelete(main_loop); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index b99d3e3422..69c7d851b8 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -35,6 +35,7 @@ #include "crash_handler_win.h" #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/wasapi/audio_driver_wasapi.h" +#include "drivers/winmidi/win_midi.h" #include "os/input.h" #include "os/os.h" #include "power_windows.h" @@ -144,6 +145,9 @@ class OS_Windows : public OS { #ifdef XAUDIO2_ENABLED AudioDriverXAudio2 driver_xaudio2; #endif +#ifdef WINMIDI_ENABLED + MIDIDriverWinMidi driver_midi; +#endif CrashHandler crash_handler; diff --git a/platform/x11/detect.py b/platform/x11/detect.py index feaa2e598f..6a7a426804 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -249,7 +249,7 @@ def configure(env): if (os.system("pkg-config --exists alsa") == 0): # 0 means found print("Enabling ALSA") - env.Append(CPPFLAGS=["-DALSA_ENABLED"]) + env.Append(CPPFLAGS=["-DALSA_ENABLED", "-DALSAMIDI_ENABLED"]) env.ParseConfig('pkg-config alsa --cflags --libs') else: print("ALSA libraries not found, disabling driver") diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index af0e02173f..260ce57732 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -342,6 +342,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a AudioDriverManager::initialize(p_audio_driver); +#ifdef ALSAMIDI_ENABLED + driver_alsamidi.open(); +#endif + ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE); @@ -606,6 +610,9 @@ void OS_X11::finalize() { memdelete(debugger_connection_console); } */ +#ifdef ALSAMIDI_ENABLED + driver_alsamidi.close(); +#endif #ifdef JOYDEV_ENABLED memdelete(joypad); diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index b67bc824be..44455a2d8d 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -38,6 +38,7 @@ #include "servers/visual_server.h" //#include "servers/visual/visual_server_wrap_mt.h" #include "drivers/alsa/audio_driver_alsa.h" +#include "drivers/alsamidi/alsa_midi.h" #include "drivers/pulseaudio/audio_driver_pulseaudio.h" #include "joypad_linux.h" #include "main/input_default.h" @@ -168,6 +169,10 @@ class OS_X11 : public OS_Unix { AudioDriverALSA driver_alsa; #endif +#ifdef ALSAMIDI_ENABLED + MIDIDriverALSAMidi driver_alsamidi; +#endif + #ifdef PULSEAUDIO_ENABLED AudioDriverPulseAudio driver_pulseaudio; #endif diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 47326b9be2..a035d9021f 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -411,6 +411,9 @@ void CanvasItem::_enter_canvas() { if (canvas_layer) { break; } + if (Object::cast_to<Viewport>(n)) { + break; + } n = n->get_parent(); } diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 74ad3e79d0..a3f1b25e05 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -292,7 +292,6 @@ void LineBuilder::build() { color1 = gradient->get_color_at_offset(current_distance1 / total_distance); } if (texture_mode == Line2D::LINE_TEXTURE_TILE) { - uvx0 = current_distance0 / (width * tile_aspect); uvx1 = current_distance1 / (width * tile_aspect); } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) { uvx0 = current_distance0 / total_distance; @@ -306,7 +305,6 @@ void LineBuilder::build() { u0 = u1; f0 = f1; pos0 = pos1; - current_distance0 = current_distance1; if (intersection_result == SEGMENT_INTERSECT) { if (current_joint_mode == Line2D::LINE_JOINT_SHARP) { pos_up0 = pos_up1; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 9f7503577b..81fdc32788 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -150,7 +150,7 @@ void Tween::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - if (!processing) { + if (!is_active()) { //make sure that a previous process state was not saved //only process if "processing" is set set_physics_process_internal(false); @@ -164,7 +164,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_PHYSICS) break; - if (processing) + if (is_active()) _tween_process(get_process_delta_time()); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -172,7 +172,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_IDLE) break; - if (processing) + if (is_active()) _tween_process(get_physics_process_delta_time()); } break; case NOTIFICATION_EXIT_TREE: { @@ -201,7 +201,6 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all); ClassDB::bind_method(D_METHOD("stop", "object", "key"), &Tween::stop, DEFVAL("")); ClassDB::bind_method(D_METHOD("stop_all"), &Tween::stop_all); - ClassDB::bind_method(D_METHOD("is_stopped"), &Tween::is_stopped); ClassDB::bind_method(D_METHOD("resume", "object", "key"), &Tween::resume, DEFVAL("")); ClassDB::bind_method(D_METHOD("resume_all"), &Tween::resume_all); ClassDB::bind_method(D_METHOD("remove", "object", "key"), &Tween::remove, DEFVAL("")); @@ -522,8 +521,8 @@ void Tween::_tween_process(float p_delta) { pending_update++; // if repeat and all interpolates was finished then reset all interpolates + bool all_finished = true; if (repeat) { - bool all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -539,9 +538,12 @@ void Tween::_tween_process(float p_delta) { reset_all(); } + all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { InterpolateData &data = E->get(); + all_finished = all_finished && data.finish; + if (!data.active || data.finish) continue; @@ -555,8 +557,8 @@ void Tween::_tween_process(float p_delta) { continue; else if (prev_delaying) { - emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); _apply_tween_value(data, data.initial_val); + emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); } if (data.elapsed > (data.delay + data.duration)) { @@ -603,32 +605,29 @@ void Tween::_tween_process(float p_delta) { } } else { Variant result = _run_equation(data); - emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); _apply_tween_value(data, result); + emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); } if (data.finish) { _apply_tween_value(data, data.final_val); + data.elapsed = 0; emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false)); // not repeat mode, remove completed action if (!repeat) call_deferred("_remove", object, NodePath(Vector<StringName>(), data.key, false), true); - } + } else if (!repeat) + all_finished = all_finished && data.finish; } pending_update--; + + if (all_finished) + set_active(false); } void Tween::set_tween_process_mode(TweenProcessMode p_mode) { - if (tween_process_mode == p_mode) - return; - - bool pr = processing; - if (pr) - _set_process(false); tween_process_mode = p_mode; - if (pr) - _set_process(true); } Tween::TweenProcessMode Tween::get_tween_process_mode() const { @@ -636,32 +635,21 @@ Tween::TweenProcessMode Tween::get_tween_process_mode() const { return tween_process_mode; } -void Tween::_set_process(bool p_process, bool p_force) { - - if (processing == p_process && !p_force) - return; - - switch (tween_process_mode) { - - case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break; - case TWEEN_PROCESS_IDLE: set_process_internal(p_process && active); break; - } - - processing = p_process; -} - bool Tween::is_active() const { - return active; + return is_processing_internal() || is_physics_processing_internal(); } void Tween::set_active(bool p_active) { - if (active == p_active) + if (is_active() == p_active) return; - active = p_active; - _set_process(processing, true); + switch (tween_process_mode) { + + case TWEEN_PROCESS_IDLE: set_process_internal(p_active); break; + case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_active); break; + } } bool Tween::is_repeat() const { @@ -687,7 +675,6 @@ float Tween::get_speed_scale() const { bool Tween::start() { set_active(true); - _set_process(true); return true; } @@ -744,14 +731,9 @@ bool Tween::stop(Object *p_object, StringName p_key) { return true; } -bool Tween::is_stopped() const { - return tell() >= get_runtime(); -} - bool Tween::stop_all() { set_active(false); - _set_process(false); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -766,7 +748,6 @@ bool Tween::stop_all() { bool Tween::resume(Object *p_object, StringName p_key) { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -785,7 +766,6 @@ bool Tween::resume(Object *p_object, StringName p_key) { bool Tween::resume_all() { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -834,7 +814,6 @@ bool Tween::remove_all() { return true; } set_active(false); - _set_process(false); interpolates.clear(); return true; } @@ -1425,8 +1404,6 @@ Tween::Tween() { //String autoplay; tween_process_mode = TWEEN_PROCESS_IDLE; - processing = false; - active = false; repeat = false; speed_scale = 1; pending_update = 0; diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 36094bf294..9997349c64 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -104,8 +104,6 @@ private: String autoplay; TweenProcessMode tween_process_mode; - bool processing; - bool active; bool repeat; float speed_scale; mutable int pending_update; @@ -133,7 +131,6 @@ private: bool _apply_tween_value(InterpolateData &p_data, Variant &value); void _tween_process(float p_delta); - void _set_process(bool p_process, bool p_force = false); void _remove(Object *p_object, StringName p_key, bool first_only); protected: @@ -162,7 +159,6 @@ public: bool reset_all(); bool stop(Object *p_object, StringName p_key); bool stop_all(); - bool is_stopped() const; bool resume(Object *p_object, StringName p_key); bool resume_all(); bool remove(Object *p_object, StringName p_key); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 17c349858f..12aeed1520 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2972,7 +2972,6 @@ Control::Control() { data.SI = NULL; data.MI = NULL; data.RI = NULL; - data.modal = false; data.theme_owner = NULL; data.modal_exclusive = false; data.default_cursor = CURSOR_ARROW; diff --git a/scene/gui/control.h b/scene/gui/control.h index 94231867d7..6bea04345b 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -182,7 +182,6 @@ private: Control *parent; ObjectID drag_owner; - bool modal; bool modal_exclusive; uint64_t modal_frame; //frame used to put something as modal Ref<Theme> theme; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index e2c730a56e..d95ec9e495 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -268,6 +268,10 @@ void GraphEdit::remove_child_notify(Node *p_child) { void GraphEdit::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal"); + port_grab_distance_vertical = get_constant("port_grab_distance_vertical"); + } if (p_what == NOTIFICATION_READY) { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); @@ -343,8 +347,6 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { Ref<Texture> port = get_icon("port", "GraphNode"); - float grab_r_extend = 2.0; - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -354,14 +356,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) + if (create_hot_zone(pos).has_point(p_point)) return true; } for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) { + if (create_hot_zone(pos).has_point(p_point)) { return true; } } @@ -372,13 +374,11 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { - float grab_r_extend = 2.0; Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos(mb->get_position().x, mb->get_position().y); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -388,7 +388,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect @@ -435,8 +435,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect @@ -492,7 +491,6 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos = mm->get_position(); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -504,7 +502,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; @@ -519,7 +517,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -559,6 +557,10 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } +Rect2 GraphEdit::create_hot_zone(const Vector2 &pos) { + return Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2); +} + template <class Vector2> static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, Vector2 start, Vector2 control_1, Vector2 control_2, Vector2 end) { /* Formula from Wikipedia article on Bezier curves. */ diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 14789001e4..64ba18681e 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -81,6 +81,9 @@ private: HScrollBar *h_scroll; VScrollBar *v_scroll; + float port_grab_distance_horizontal; + float port_grab_distance_vertical; + bool connecting; String connecting_from; bool connecting_out; @@ -127,6 +130,9 @@ private: Control *connections_layer; GraphEditFilter *top_layer; void _top_layer_input(const Ref<InputEvent> &p_ev); + + Rect2 create_hot_zone(const Vector2 &pos); + void _top_layer_draw(); void _connections_layer_draw(); void _update_scroll_offset(); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 2356444ecb..0636accfee 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -43,7 +43,6 @@ class MenuButton : public Button { bool clicked; bool disable_shortcuts; PopupMenu *popup; - virtual void pressed(); void _unhandled_key_input(Ref<InputEvent> p_event); Array _get_items() const; @@ -55,6 +54,8 @@ protected: static void _bind_methods(); public: + virtual void pressed(); + PopupMenu *get_popup() const; void set_disable_shortcuts(bool p_disabled); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 90cb475a7b..cccd1bd197 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -337,10 +337,6 @@ void TextEdit::_update_scrollbars() { int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1; int visible_rows = get_visible_rows(); - int first_vis_line = get_first_visible_line(); - int wi; - int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi)); - int total_rows = get_total_visible_rows(); if (scroll_past_end_of_file_enabled) { total_rows += visible_rows - 1; @@ -1673,7 +1669,6 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co rows /= get_row_height(); rows += get_v_scroll_offset(); int first_vis_line = get_first_visible_line(); - int last_vis_line = get_last_visible_line(); int row = first_vis_line + Math::floor(rows); int wrap_index = 0; @@ -3800,7 +3795,6 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { } // line ends before hit wrap_at; add this word to the substring wrap_substring += word_str; - px += word_px; lines.push_back(wrap_substring); return lines; } @@ -5519,9 +5513,8 @@ int TextEdit::get_last_visible_line() const { int TextEdit::get_last_visible_line_wrap_index() const { int first_vis_line = get_first_visible_line(); - int last_vis_line = 0; int wi; - last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1; + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi); return wi; } diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 82d983184b..6e4fe88dbf 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -309,15 +309,23 @@ void TextureProgress::_notification(int p_what) { draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_CLOCKWISE: - case FILL_COUNTER_CLOCKWISE: { + case FILL_COUNTER_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { Rect2 region = Rect2(Point2(), s); draw_texture_rect_region(progress, region, region, tint_progress); } else if (val != 0) { Array pts; - float direction = mode == FILL_CLOCKWISE ? 1 : -1; - float start = rad_init_angle / 360; + float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1; + float start; + + if (mode == FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE) { + start = rad_init_angle / 360 - val / 2; + } else { + start = rad_init_angle / 360; + } + float end = start + direction * val; pts.append(start); pts.append(end); @@ -351,6 +359,14 @@ void TextureProgress::_notification(int p_what) { draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2); } } break; + case FILL_BILINEAR_LEFT_AND_RIGHT: { + Rect2 region = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, region, tint_progress); + } break; + case FILL_BILINEAR_TOP_AND_BOTTOM: { + Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, region, tint_progress); + } break; default: draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } @@ -364,7 +380,7 @@ void TextureProgress::_notification(int p_what) { } void TextureProgress::set_fill_mode(int p_fill) { - ERR_FAIL_INDEX(p_fill, 6); + ERR_FAIL_INDEX(p_fill, 9); mode = (FillMode)p_fill; update(); } @@ -446,7 +462,7 @@ void TextureProgress::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_under_texture", "get_under_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture"); - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode"); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom), Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); ADD_GROUP("Tint", "tint_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over"); @@ -468,6 +484,9 @@ void TextureProgress::_bind_methods() { BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP); BIND_ENUM_CONSTANT(FILL_CLOCKWISE); BIND_ENUM_CONSTANT(FILL_COUNTER_CLOCKWISE); + BIND_ENUM_CONSTANT(FILL_BILINEAR_LEFT_AND_RIGHT); + BIND_ENUM_CONSTANT(FILL_BILINEAR_TOP_AND_BOTTOM); + BIND_ENUM_CONSTANT(FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE); } TextureProgress::TextureProgress() { diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h index 34158b5db5..a11e55234a 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress.h @@ -52,7 +52,10 @@ public: FILL_TOP_TO_BOTTOM, FILL_BOTTOM_TO_TOP, FILL_CLOCKWISE, - FILL_COUNTER_CLOCKWISE + FILL_COUNTER_CLOCKWISE, + FILL_BILINEAR_LEFT_AND_RIGHT, + FILL_BILINEAR_TOP_AND_BOTTOM, + FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE }; void set_fill_mode(int p_fill); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 1d27612766..6ab1bf3d58 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2426,14 +2426,23 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { int col, h, section; TreeItem *it = _find_item_at_pos(root, mpos, col, h, section); - if ((drop_mode_flags && it != drop_mode_over) || section != drop_mode_section) { - drop_mode_over = it; - drop_mode_section = section; - update(); + if (drop_mode_flags) { + if (it != drop_mode_over) { + drop_mode_over = it; + update(); + } + if (it && section != drop_mode_section) { + drop_mode_section = section; + update(); + } } - if (it != cache.hover_item || col != cache.hover_cell) { + if (it != cache.hover_item) { cache.hover_item = it; + update(); + } + + if (it && col != cache.hover_cell) { cache.hover_cell = col; update(); } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 9013d276c7..573c401290 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -41,7 +41,10 @@ #include "scene/3d/spatial.h" #include "scene/gui/control.h" #include "scene/gui/label.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" #include "scene/gui/panel_container.h" +#include "scene/gui/popup_menu.h" #include "scene/main/timer.h" #include "scene/resources/mesh.h" #include "scene/scene_string_names.h" @@ -1853,8 +1856,32 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (gui.drag_data.get_type() == Variant::NIL && over && !gui.modal_stack.empty()) { Control *top = gui.modal_stack.back()->get(); + if (over != top && !top->is_a_parent_of(over)) { - over = NULL; //nothing can be found outside the modal stack + + PopupMenu *popup_menu = Object::cast_to<PopupMenu>(top); + MenuButton *popup_menu_parent; + MenuButton *menu_button = Object::cast_to<MenuButton>(over); + + if (popup_menu) + popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent()); + + // If the mouse is over a menu button, this menu will open automatically + // if there is already a pop-up menu open at the same hierarchical level. + if (popup_menu_parent && menu_button && + popup_menu_parent->get_icon().is_null() && + menu_button->get_icon().is_null() && + (popup_menu->get_parent()->get_parent()->is_a_parent_of(menu_button) || + menu_button->get_parent()->is_a_parent_of(popup_menu))) { + + popup_menu->notification(Control::NOTIFICATION_MODAL_CLOSE); + popup_menu->_modal_stack_remove(); + popup_menu->hide(); + + menu_button->pressed(); + } else { + over = NULL; //nothing can be found outside the modal stack + } } } diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 0d69c037fe..a4fd35304a 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -156,6 +156,7 @@ #include "scene/resources/sky_box.h" #include "scene/resources/sphere_shape.h" #include "scene/resources/surface_tool.h" +#include "scene/resources/text_file.h" #include "scene/resources/texture.h" #include "scene/resources/tile_set.h" #include "scene/resources/video_stream.h" @@ -617,6 +618,8 @@ void register_scene_types() { ClassDB::register_class<BitmapFont>(); ClassDB::register_class<Curve>(); + ClassDB::register_class<TextFile>(); + ClassDB::register_class<DynamicFontData>(); ClassDB::register_class<DynamicFont>(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index fe12e2f5f6..601f6fb558 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -883,6 +883,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("logo", "Icons", make_icon(logo_png)); + // Visual Node Ports + theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale); + theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); + // Theme default_icon = make_icon(error_icon_png); diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index e5d463d391..eb7d517841 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -211,9 +211,9 @@ Error DynamicFontAtSize::_load() { scale_color_font = float(id.size) / face->available_sizes[i].width; } } - error = FT_Select_Size(face, best_match); + FT_Select_Size(face, best_match); } else { - error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); + FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); } ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font; diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp new file mode 100644 index 0000000000..e2fe0adfc5 --- /dev/null +++ b/scene/resources/text_file.cpp @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* text_file.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "text_file.h" + +#include "os/file_access.h" + +bool TextFile::has_text() const { + return text != ""; +} + +String TextFile::get_text() const { + return text; +} + +void TextFile::set_text(const String &p_code) { + text = p_code; +} + +void TextFile::reload_from_file() { + load_text(path); +} + +Error TextFile::load_text(const String &p_path) { + + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + if (err) { + ERR_FAIL_COND_V(err, err); + } + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); + w[len] = 0; + + String s; + if (s.parse_utf8((const char *)w.ptr())) { + ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + ERR_FAIL_V(ERR_INVALID_DATA); + } + text = s; + path = p_path; + return OK; +} diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h new file mode 100644 index 0000000000..40b648eebb --- /dev/null +++ b/scene/resources/text_file.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* text_file.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXTFILE_H +#define TEXTFILE_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" + +class TextFile : public Resource { + + GDCLASS(TextFile, Resource) + +private: + String text; + String path; + +public: + virtual bool has_text() const; + virtual String get_text() const; + virtual void set_text(const String &p_code); + virtual void reload_from_file(); + + void set_file_path(const String &p_path) { path = p_path; } + Error load_text(const String &p_path); +}; + +#endif // TEXTFILE_H diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 58057cda0c..f9df6b4304 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -923,6 +923,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("tile_get_normal_map", "id"), &TileSet::tile_get_normal_map); ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material); ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material); + ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate); + ClassDB::bind_method(D_METHOD("tile_get_modulate", "id"), &TileSet::tile_get_modulate); ClassDB::bind_method(D_METHOD("tile_set_texture_offset", "id", "texture_offset"), &TileSet::tile_set_texture_offset); ClassDB::bind_method(D_METHOD("tile_get_texture_offset", "id"), &TileSet::tile_get_texture_offset); ClassDB::bind_method(D_METHOD("tile_set_region", "id", "region"), &TileSet::tile_set_region); diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp index 9faa4056c3..3414351681 100644 --- a/servers/audio/audio_rb_resampler.cpp +++ b/servers/audio/audio_rb_resampler.cpp @@ -100,6 +100,8 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i if (C == 6) { + // FIXME: Lot of unused assignments here, but it seems like intermediate calculations + // should be done as for C == 2 (C == 4 also has some unused assignments). float v0 = rb[(pos * 6) + 0]; float v1 = rb[(pos * 6) + 1]; float v2 = rb[(pos * 6) + 2]; diff --git a/servers/physics/collision_solver_sat.cpp b/servers/physics/collision_solver_sat.cpp index e587485fcb..44b7c9ac34 100644 --- a/servers/physics/collision_solver_sat.cpp +++ b/servers/physics/collision_solver_sat.cpp @@ -217,8 +217,6 @@ static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_ // generate contacts //Plane plane_A(p_points_A[0],p_points_A[1],p_points_A[2]); - int added = 0; - for (int i = 0; i < clipbuf_len; i++) { real_t d = plane_B.distance_to(clipbuf_src[i]); @@ -233,7 +231,6 @@ static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_ continue; p_callback->call(clipbuf_src[i], closest_B); - added++; } } diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 8e21341cee..1f3319dc04 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -187,16 +187,14 @@ RID VisualServer::_make_test_cube() { PoolVector<float> tangents; PoolVector<Vector3> uvs; - int vtx_idx = 0; -#define ADD_VTX(m_idx) \ - vertices.push_back(face_points[m_idx]); \ - normals.push_back(normal_points[m_idx]); \ - tangents.push_back(normal_points[m_idx][1]); \ - tangents.push_back(normal_points[m_idx][2]); \ - tangents.push_back(normal_points[m_idx][0]); \ - tangents.push_back(1.0); \ - uvs.push_back(Vector3(uv_points[m_idx * 2 + 0], uv_points[m_idx * 2 + 1], 0)); \ - vtx_idx++; +#define ADD_VTX(m_idx) \ + vertices.push_back(face_points[m_idx]); \ + normals.push_back(normal_points[m_idx]); \ + tangents.push_back(normal_points[m_idx][1]); \ + tangents.push_back(normal_points[m_idx][2]); \ + tangents.push_back(normal_points[m_idx][0]); \ + tangents.push_back(1.0); \ + uvs.push_back(Vector3(uv_points[m_idx * 2 + 0], uv_points[m_idx * 2 + 1], 0)); for (int i = 0; i < 6; i++) { |