diff options
37 files changed, 559 insertions, 157 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7ac70a4367..09ff2454ee 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,56 +2,181 @@ # Each line is a file pattern followed by one or more owners. # Owners can be @users, @org/teams or emails -/doc/ @godotengine/documentation -doc_classes/* @godotengine/documentation - -# Rendering -/drivers/gl_context/ @reduz -/drivers/gles2/ @reduz -/drivers/gles3/ @reduz - -# Audio -/drivers/alsa/ @marcelofg55 -/drivers/alsamidi/ @marcelofg55 -/drivers/coreaudio/ @marcelofg55 -/drivers/coremidi/ @marcelofg55 -/drivers/pulseaudio/ @marcelofg55 -/drivers/wasapi/ @marcelofg55 -/drivers/winmidi/ @marcelofg55 -/drivers/xaudio2/ @marcelofg55 - -/drivers/unix/ @hpvb -/drivers/windows/ @hpvb - -/editor/icons/ @djrm - -/misc/ @akien-mga - -/modules/bullet/ @AndreaCatania -/modules/csg/ @BastiaanOlij -/modules/enet/ @godotengine/network -/modules/gdnative/*arvr/ @BastiaanOlij -/modules/gdnative/text/ @bruvzg -/modules/gdscript/ @vnen -/modules/mbedtls/ @godotengine/network -/modules/mobile_vr/ @BastiaanOlij -/modules/mono/ @neikeq -/modules/mono/glue/GodotSharp @aaronfranke -/modules/opensimplex/ @JFonS -/modules/regex/ @LeeZH -/modules/text_server_*/ @bruvzg -/modules/upnp/ @godotengine/network -/modules/websocket/ @godotengine/network - -/platform/javascript/ @eska014 -/platform/uwp/ @vnen - -/scene/resources/font.* @bruvzg -/scene/resources/text_line.* @bruvzg -/scene/resources/text_paragraph.* @bruvzg - -/servers/physics*/ @reduz @AndreaCatania -/servers/text_server.* @bruvzg -/servers/visual*/ @reduz - -/thirdparty/ @akien-mga +# Buildsystem + +.* @godotengine/buildsystem +.github/ @godotengine/buildsystem +*.py @godotengine/buildsystem +SConstruct @godotengine/buildsystem +SCsub @godotengine/buildsystem + +# Core + +/core/ @godotengine/core +/core/crypto/ @godotengine/network +/core/debugger/ @godotengine/debugger +/core/input/ @godotengine/input + +# Doc + +/doc/ @godotengine/documentation +doc_classes/* @godotengine/documentation + +# Drivers + +## Audio +/drivers/alsa/ @godotengine/audio +/drivers/alsamidi/ @godotengine/audio +/drivers/coreaudio/ @godotengine/audio +/drivers/coremidi/ @godotengine/audio +/drivers/pulseaudio/ @godotengine/audio +/drivers/wasapi/ @godotengine/audio +/drivers/winmidi/ @godotengine/audio +/drivers/xaudio2/ @godotengine/audio + +## Rendering +/drivers/dummy/ @godotengine/rendering +/drivers/spirv-reflect/ @godotengine/rendering +/drivers/vulkan/ @godotengine/rendering + +## OS +/drivers/unix/ @godotengine/_platforms +/drivers/windows/ @godotengine/windows + +## Misc +/drivers/png/ @godotengine/import + +# Editor + +/editor/*debugger* @godotengine/debugger +/editor/icons/ @godotengine/usability +/editor/import/ @godotengine/import +/editor/plugins/*2d_*.* @godotengine/2d-editor +/editor/plugins/*3d_*.* @godotengine/3d-editor +/editor/plugins/script_*.* @godotengine/script-editor +/editor/plugins/*shader*.* @godotengine/shaders +/editor/code_editor.* @godotengine/script-editor +/editor/*dock*.* @godotengine/docks +/editor/*shader*.* @godotengine/shaders + +# Main + +/main/ @godotengine/core + +# Misc + +/misc/ @godotengine/buildsystem + +# Modules + +## Audio (+ video) +/modules/minimp3/ @godotengine/audio +/modules/ogg/ @godotengine/audio +/modules/opus/ @godotengine/audio +/modules/stb_vorbis/ @godotengine/audio +/modules/theora/ @godotengine/audio +/modules/vorbis/ @godotengine/audio +/modules/webm/ @godotengine/audio + +## Import +/modules/basis_universal/ @godotengine/import +/modules/bmp/ @godotengine/import +/modules/cvtt/ @godotengine/import +/modules/dds/ @godotengine/import +/modules/etc/ @godotengine/import +/modules/fbx/ @godotengine/import +/modules/gltf/ @godotengine/import +/modules/hdr/ @godotengine/import +/modules/jpg/ @godotengine/import +/modules/pvr/ @godotengine/import +/modules/squish/ @godotengine/import +/modules/svg/ @godotengine/import +/modules/tga/ @godotengine/import +/modules/tinyexr/ @godotengine/import +/modules/webp/ @godotengine/import + +## Network +/modules/enet/ @godotengine/network +/modules/mbedtls/ @godotengine/network +/modules/upnp/ @godotengine/network +/modules/webrtc/ @godotengine/network +/modules/websocket/ @godotengine/network + +## Rendering +/modules/denoise/ @godotengine/rendering +/modules/glslang/ @godotengine/rendering +/modules/lightmapper_rd/ @godotengine/rendering +/modules/meshoptimizer/ @godotengine/rendering +/modules/vhacd/ @godotengine/rendering +/modules/xatlas_unwrap/ @godotengine/rendering + +## Scripting +/modules/gdnative/ @godotengine/gdnative +/modules/gdscript/ @godotengine/gdscript +/modules/jsonrpc/ @godotengine/gdscript +/modules/mono/ @godotengine/mono +/modules/visual_script/ @godotengine/visualscript + +## Text +/modules/freetype/ @godotengine/buildsystem +/modules/gdnative/text/ @godotengine/gui-nodes +/modules/text_server_adv/ @godotengine/gui-nodes +/modules/text_server_fb/ @godotengine/gui-nodes + +## XR +/modules/camera/ @godotengine/xr +/modules/gdnative/xr/ @godotengine/xr +/modules/mobile_vr/ @godotengine/xr +/modules/webxr/ @godotengine/xr + +## Misc +/modules/bullet/ @godotengine/physics +/modules/csg/ @godotengine/3d-nodes +/modules/gdnavigation/ @godotengine/navigation +/modules/gridmap/ @godotengine/3d-nodes +/modules/opensimplex/ @godotengine/3d-nodes +/modules/regex/ @godotengine/core + +# Platform + +/platform/android/ @godotengine/android +/platform/iphone/ @godotengine/ios +/platform/javascript/ @godotengine/html5 +/platform/linuxbsd/ @godotengine/linux-bsd +/platform/osx/ @godotengine/macos +/platform/uwp/ @godotengine/uwp +/platform/windows/ @godotengine/windows + +# Scene + +/scene/2d/ @godotengine/2d-nodes +/scene/3d/ @godotengine/3d-nodes +/scene/animation/ @godotengine/animation +/scene/audio/ @godotengine/audio +/scene/debugger/ @godotengine/debugger +/scene/gui/ @godotengine/gui-nodes +/scene/main/ @godotengine/core +/scene/resources/default_theme/ @godotengine/gui-nodes +/scene/resources/font.* @godotengine/gui-nodes +/scene/resources/text_line.* @godotengine/gui-nodes +/scene/resources/text_paragraph.* @godotengine/gui-nodes +/scene/resources/visual_shader*.* @godotengine/shaders + +# Servers + +/servers/audio* @godotengine/audio +/servers/camera* @godotengine/xr +/servers/display_server.* @godotengine/_platforms +/servers/navigation_server*.* @godotengine/navigation +/servers/physics* @godotengine/physics +/servers/rendering* @godotengine/rendering +/servers/text_server.* @godotengine/gui-nodes +/servers/xr* @godotengine/xr + +# Tests + +/tests/ @godotengine/tests + +# Thirdparty + +/thirdparty/ @godotengine/buildsystem diff --git a/core/input/input.cpp b/core/input/input.cpp index 047aeb47fd..48e573626d 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -97,11 +97,11 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); - ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action"), &Input::get_action_strength); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact"), &Input::is_action_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact"), &Input::is_action_just_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact"), &Input::is_action_just_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact"), &Input::get_action_strength, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact"), &Input::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis); ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f)); ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); @@ -240,16 +240,20 @@ bool Input::is_joy_button_pressed(int p_device, int p_button) const { return joy_buttons_pressed.has(_combine_device(p_button, p_device)); } -bool Input::is_action_pressed(const StringName &p_action) const { - return action_state.has(p_action) && action_state[p_action].pressed; +bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { + return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true); } -bool Input::is_action_just_pressed(const StringName &p_action) const { +bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -257,12 +261,16 @@ bool Input::is_action_just_pressed(const StringName &p_action) const { } } -bool Input::is_action_just_released(const StringName &p_action) const { +bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -270,21 +278,29 @@ bool Input::is_action_just_released(const StringName &p_action) const { } } -float Input::get_action_strength(const StringName &p_action) const { +float Input::get_action_strength(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().strength; } -float Input::get_action_raw_strength(const StringName &p_action) const { +float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().raw_strength; } @@ -590,14 +606,15 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { if (InputMap::get_singleton()->event_is_action(p_event, E->key())) { - // Save the action's state - if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) { + // If not echo and action pressed state has changed + if (!p_event->is_echo() && is_action_pressed(E->key(), false) != p_event->is_action_pressed(E->key())) { Action action; action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.process_frame = Engine::get_singleton()->get_process_frames(); action.pressed = p_event->is_action_pressed(E->key()); action.strength = 0.0f; action.raw_strength = 0.0f; + action.exact = InputMap::get_singleton()->event_is_action(p_event, E->key(), true); action_state[E->key()] = action; } action_state[E->key()].strength = p_event->get_action_strength(E->key()); diff --git a/core/input/input.h b/core/input/input.h index 445b7ff0cf..0e3af42381 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -116,6 +116,7 @@ private: uint64_t physics_frame; uint64_t process_frame; bool pressed; + bool exact; float strength; float raw_strength; }; @@ -261,11 +262,11 @@ public: bool is_key_pressed(int p_keycode) const; bool is_mouse_button_pressed(int p_button) const; bool is_joy_button_pressed(int p_device, int p_button) const; - bool is_action_pressed(const StringName &p_action) const; - bool is_action_just_pressed(const StringName &p_action) const; - bool is_action_just_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - float get_action_raw_strength(const StringName &p_action) const; + bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; + bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const; + bool is_action_just_released(const StringName &p_action, bool p_exact = false) const; + float get_action_strength(const StringName &p_action, bool p_exact = false) const; + float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const; float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const; Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const; diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index c91dc4d067..b1c8f1ad66 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -44,31 +44,31 @@ int InputEvent::get_device() const { return device; } -bool InputEvent::is_action(const StringName &p_action) const { - return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action); +bool InputEvent::is_action(const StringName &p_action, bool p_exact_match) const { + return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match); } -bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo) const { +bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && pressed && (p_allow_echo || !is_echo()); } -bool InputEvent::is_action_released(const StringName &p_action) const { +bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && !pressed; } -float InputEvent::get_action_strength(const StringName &p_action) const { +float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match) const { float strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, &strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, &strength, nullptr); return valid ? strength : 0.0f; } -float InputEvent::get_action_raw_strength(const StringName &p_action) const { +float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exact_match) const { float raw_strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, nullptr, &raw_strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, nullptr, &raw_strength); return valid ? raw_strength : 0.0f; } @@ -100,10 +100,10 @@ void InputEvent::_bind_methods() { ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device); ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device); - ClassDB::bind_method(D_METHOD("is_action", "action"), &InputEvent::is_action); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo"), &InputEvent::is_action_pressed, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("is_action_released", "action"), &InputEvent::is_action_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputEvent::get_action_strength); + ClassDB::bind_method(D_METHOD("is_action", "action", "exact_match"), &InputEvent::is_action, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo", "exact_match"), &InputEvent::is_action_pressed, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match"), &InputEvent::is_action_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &InputEvent::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed); ClassDB::bind_method(D_METHOD("is_echo"), &InputEvent::is_echo); diff --git a/core/input/input_event.h b/core/input/input_event.h index a354119cf9..1ce1fad9c2 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -128,11 +128,11 @@ public: void set_device(int p_device); int get_device() const; - bool is_action(const StringName &p_action) const; - bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false) const; - bool is_action_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - float get_action_raw_strength(const StringName &p_action) const; + bool is_action(const StringName &p_action, bool p_exact_match = false) const; + bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const; + bool is_action_released(const StringName &p_action, bool p_exact_match = false) const; + float get_action_strength(const StringName &p_action, bool p_exact_match = false) const; + float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const; // To be removed someday, since they do not make sense for all events virtual bool is_pressed() const; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 53ed925c1f..c03e64eaf4 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -50,7 +50,7 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events); ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events); - ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action); + ClassDB::bind_method(D_METHOD("event_is_action", "event", "action", "exact_match"), &InputMap::event_is_action, DEFVAL(false)); ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings); } @@ -95,7 +95,7 @@ List<StringName> InputMap::get_actions() const { return actions; } -List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { @@ -106,7 +106,9 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re int device = e->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { + if (p_exact_match && e->shortcut_match(p_event)) { + return E; + } else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { return E; } } @@ -134,7 +136,7 @@ void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); - if (_find_event(input_map[p_action], p_event)) { + if (_find_event(input_map[p_action], p_event, true)) { return; // Already addded. } @@ -143,13 +145,13 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); - return (_find_event(input_map[p_action], p_event) != nullptr); + return (_find_event(input_map[p_action], p_event, true) != nullptr); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); - List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event); + List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { input_map[p_action].inputs.erase(E); if (Input::get_singleton()->is_action_pressed(p_action)) { @@ -185,11 +187,11 @@ const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_act return &E->get().inputs; } -bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const { - return event_get_action_status(p_event, p_action); +bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const { + return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { Map<StringName, Action>::Element *E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); @@ -207,7 +209,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str bool pressed; float strength; float raw_strength; - List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, &pressed, &strength, &raw_strength); + List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, p_exact_match, &pressed, &strength, &raw_strength); if (event != nullptr) { if (p_pressed != nullptr) { *p_pressed = pressed; diff --git a/core/input/input_map.h b/core/input/input_map.h index 1646e7fadb..28c692c7ba 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -54,7 +54,7 @@ private: mutable Map<StringName, Action> input_map; - List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; Array _action_get_events(const StringName &p_action); Array _get_actions(); @@ -78,8 +78,8 @@ public: void action_erase_events(const StringName &p_action); const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); - bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const; - bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; + bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; const Map<StringName, Action> &get_action_map() const; void load_from_project_settings(); diff --git a/core/math/vector3.h b/core/math/vector3.h index 6b4ff3f9a8..377581bb45 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -110,6 +110,7 @@ struct Vector3 { _FORCE_INLINE_ Vector3 project(const Vector3 &p_to) const; _FORCE_INLINE_ real_t angle_to(const Vector3 &p_to) const; + _FORCE_INLINE_ real_t signed_angle_to(const Vector3 &p_to, const Vector3 &p_axis) const; _FORCE_INLINE_ Vector3 direction_to(const Vector3 &p_to) const; _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; @@ -230,6 +231,13 @@ real_t Vector3::angle_to(const Vector3 &p_to) const { return Math::atan2(cross(p_to).length(), dot(p_to)); } +real_t Vector3::signed_angle_to(const Vector3 &p_to, const Vector3 &p_axis) const { + Vector3 cross_to = cross(p_to); + real_t unsigned_angle = Math::atan2(cross_to.length(), dot(p_to)); + real_t sign = cross_to.dot(p_axis); + return (sign < 0) ? -unsigned_angle : unsigned_angle; +} + Vector3 Vector3::direction_to(const Vector3 &p_to) const { Vector3 ret(p_to.x - x, p_to.y - y, p_to.z - z); ret.normalize(); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 8f2cba138b..48f9816074 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1059,6 +1059,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector3, min_axis, sarray(), varray()); bind_method(Vector3, max_axis, sarray(), varray()); bind_method(Vector3, angle_to, sarray("to"), varray()); + bind_method(Vector3, signed_angle_to, sarray("to", "axis"), varray()); bind_method(Vector3, direction_to, sarray("b"), varray()); bind_method(Vector3, distance_to, sarray("b"), varray()); bind_method(Vector3, distance_squared_to, sarray("b"), varray()); diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index fd32e86d7e..7ca70f5a7a 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -80,6 +80,27 @@ Returns the total number of paragraphs (newlines or [code]p[/code] tags in the tag stack's text tags). Considers wrapped text as one paragraph. </description> </method> + <method name="get_selected_text" qualifiers="const"> + <return type="String"> + </return> + <description> + Returns the current selection text. Does not include BBCodes. + </description> + </method> + <method name="get_selection_from" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the current selection first character index if a selection is active, [code]-1[/code] otherwise. Does not include BBCodes. + </description> + </method> + <method name="get_selection_to" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the current selection last character index if a selection is active, [code]-1[/code] otherwise. Does not include BBCodes. + </description> + </method> <method name="get_total_character_count" qualifiers="const"> <return type="int"> </return> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 2d129a2c86..ea80b7c248 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -68,7 +68,7 @@ <argument index="0" name="to" type="Vector3"> </argument> <description> - Returns the minimum angle to the given vector, in radians. + Returns the unsigned minimum angle to the given vector, in radians. </description> </method> <method name="bounce"> @@ -465,6 +465,17 @@ Returns a vector with each component set to one or negative one, depending on the signs of this vector's components, or zero if the component is zero, by calling [method @GlobalScope.sign] on each component. </description> </method> + <method name="signed_angle_to"> + <return type="float"> + </return> + <argument index="0" name="to" type="Vector3"> + </argument> + <argument index="1" name="axis" type="Vector3"> + </argument> + <description> + Returns the signed angle to the given vector, in radians. The sign of the angle is positive in a counter-clockwise direction and negative in a clockwise direction when viewed from the side specified by the [code]axis[/code]. + </description> + </method> <method name="slerp"> <return type="Vector3"> </return> diff --git a/editor/icons/GPUParticlesAttractorBox.svg b/editor/icons/GPUParticlesAttractorBox.svg new file mode 100644 index 0000000000..3c27b2d3cb --- /dev/null +++ b/editor/icons/GPUParticlesAttractorBox.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fc9c9c" fill-opacity=".996078" r="1"/><g fill="none" stroke="#fc9c9c" stroke-opacity=".996078"><ellipse cx="8" cy="-8" rx="2.339226" ry="4.949748" transform="rotate(90)"/><ellipse cx="8" cy="8" rx="2.339226" ry="4.949748"/><path d="m1.498906 1.498906h13.002189v13.002188h-13.002189z" stroke-width=".997813"/></g></svg> diff --git a/editor/icons/GPUParticlesAttractorSphere.svg b/editor/icons/GPUParticlesAttractorSphere.svg new file mode 100644 index 0000000000..5473a23854 --- /dev/null +++ b/editor/icons/GPUParticlesAttractorSphere.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><ellipse cx="-8" cy="-7.999999" fill="none" rx="6.499003" ry="6.499001" stroke="#fc9c9c" stroke-opacity=".996078" stroke-width="1.002" transform="scale(-1)"/><circle cx="8" cy="8" fill="#fc9c9c" fill-opacity=".996078" r="1"/><g fill="none" stroke="#fc9c9c" stroke-opacity=".996078"><ellipse cx="11.313708" rx="2.339226" ry="4.949748" transform="matrix(.70710678 .70710678 -.70710678 .70710678 0 0)"/><ellipse cy="11.313708" rx="2.339226" ry="4.949748" transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 0)"/></g></svg> diff --git a/editor/icons/GPUParticlesAttractorVectorField.svg b/editor/icons/GPUParticlesAttractorVectorField.svg new file mode 100644 index 0000000000..93a29789e3 --- /dev/null +++ b/editor/icons/GPUParticlesAttractorVectorField.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><ellipse cx="6.663637" cy="9.245457" fill="#fc9c9c" fill-opacity=".996078" rx="1.030661" ry=".998146"/><ellipse cx="-6.672815" cy="-9.387111" fill="none" rx="2.408711" ry="5.096776" stroke="#fc9c9c" stroke-opacity=".996078" stroke-width="1.0297" transform="matrix(-.99999945 .00104887 .00104887 -.99999945 0 0)"/><ellipse cx="9.387111" cy="-6.672815" fill="none" rx="2.408711" ry="5.096776" stroke="#fc9c9c" stroke-opacity=".996078" stroke-width="1.0297" transform="matrix(-.00104887 .99999945 -.99999945 .00104887 0 0)"/><g fill="#fc9c9c" fill-opacity=".996078"><path d="m11.8 15 2.4-2.4.8.8v-2.4h-2.4l.8.8-2.4 2.4z"/><path d="m11 6 3-3 1 1v-3h-3l1 1-3 3z"/><path d="m1.8 5 2.4-2.4.8.8v-2.4h-2.4l.8.8-2.4 2.4z"/></g></svg> diff --git a/editor/icons/GPUParticlesCollisionBox.svg b/editor/icons/GPUParticlesCollisionBox.svg new file mode 100644 index 0000000000..f7296b34c3 --- /dev/null +++ b/editor/icons/GPUParticlesCollisionBox.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc9c9c" fill-opacity=".996078"><path d="m7.5 2.8124998-5.5883107 2.7941554v5.7660988l5.5883107 2.794155 5.588311-2.794155v-5.7660988zm0 1.6886278 3.145021 1.5732692-3.145021 1.5717523-3.1450214-1.5717523zm-3.9916505 2.8362274 3.1933204 1.5966602v3.1465378l-3.1933204-1.598256zm7.9833015 0v3.145021l-3.1933209 1.598257v-3.146538z" stroke-width=".851579"/><circle cx="1.875" cy="3.75" r=".9375"/><circle cx="13.124999" cy="3.75" r=".9375"/><circle cx="9.374999" cy="1.875" r=".9375"/><circle cx="5.625" cy="1.875" r=".9375"/></g></svg> diff --git a/editor/icons/GPUParticlesCollisionHeightField.svg b/editor/icons/GPUParticlesCollisionHeightField.svg new file mode 100644 index 0000000000..b483f299e9 --- /dev/null +++ b/editor/icons/GPUParticlesCollisionHeightField.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc9c9c"><path d="m1 10c1-1 3-2 3-4s2-4 4-4 4 2 4 4 2 3 3 4l-7 5z"/><circle cx="2" cy="6" r="1"/><circle cx="14" cy="6" r="1"/><circle cx="12" cy="2" r="1"/><circle cx="4" cy="2" r="1"/></g></svg> diff --git a/editor/icons/GPUParticlesCollisionSDF.svg b/editor/icons/GPUParticlesCollisionSDF.svg new file mode 100644 index 0000000000..6279990f3a --- /dev/null +++ b/editor/icons/GPUParticlesCollisionSDF.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 14h-12v-9s3 4 5.9999999 3.9999999c3.0000001-.0000001 6.0000001-3.9999999 6.0000001-3.9999999z" fill="none" stroke="#fc9c9c" stroke-linejoin="round" stroke-opacity=".996078" stroke-width="2"/><g fill="#fc9c9c" fill-opacity=".996078"><circle cx="2" cy="2" r="1"/><circle cx="14" cy="2" r="1"/><circle cx="10" cy="5" r="1"/><circle cx="6" cy="5" r="1"/></g></svg> diff --git a/editor/icons/GPUParticlesCollisionSphere.svg b/editor/icons/GPUParticlesCollisionSphere.svg new file mode 100644 index 0000000000..fc7715445c --- /dev/null +++ b/editor/icons/GPUParticlesCollisionSphere.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc9c9c" fill-opacity=".996078"><path d="m8 3.0532484c-3.2888554 0-5.9733758 2.6845204-5.9733758 5.9733758 0 3.2889408 2.6845204 5.9733758 5.9733758 5.9733758 3.288855 0 5.973376-2.684435 5.973376-5.9733758 0-3.2888554-2.684521-5.9733758-5.973376-5.9733758zm-.8533394 1.79005v4.1567016c-1.1034532-.0608789-2.2238878-.2544573-3.3650586-.5900074.256693-1.7901354 1.6087154-3.2141029 3.3650586-3.5667027zm1.7066788 0c1.7535276.3520281 3.1035956 1.77213 3.3633516 3.55834-1.113266.3129793-2.2321649.5142138-3.3633516.5866709zm3.2300606 5.3599956c-.434043 1.51792-1.663927 2.690664-3.2300606 3.005035v-2.518376c1.0915918-.0617 2.1691036-.227875 3.2300606-.486668zm-8.161765.015c1.0865571.272147 2.162106.428504 3.2250256.480003v2.510013c-1.5608431-.313338-2.7870065-1.479605-3.2250256-2.990016z" stroke-width=".853339"/><circle cx="2" cy="5" r="1"/><circle cx="14" cy="5" r="1"/><circle cx="10" cy="2" r="1"/><circle cx="6" cy="2" r="1"/></g></svg> diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 8627f71987..b128b81000 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -159,7 +159,8 @@ void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) { const uint8_t *src_datar = p_data.ptr(); mp3dec_ex_t mp3d; - mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE); + int err = mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE); + ERR_FAIL_COND(err != 0); channels = mp3d.info.channels; sample_rate = mp3d.info.hz; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index 42dbdf25c3..3b895bbbf6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -111,10 +111,10 @@ namespace Godot } /// <summary> - /// Returns the minimum angle to the given vector, in radians. + /// Returns the unsigned minimum angle to the given vector, in radians. /// </summary> /// <param name="to">The other vector to compare this vector to.</param> - /// <returns>The angle between the two vectors, in radians.</returns> + /// <returns>The unsigned angle between the two vectors, in radians.</returns> public real_t AngleTo(Vector3 to) { return Mathf.Atan2(Cross(to).Length(), Dot(to)); @@ -469,6 +469,23 @@ namespace Godot } /// <summary> + /// Returns the signed angle to the given vector, in radians. + /// The sign of the angle is positive in a counter-clockwise + /// direction and negative in a clockwise direction when viewed + /// from the side specified by the `axis`. + /// </summary> + /// <param name="to">The other vector to compare this vector to.</param> + /// <param name="axis">The reference axis to use for the angle sign.</param> + /// <returns>The signed angle between the two vectors, in radians.</returns> + public real_t SignedAngleTo(Vector3 to, Vector3 axis) + { + Vector3 crossTo = Cross(to); + real_t unsignedAngle = Mathf.Atan2(crossTo.Length(), Dot(to)); + real_t sign = crossTo.Dot(axis); + return (sign < 0) ? -unsignedAngle : unsignedAngle; + } + + /// <summary> /// Returns the result of the spherical linear interpolation between /// this vector and `to` by amount `weight`. /// diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index f7bf650354..6732078efc 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -47,7 +47,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, buffer, todo * 2); if (vorbis_stream->channels == 1 && mixed > 0) { //mix mono to stereo - for (int i = start_buffer; i < mixed; i++) { + for (int i = start_buffer; i < start_buffer + mixed; i++) { p_buffer[i].r = p_buffer[i].l; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index a22b80761d..993f0e9127 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -46,7 +46,9 @@ import androidx.annotation.Nullable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -107,14 +109,47 @@ public abstract class GodotPlugin { * This method is invoked on the render thread. */ public final void onRegisterPluginWithGodotNative() { - nativeRegisterSingleton(getPluginName()); + registeredSignals.putAll(registerPluginWithGodotNative(this, new GodotPluginInfoProvider() { + @NonNull + @Override + public String getPluginName() { + return GodotPlugin.this.getPluginName(); + } + + @NonNull + @Override + public List<String> getPluginMethods() { + return GodotPlugin.this.getPluginMethods(); + } + + @NonNull + @Override + public Set<SignalInfo> getPluginSignals() { + return GodotPlugin.this.getPluginSignals(); + } + + @NonNull + @Override + public Set<String> getPluginGDNativeLibrariesPaths() { + return GodotPlugin.this.getPluginGDNativeLibrariesPaths(); + } + })); + } - Class clazz = getClass(); + /** + * Register the plugin with Godot native code. + * + * This method must be invoked on the render thread. + */ + public static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, GodotPluginInfoProvider pluginInfoProvider) { + nativeRegisterSingleton(pluginInfoProvider.getPluginName(), pluginObject); + + Class clazz = pluginObject.getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { boolean found = false; - for (String s : getPluginMethods()) { + for (String s : pluginInfoProvider.getPluginMethods()) { if (s.equals(method.getName())) { found = true; break; @@ -123,7 +158,7 @@ public abstract class GodotPlugin { if (!found) continue; - List<String> ptr = new ArrayList<String>(); + List<String> ptr = new ArrayList<>(); Class[] paramTypes = method.getParameterTypes(); for (Class c : paramTypes) { @@ -133,26 +168,29 @@ public abstract class GodotPlugin { String[] pt = new String[ptr.size()]; ptr.toArray(pt); - nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt); + nativeRegisterMethod(pluginInfoProvider.getPluginName(), method.getName(), method.getReturnType().getName(), pt); } // Register the signals for this plugin. - for (SignalInfo signalInfo : getPluginSignals()) { + Map<String, SignalInfo> registeredSignals = new HashMap<>(); + for (SignalInfo signalInfo : pluginInfoProvider.getPluginSignals()) { String signalName = signalInfo.getName(); - nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames()); + nativeRegisterSignal(pluginInfoProvider.getPluginName(), signalName, signalInfo.getParamTypesNames()); registeredSignals.put(signalName, signalInfo); } // Get the list of gdnative libraries to register. - Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths(); + Set<String> gdnativeLibrariesPaths = pluginInfoProvider.getPluginGDNativeLibrariesPaths(); if (!gdnativeLibrariesPaths.isEmpty()) { nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0])); } + + return registeredSignals; } /** * Invoked once during the Godot Android initialization process after creation of the - * {@link org.godotengine.godot.GodotView} view. + * {@link org.godotengine.godot.GodotRenderView} view. * <p> * The plugin can return a non-null {@link View} layout in order to add it to the Godot view * hierarchy. @@ -290,8 +328,8 @@ public abstract class GodotPlugin { /** * Emit a registered Godot signal. - * @param signalName - * @param signalArgs + * @param signalName Name of the signal to emit. It will be validated against the set of registered signals. + * @param signalArgs Arguments used to populate the emitted signal. The arguments will be validated against the {@link SignalInfo} matching the registered signalName parameter. */ protected void emitSignal(final String signalName, final Object... signalArgs) { try { @@ -301,6 +339,27 @@ public abstract class GodotPlugin { throw new IllegalArgumentException( "Signal " + signalName + " is not registered for this plugin."); } + emitSignal(getGodot(), getPluginName(), signalInfo, signalArgs); + } catch (IllegalArgumentException exception) { + Log.w(TAG, exception.getMessage()); + if (BuildConfig.DEBUG) { + throw exception; + } + } + } + + /** + * Emit a Godot signal. + * @param godot + * @param pluginName Name of the Godot plugin the signal will be emitted from. The plugin must already be registered with the Godot engine. + * @param signalInfo Information about the signal to emit. + * @param signalArgs Arguments used to populate the emitted signal. The arguments will be validated against the given {@link SignalInfo} parameter. + */ + public static void emitSignal(Godot godot, String pluginName, SignalInfo signalInfo, final Object... signalArgs) { + try { + if (signalInfo == null) { + throw new IllegalArgumentException("Signal must be non null."); + } // Validate the arguments count. Class<?>[] signalParamTypes = signalInfo.getParamTypes(); @@ -317,12 +376,8 @@ public abstract class GodotPlugin { } } - runOnRenderThread(new Runnable() { - @Override - public void run() { - nativeEmitSignal(getPluginName(), signalName, signalArgs); - } - }); + godot.runOnRenderThread(() -> nativeEmitSignal(pluginName, signalInfo.getName(), signalArgs)); + } catch (IllegalArgumentException exception) { Log.w(TAG, exception.getMessage()); if (BuildConfig.DEBUG) { @@ -335,7 +390,7 @@ public abstract class GodotPlugin { * Used to setup a {@link GodotPlugin} instance. * @param p_name Name of the instance. */ - private native void nativeRegisterSingleton(String p_name); + private static native void nativeRegisterSingleton(String p_name, Object object); /** * Used to complete registration of the {@link GodotPlugin} instance's methods. @@ -344,13 +399,13 @@ public abstract class GodotPlugin { * @param p_ret Return type of the registered method * @param p_params Method parameters types */ - private native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params); + private static native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params); /** * Used to register gdnative libraries bundled by the plugin. * @param gdnlibPaths Paths to the libraries relative to the 'assets' directory. */ - private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths); + private static native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths); /** * Used to complete registration of the {@link GodotPlugin} instance's methods. @@ -358,7 +413,7 @@ public abstract class GodotPlugin { * @param signalName Name of the signal to register * @param signalParamTypes Signal parameters types */ - private native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes); + private static native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes); /** * Used to emit signal by {@link GodotPlugin} instance. @@ -366,5 +421,5 @@ public abstract class GodotPlugin { * @param signalName Name of the signal to emit * @param signalParams Signal parameters */ - private native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams); + private static native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java new file mode 100644 index 0000000000..c3084b036e --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* GodotPluginInfoProvider.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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. */ +/*************************************************************************/ + +package org.godotengine.godot.plugin; + +import androidx.annotation.NonNull; + +import java.util.List; +import java.util.Set; + +/** + * Provides the set of information expected from a Godot plugin. + */ +public interface GodotPluginInfoProvider { + /** + * Returns the name of the plugin. + */ + @NonNull + String getPluginName(); + + /** + * Returns the list of methods to be exposed to Godot. + */ + @NonNull + List<String> getPluginMethods(); + + /** + * Returns the list of signals to be exposed to Godot. + */ + @NonNull + Set<SignalInfo> getPluginSignals(); + + /** + * Returns the paths for the plugin's gdnative libraries (if any). + * + * The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file. + */ + @NonNull + Set<String> getPluginGDNativeLibrariesPaths(); +} diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index f602e99e61..ba3e9fa20f 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -41,7 +41,7 @@ static HashMap<String, JNISingleton *> jni_singletons; extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) { String singname = jstring_to_string(name, env); JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton"); s->set_instance(env->NewGlobalRef(obj)); @@ -51,7 +51,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis ProjectSettings::get_singleton()->set(singname, s); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args) { String singname = jstring_to_string(sname, env); ERR_FAIL_COND(!jni_singletons.has(singname)); @@ -83,7 +83,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis s->add_method(mname, mid, types, get_jni_type(retval)); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) { String singleton_name = jstring_to_string(j_plugin_name, env); ERR_FAIL_COND(!jni_singletons.has(singleton_name)); @@ -104,7 +104,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis singleton->add_signal(signal_name, types); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params) { String singleton_name = jstring_to_string(j_plugin_name, env); ERR_FAIL_COND(!jni_singletons.has(singleton_name)); @@ -129,7 +129,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS singleton->emit_signal(signal_name, args, count); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths) { int gdnlib_count = env->GetArrayLength(gdnlib_paths); if (gdnlib_count == 0) { return; diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h index 8a08ec3709..b87f922e03 100644 --- a/platform/android/plugin/godot_plugin_jni.h +++ b/platform/android/plugin/godot_plugin_jni.h @@ -35,11 +35,11 @@ #include <jni.h> extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name); -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types); -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params); -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths); +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj); +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args); +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types); +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths); } #endif // GODOT_PLUGIN_JNI_H diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 4e7eec906c..d6f35cc99d 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -342,6 +342,9 @@ bool AudioStreamPlayer2D::is_playing() const { float AudioStreamPlayer2D::get_playback_position() { if (stream_playback.is_valid()) { + if (setseek >= 0.0) { + return setseek; + } return stream_playback->get_playback_position(); } diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index d420bd6075..65d0703139 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -714,6 +714,9 @@ bool AudioStreamPlayer3D::is_playing() const { float AudioStreamPlayer3D::get_playback_position() { if (stream_playback.is_valid()) { + if (setseek >= 0.0) { + return setseek; + } return stream_playback->get_playback_position(); } diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 4f77734b79..ec0a4b8696 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -267,6 +267,9 @@ bool AudioStreamPlayer::is_playing() const { float AudioStreamPlayer::get_playback_position() { if (stream_playback.is_valid()) { + if (setseek >= 0.0) { + return setseek; + } return stream_playback->get_playback_position(); } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index da5389dedf..5bd9259a64 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1587,7 +1587,7 @@ Size2 LineEdit::get_minimum_size() const { // Minimum size of text. int em_space_size = font->get_char_size('M', 0, font_size).x; - min_size.width = get_theme_constant("minimum_character_width'") * em_space_size; + min_size.width = get_theme_constant("minimum_character_width") * em_space_size; if (expand_to_text_length) { // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end. diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index d68a3206b6..f237f79be1 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -62,7 +62,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { Size2 size; Size2 icon_size = items[i].get_icon_size(); - size.height = MAX(icon_size.height, items[i].text_buf->get_size().y); + size.height = _get_item_height(i); icon_w = MAX(icon_size.width, icon_w); size.width += items[i].h_ofs; @@ -106,13 +106,35 @@ Size2 PopupMenu::_get_contents_minimum_size() const { return minsize; } +int PopupMenu::_get_item_height(int p_item) const { + ERR_FAIL_INDEX_V(p_item, items.size(), 0); + ERR_FAIL_COND_V(p_item < 0, 0); + + int icon_height = items[p_item].get_icon_size().height; + if (items[p_item].checkable_type) { + icon_height = MAX(icon_height, MAX(get_theme_icon("checked")->get_height(), get_theme_icon("radio_checked")->get_height())); + } + + int text_height = items[p_item].text_buf->get_size().height; + if (text_height == 0 && !items[p_item].separator) { + text_height = get_theme_font("font")->get_height(get_theme_font_size("font_size")); + } + + int separator_height = 0; + if (items[p_item].separator) { + separator_height = MAX(get_theme_stylebox("separator")->get_minimum_size().height, MAX(get_theme_stylebox("labeled_separator_left")->get_minimum_size().height, get_theme_stylebox("labeled_separator_right")->get_minimum_size().height)); + } + + return MAX(separator_height, MAX(text_height, icon_height)); +} + int PopupMenu::_get_items_total_height() const { int vsep = get_theme_constant("vseparation"); // Get total height of all items by taking max of icon height and font height int items_total_height = 0; for (int i = 0; i < items.size(); i++) { - items_total_height += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y) + vsep; + items_total_height += _get_item_height(i) + vsep; } // Subtract a separator which is not needed for the last item. @@ -154,7 +176,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { for (int i = 0; i < items.size(); i++) { ofs.y += i > 0 ? vseparation : (float)vseparation / 2; - ofs.y += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y); + ofs.y += _get_item_height(i); if (p_over.y - control->get_position().y < ofs.y) { return i; @@ -515,7 +537,7 @@ void PopupMenu::_draw_items() { Point2 item_ofs = ofs; Size2 icon_size = items[i].get_icon_size(); - float h = MAX(icon_size.height, items[i].text_buf->get_size().y); + float h = _get_item_height(i); if (i == mouse_over) { if (rtl) { @@ -531,19 +553,20 @@ void PopupMenu::_draw_items() { item_ofs.x += items[i].h_ofs; if (items[i].separator) { int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; + int sep_ofs = Math::floor((h - sep_h) / 2.0); if (text != String()) { int text_size = items[i].text_buf->get_size().width; int text_center = display_width / 2; int text_left = text_center - text_size / 2; int text_right = text_center + text_size / 2; if (text_left > item_ofs.x) { - labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, text_left - item_ofs.x), sep_h))); + labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, text_left - item_ofs.x), sep_h))); } if (text_right < display_width) { - labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, display_width - text_right), sep_h))); + labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - text_right), sep_h))); } } else { - separator->draw(ci, Rect2(item_ofs, Size2(display_width, sep_h))); + separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); } } diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 184be42e95..e4cbe984c9 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -101,6 +101,7 @@ class PopupMenu : public Popup { int _get_mouse_over(const Point2 &p_over) const; virtual Size2 _get_contents_minimum_size() const override; + int _get_item_height(int p_item) const; int _get_items_total_height() const; void _scroll_to_item(int p_item); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 992e272186..6a1cc291a6 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -45,7 +45,7 @@ #include "editor/editor_scale.h" #endif -RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) { +RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const { if (p_free) { if (p_item->subitems.size()) { return p_item->subitems.front()->get(); @@ -90,7 +90,7 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) { return nullptr; } -RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) { +RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) const { if (p_free) { if (p_item->subitems.size()) { return p_item->subitems.back()->get(); @@ -147,7 +147,7 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item case ITEM_TEXT: { ItemText *t = (ItemText *)it; offset += t->text.length(); - if (offset > p_position) { + if (offset >= p_position) { return it; } } break; @@ -454,6 +454,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> ItemImage *img = (ItemImage *)it; l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1); text += String::chr(0xfffc); + l.char_count += 1; } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); @@ -3523,7 +3524,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p return false; } -String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) { +String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) const { String text; ERR_FAIL_COND_V(p_frame == nullptr, text); ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text); @@ -3590,7 +3591,7 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p return text; } -String RichTextLabel::get_selected_text() { +String RichTextLabel::get_selected_text() const { if (!selection.active || !selection.enabled) { return ""; } @@ -3614,6 +3615,22 @@ bool RichTextLabel::is_selection_enabled() const { return selection.enabled; } +int RichTextLabel::get_selection_from() const { + if (!selection.active || !selection.enabled) { + return -1; + } + + return selection.from_frame->lines[selection.from_line].char_offset + selection.from_char; +} + +int RichTextLabel::get_selection_to() const { + if (!selection.active || !selection.enabled) { + return -1; + } + + return selection.to_frame->lines[selection.to_line].char_offset + selection.to_char - 1; +} + void RichTextLabel::set_bbcode(const String &p_bbcode) { bbcode = p_bbcode; if (is_inside_tree() && use_bbcode) { @@ -3649,6 +3666,8 @@ String RichTextLabel::get_text() { text += t->text; } else if (it->type == ITEM_NEWLINE) { text += "\n"; + } else if (it->type == ITEM_IMAGE) { + text += " "; } else if (it->type == ITEM_INDENT || it->type == ITEM_LIST) { text += "\t"; } @@ -3841,6 +3860,11 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled); ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled); + ClassDB::bind_method(D_METHOD("get_selection_from"), &RichTextLabel::get_selection_from); + ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to); + + ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text); + ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode); ClassDB::bind_method(D_METHOD("append_bbcode", "bbcode"), &RichTextLabel::append_bbcode); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index e89011e9f5..2351aff0a4 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -389,7 +389,7 @@ private: void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); - String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel); + String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel) const; bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to); void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset); @@ -427,8 +427,8 @@ private: void _scroll_changed(double); void _gui_input(Ref<InputEvent> p_event); - Item *_get_next_item(Item *p_item, bool p_free = false); - Item *_get_prev_item(Item *p_item, bool p_free = false); + Item *_get_next_item(Item *p_item, bool p_free = false) const; + Item *_get_prev_item(Item *p_item, bool p_free = false) const; Rect2 _get_text_rect(); Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier); @@ -524,7 +524,9 @@ public: void set_selection_enabled(bool p_enabled); bool is_selection_enabled() const; - String get_selected_text(); + int get_selection_from() const; + int get_selection_to() const; + String get_selected_text() const; void selection_copy(); Error parse_bbcode(const String &p_bbcode); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index c9bc38c36a..ad06739da9 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3342,6 +3342,9 @@ void Tree::item_selected(int p_column, TreeItem *p_item) { //emit_signal("multi_selected",p_item,p_column,true); - NO this is for TreeItem::select selected_col = p_column; + if (!selected_item) { + selected_item = p_item; + } } else { select_single_item(p_item, root, p_column); } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index b4773adc6f..702f2ed1c8 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -828,6 +828,8 @@ Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p } void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { + ERR_FAIL_COND(data.is_empty()); + uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_size, hash); @@ -858,6 +860,8 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t } void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { + ERR_FAIL_COND(data.is_empty()); + uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_size, hash); diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index ed69c093cf..925867a1f2 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -167,6 +167,7 @@ void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) { } bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { + ERR_FAIL_COND_V(p_fonts.is_null(), false); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 94957df510..444a4bb22a 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -243,6 +243,7 @@ TextServer::Orientation TextParagraph::get_orientation() const { } bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) { + ERR_FAIL_COND_V(p_fonts.is_null(), false); TS->shaped_text_clear(dropcap_rid); dropcap_margins = p_dropcap_margins; bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); @@ -257,6 +258,7 @@ void TextParagraph::clear_dropcap() { } bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { + ERR_FAIL_COND_V(p_fonts.is_null(), false); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); |