mirror of
https://github.com/godotengine/godot
synced 2024-09-30 04:38:44 +00:00
Compare commits
8 commits
aed400cce2
...
7722461dc5
Author | SHA1 | Date | |
---|---|---|---|
7722461dc5 | |||
617f5c5580 | |||
44656e8328 | |||
fb846d3522 | |||
b04c9a71f4 | |||
9cd84224bd | |||
b5213cceac | |||
e09d4d255e |
|
@ -39,6 +39,9 @@
|
|||
<member name="align" type="int" setter="set_align" getter="get_align" enum="LineEdit.Align" default="0">
|
||||
Sets the text alignment of the [SpinBox].
|
||||
</member>
|
||||
<member name="custom_arrow_step" type="float" setter="set_custom_arrow_step" getter="get_custom_arrow_step" default="0.0">
|
||||
If not [code]0[/code], [code]value[/code] will always be rounded to a multiple of [code]custom_arrow_step[/code] when interacting with the arrow buttons of the [SpinBox].
|
||||
</member>
|
||||
<member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true">
|
||||
If [code]true[/code], the [SpinBox] will be editable. Otherwise, it will be read only.
|
||||
</member>
|
||||
|
|
|
@ -708,6 +708,31 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
|
|||
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t curr_frame = Engine::get_singleton()->get_idle_frames();
|
||||
if (curr_frame != last_parsed_frame) {
|
||||
frame_parsed_events.clear();
|
||||
last_parsed_frame = curr_frame;
|
||||
frame_parsed_events.insert(p_event);
|
||||
} else if (frame_parsed_events.has(p_event)) {
|
||||
// It would be technically safe to send the same event in cases such as:
|
||||
// - After an explicit flush.
|
||||
// - In platforms using buffering when agile flushing is enabled, after one of the mid-frame flushes.
|
||||
// - If platform doesn't use buffering and event accumulation is disabled.
|
||||
// - If platform doesn't use buffering and the event type is not accumulable.
|
||||
// However, it wouldn't be reasonable to ask users to remember the full ruleset and be aware at all times
|
||||
// of the possibilites of the target platform, project settings and engine internals, which may change
|
||||
// without prior notice.
|
||||
// Therefore, the guideline is, "don't send the same event object more than once per frame".
|
||||
WARN_PRINT_ONCE(
|
||||
"An input event object is being parsed more than once in the same frame, which is unsafe.\n"
|
||||
"If you are generating events in a script, you have to instantiate a new event instead of sending the same one more than once, unless the original one was sent on an earlier frame.\n"
|
||||
"You can call duplicate() on the event to get a new instance with identical values.");
|
||||
} else {
|
||||
frame_parsed_events.insert(p_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (use_accumulated_input) {
|
||||
if (buffered_events.empty() || !buffered_events.back()->get()->accumulate(p_event)) {
|
||||
buffered_events.push_back(p_event);
|
||||
|
|
|
@ -204,6 +204,11 @@ private:
|
|||
bool use_input_buffering;
|
||||
bool use_accumulated_input;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Set<Ref<InputEvent>> frame_parsed_events;
|
||||
uint64_t last_parsed_frame = UINT64_MAX;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
struct VibrationInfo {
|
||||
float weak_magnitude;
|
||||
|
|
|
@ -968,6 +968,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
|||
encode_uint32(is_resizeable, &p_manifest.write[iofs + 16]);
|
||||
}
|
||||
|
||||
if (tname == "provider" && attrname == "authorities") {
|
||||
string_table.write[attr_value] = get_package_name(package_name) + String(".fileprovider");
|
||||
}
|
||||
|
||||
if (tname == "supports-screens") {
|
||||
if (attrname == "smallScreens") {
|
||||
encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
|
||||
|
|
|
@ -20,6 +20,16 @@
|
|||
android:exported="false"
|
||||
/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/godot_provider_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
11
platform/android/java/lib/res/xml/godot_provider_paths.xml
Normal file
11
platform/android/java/lib/res/xml/godot_provider_paths.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<external-path
|
||||
name="public"
|
||||
path="." />
|
||||
|
||||
<external-files-path
|
||||
name="app"
|
||||
path="." />
|
||||
</paths>
|
|
@ -49,6 +49,9 @@ import android.view.Display;
|
|||
import android.view.DisplayCutout;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -84,29 +87,42 @@ public class GodotIO {
|
|||
// MISCELLANEOUS OS IO
|
||||
/////////////////////////
|
||||
|
||||
public int openURI(String p_uri) {
|
||||
public int openURI(String uriString) {
|
||||
try {
|
||||
String path = p_uri;
|
||||
String type = "";
|
||||
if (path.startsWith("/")) {
|
||||
//absolute path to filesystem, prepend file://
|
||||
path = "file://" + path;
|
||||
if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) {
|
||||
type = "image/*";
|
||||
Uri dataUri;
|
||||
String dataType = "";
|
||||
boolean grantReadUriPermission = false;
|
||||
|
||||
if (uriString.startsWith("/") || uriString.startsWith("file://")) {
|
||||
String filePath = uriString;
|
||||
// File uris needs to be provided via the FileProvider
|
||||
grantReadUriPermission = true;
|
||||
if (filePath.startsWith("file://")) {
|
||||
filePath = filePath.replace("file://", "");
|
||||
}
|
||||
|
||||
File targetFile = new File(filePath);
|
||||
dataUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", targetFile);
|
||||
dataType = activity.getContentResolver().getType(dataUri);
|
||||
} else {
|
||||
dataUri = Uri.parse(uriString);
|
||||
}
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
if (!type.equals("")) {
|
||||
intent.setDataAndType(Uri.parse(path), type);
|
||||
if (TextUtils.isEmpty(dataType)) {
|
||||
intent.setData(dataUri);
|
||||
} else {
|
||||
intent.setData(Uri.parse(path));
|
||||
intent.setDataAndType(dataUri, dataType);
|
||||
}
|
||||
if (grantReadUriPermission) {
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
|
||||
activity.startActivity(intent);
|
||||
return 0;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG, "Unable to open uri " + uriString, e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,14 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
|
|||
}
|
||||
|
||||
String Navigation2D::get_configuration_warning() const {
|
||||
return TTR("'Navigation2D' node and 'Navigation2D.get_simple_path()' are deprecated and will be removed in a future version. Use 'Navigation2DServer.map_get_path()' instead.");
|
||||
String warning = Node2D::get_configuration_warning();
|
||||
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("'Navigation2D' node and 'Navigation2D.get_simple_path()' are deprecated and will be removed in a future version. Use 'Navigation2DServer.map_get_path()' instead.");
|
||||
|
||||
return warning;
|
||||
}
|
||||
|
||||
Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) const {
|
||||
|
|
|
@ -398,11 +398,16 @@ void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
|
|||
}
|
||||
|
||||
String NavigationAgent2D::get_configuration_warning() const {
|
||||
String warning = Node::get_configuration_warning();
|
||||
|
||||
if (!Object::cast_to<Node2D>(get_parent())) {
|
||||
return TTR("The NavigationAgent2D can be used only under a Node2D inheriting parent node.");
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("The NavigationAgent2D can be used only under a Node2D inheriting parent node.");
|
||||
}
|
||||
|
||||
return String();
|
||||
return warning;
|
||||
}
|
||||
|
||||
void NavigationAgent2D::update_navigation() {
|
||||
|
|
|
@ -158,16 +158,24 @@ Node *NavigationObstacle2D::get_navigation_node() const {
|
|||
}
|
||||
|
||||
String NavigationObstacle2D::get_configuration_warning() const {
|
||||
String warning = Node::get_configuration_warning();
|
||||
|
||||
if (!Object::cast_to<Node2D>(get_parent())) {
|
||||
return TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
|
||||
}
|
||||
|
||||
if (Object::cast_to<StaticBody2D>(get_parent())) {
|
||||
return TTR("The NavigationObstacle2D is intended for constantly moving bodies like KinematicBody2D or RigidBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
|
||||
"\nNot constantly moving or complete static objects should be captured with a refreshed NavigationPolygon so agents can not only avoid them but also move along those objects outline at high detail");
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("The NavigationObstacle2D is intended for constantly moving bodies like KinematicBody2D or RigidBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
|
||||
"\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail");
|
||||
}
|
||||
|
||||
return String();
|
||||
return warning;
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::initialize_agent() {
|
||||
|
|
|
@ -582,11 +582,8 @@ void NavigationPolygonInstance::_map_changed(RID p_map) {
|
|||
}
|
||||
|
||||
String NavigationPolygonInstance::get_configuration_warning() const {
|
||||
if (!is_visible_in_tree() || !is_inside_tree()) {
|
||||
return String();
|
||||
}
|
||||
|
||||
String warning = Node2D::get_configuration_warning();
|
||||
|
||||
if (!navpoly.is_valid()) {
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
|
|
|
@ -38,7 +38,14 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector
|
|||
}
|
||||
|
||||
String Navigation::get_configuration_warning() const {
|
||||
return TTR("'Navigation' node and 'Navigation.get_simple_path()' are deprecated and will be removed in a future version. Use 'NavigationServer.map_get_path()' instead.");
|
||||
String warning = Spatial::get_configuration_warning();
|
||||
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("'Navigation' node and 'Navigation.get_simple_path()' are deprecated and will be removed in a future version. Use 'NavigationServer.map_get_path()' instead.");
|
||||
|
||||
return warning;
|
||||
}
|
||||
|
||||
Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const {
|
||||
|
|
|
@ -407,11 +407,16 @@ void NavigationAgent::_avoidance_done(Vector3 p_new_velocity) {
|
|||
}
|
||||
|
||||
String NavigationAgent::get_configuration_warning() const {
|
||||
String warning = Node::get_configuration_warning();
|
||||
|
||||
if (!Object::cast_to<Spatial>(get_parent())) {
|
||||
return TTR("The NavigationAgent can be used only under a Spatial inheriting parent node.");
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("The NavigationAgent can be used only under a Spatial inheriting parent node.");
|
||||
}
|
||||
|
||||
return String();
|
||||
return warning;
|
||||
}
|
||||
|
||||
void NavigationAgent::update_navigation() {
|
||||
|
|
|
@ -245,15 +245,16 @@ void NavigationMeshInstance::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
|
|||
}
|
||||
|
||||
String NavigationMeshInstance::get_configuration_warning() const {
|
||||
if (!is_visible_in_tree() || !is_inside_tree()) {
|
||||
return String();
|
||||
}
|
||||
String warning = Spatial::get_configuration_warning();
|
||||
|
||||
if (!navmesh.is_valid()) {
|
||||
return TTR("A NavigationMesh resource must be set or created for this node to work.");
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("A NavigationMesh resource must be set or created for this node to work.");
|
||||
}
|
||||
|
||||
return String();
|
||||
return warning;
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::_bind_methods() {
|
||||
|
|
|
@ -164,16 +164,24 @@ Node *NavigationObstacle::get_navigation_node() const {
|
|||
}
|
||||
|
||||
String NavigationObstacle::get_configuration_warning() const {
|
||||
String warning = Node::get_configuration_warning();
|
||||
|
||||
if (!Object::cast_to<Spatial>(get_parent())) {
|
||||
return TTR("The NavigationObstacle only serves to provide collision avoidance to a Spatial inheriting parent object.");
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("The NavigationObstacle only serves to provide collision avoidance to a Spatial inheriting parent object.");
|
||||
}
|
||||
|
||||
if (Object::cast_to<StaticBody>(get_parent())) {
|
||||
return TTR("The NavigationObstacle is intended for constantly moving bodies like KinematicBody3D or RigidBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
|
||||
"\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail");
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("The NavigationObstacle is intended for constantly moving bodies like KinematicBody or RigidBody as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
|
||||
"\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail");
|
||||
}
|
||||
|
||||
return String();
|
||||
return warning;
|
||||
}
|
||||
|
||||
void NavigationObstacle::initialize_agent() {
|
||||
|
|
|
@ -79,7 +79,8 @@ void SpinBox::_line_edit_input(const Ref<InputEvent> &p_event) {
|
|||
void SpinBox::_range_click_timeout() {
|
||||
if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) {
|
||||
bool up = get_local_mouse_position().y < (get_size().height / 2);
|
||||
set_value(get_value() + (up ? get_step() : -get_step()));
|
||||
double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
|
||||
set_value(get_value() + (up ? step : -step));
|
||||
|
||||
if (range_click_timer->is_one_shot()) {
|
||||
range_click_timer->set_wait_time(0.075);
|
||||
|
@ -109,6 +110,8 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
|
||||
double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
|
||||
|
||||
if (mb.is_valid() && mb->is_pressed()) {
|
||||
bool up = mb->get_position().y < (get_size().height / 2);
|
||||
|
||||
|
@ -116,7 +119,7 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
|
|||
case BUTTON_LEFT: {
|
||||
line_edit->grab_focus();
|
||||
|
||||
set_value(get_value() + (up ? get_step() : -get_step()));
|
||||
set_value(get_value() + (up ? step : -step));
|
||||
|
||||
range_click_timer->set_wait_time(0.6);
|
||||
range_click_timer->set_one_shot(true);
|
||||
|
@ -131,13 +134,13 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
|
|||
} break;
|
||||
case BUTTON_WHEEL_UP: {
|
||||
if (line_edit->has_focus()) {
|
||||
set_value(get_value() + get_step() * mb->get_factor());
|
||||
set_value(get_value() + step * mb->get_factor());
|
||||
accept_event();
|
||||
}
|
||||
} break;
|
||||
case BUTTON_WHEEL_DOWN: {
|
||||
if (line_edit->has_focus()) {
|
||||
set_value(get_value() - get_step() * mb->get_factor());
|
||||
set_value(get_value() - step * mb->get_factor());
|
||||
accept_event();
|
||||
}
|
||||
} break;
|
||||
|
@ -156,8 +159,8 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
|
||||
if (drag.enabled) {
|
||||
drag.diff_y += mm->get_relative().y;
|
||||
float diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8f) * SGN(drag.diff_y);
|
||||
set_value(CLAMP(drag.base_val + get_step() * diff_y, get_min(), get_max()));
|
||||
double diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8f) * SGN(drag.diff_y);
|
||||
set_value(CLAMP(drag.base_val + step * diff_y, get_min(), get_max()));
|
||||
} else if (drag.allowed && drag.capture_pos.distance_to(mm->get_position()) > 2) {
|
||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
|
||||
drag.enabled = true;
|
||||
|
@ -252,6 +255,14 @@ void SpinBox::apply() {
|
|||
_text_entered(line_edit->get_text());
|
||||
}
|
||||
|
||||
void SpinBox::set_custom_arrow_step(double p_custom_arrow_step) {
|
||||
custom_arrow_step = p_custom_arrow_step;
|
||||
}
|
||||
|
||||
double SpinBox::get_custom_arrow_step() const {
|
||||
return custom_arrow_step;
|
||||
}
|
||||
|
||||
void SpinBox::_bind_methods() {
|
||||
//ClassDB::bind_method(D_METHOD("_value_changed"),&SpinBox::_value_changed);
|
||||
ClassDB::bind_method(D_METHOD("_gui_input"), &SpinBox::_gui_input);
|
||||
|
@ -262,8 +273,10 @@ void SpinBox::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_suffix"), &SpinBox::get_suffix);
|
||||
ClassDB::bind_method(D_METHOD("set_prefix", "prefix"), &SpinBox::set_prefix);
|
||||
ClassDB::bind_method(D_METHOD("get_prefix"), &SpinBox::get_prefix);
|
||||
ClassDB::bind_method(D_METHOD("set_editable", "editable"), &SpinBox::set_editable);
|
||||
ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &SpinBox::set_editable);
|
||||
ClassDB::bind_method(D_METHOD("is_editable"), &SpinBox::is_editable);
|
||||
ClassDB::bind_method(D_METHOD("set_custom_arrow_step", "arrow_step"), &SpinBox::set_custom_arrow_step);
|
||||
ClassDB::bind_method(D_METHOD("get_custom_arrow_step"), &SpinBox::get_custom_arrow_step);
|
||||
ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply);
|
||||
ClassDB::bind_method(D_METHOD("_line_edit_focus_enter"), &SpinBox::_line_edit_focus_enter);
|
||||
ClassDB::bind_method(D_METHOD("_line_edit_focus_exit"), &SpinBox::_line_edit_focus_exit);
|
||||
|
@ -275,6 +288,7 @@ void SpinBox::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "custom_arrow_step", PROPERTY_HINT_RANGE, "0,10000,0.0001,or_greater"), "set_custom_arrow_step", "get_custom_arrow_step");
|
||||
}
|
||||
|
||||
SpinBox::SpinBox() {
|
||||
|
|
|
@ -49,6 +49,7 @@ class SpinBox : public Range {
|
|||
virtual void _value_changed(double);
|
||||
String prefix;
|
||||
String suffix;
|
||||
double custom_arrow_step = 0.0;
|
||||
|
||||
void _line_edit_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
|
@ -90,6 +91,8 @@ public:
|
|||
String get_prefix() const;
|
||||
|
||||
void apply();
|
||||
void set_custom_arrow_step(const double p_custom_arrow_step);
|
||||
double get_custom_arrow_step() const;
|
||||
|
||||
SpinBox();
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue