summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/AStarGrid2D.xml62
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp4
-rw-r--r--editor/progress_dialog.cpp2
-rw-r--r--platform/android/android_input_handler.cpp217
-rw-r--r--platform/android/android_input_handler.h22
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt16
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java23
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java35
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java23
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java87
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt289
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java279
-rw-r--r--platform/android/java_godot_lib_jni.cpp44
-rw-r--r--platform/android/java_godot_lib_jni.h10
-rw-r--r--scene/gui/rich_text_label.cpp7
-rw-r--r--servers/rendering/shader_language.cpp8
18 files changed, 782 insertions, 352 deletions
diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml
index ae696eb468..19cd9d21d7 100644
--- a/doc/classes/AStarGrid2D.xml
+++ b/doc/classes/AStarGrid2D.xml
@@ -1,8 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AStarGrid2D" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A* (or "A-Star") pathfinding tailored to find the shortest paths on 2D grids.
</brief_description>
<description>
+ Compared to [AStar2D] you don't need to manually create points or connect them together. It also supports multiple type of heuristics and modes for diagonal movement. This class also provides a jumping mode which is faster to calculate than without it in the [AStar2D] class.
+ In contrast to [AStar2D], you only need set the [member size] of the grid, optionally set the [member cell_size] and then call the [method update] method:
+ [codeblock]
+ var astar_grid = AStarGrid2D.new()
+ astar_grid.size = Vector2i(32, 32)
+ astar_grid.cell_size = Vector2(16, 16)
+ astar_grid.update()
+ print(astar_grid.get_id_path(Vector2i(0, 0), Vector2i(3, 4))) # prints (0, 0), (1, 1), (2, 2), (3, 3), (3, 4)
+ print(astar_grid.get_point_path(Vector2i(0, 0), Vector2i(3, 4))) # prints (0, 0), (16, 16), (32, 32), (48, 48), (48, 64)
+ [/codeblock]
</description>
<tutorials>
</tutorials>
@@ -12,6 +23,8 @@
<param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" />
<description>
+ Called when computing the cost between two connected points.
+ Note that this function is hidden in the default [code]AStarGrid2D[/code] class.
</description>
</method>
<method name="_estimate_cost" qualifiers="virtual const">
@@ -19,11 +32,14 @@
<param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" />
<description>
+ Called when estimating the cost between a point and the path's ending point.
+ Note that this function is hidden in the default [code]AStarGrid2D[/code] class.
</description>
</method>
<method name="clear">
<return type="void" />
<description>
+ Clears the grid and sets the [member size] to [constant Vector2i.ZERO].
</description>
</method>
<method name="get_id_path">
@@ -31,6 +47,7 @@
<param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" />
<description>
+ Returns an array with the IDs of the points that form the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path.
</description>
</method>
<method name="get_point_path">
@@ -38,11 +55,14 @@
<param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" />
<description>
+ Returns an array with the points that are in the path found by AStarGrid2D between the given points. The array is ordered from the starting point to the ending point of the path.
+ [b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message.
</description>
</method>
<method name="is_dirty" qualifiers="const">
<return type="bool" />
<description>
+ Indicates that the grid parameters were changed and [method update] needs to be called.
</description>
</method>
<method name="is_in_bounds" qualifiers="const">
@@ -50,18 +70,21 @@
<param index="0" name="x" type="int" />
<param index="1" name="y" type="int" />
<description>
+ Returns [code]true[/code] if the [param x] and [param y] is a valid grid coordinate (id).
</description>
</method>
<method name="is_in_boundsv" qualifiers="const">
<return type="bool" />
<param index="0" name="id" type="Vector2i" />
<description>
+ Returns [code]true[/code] if the [param id] vector is a valid grid coordinate.
</description>
</method>
<method name="is_point_solid" qualifiers="const">
<return type="bool" />
<param index="0" name="id" type="Vector2i" />
<description>
+ Returns [code]true[/code] if a point is disabled for pathfinding. By default, all points are enabled.
</description>
</method>
<method name="set_point_solid">
@@ -69,48 +92,87 @@
<param index="0" name="id" type="Vector2i" />
<param index="1" name="solid" type="bool" default="true" />
<description>
+ Disables or enables the specified point for pathfinding. Useful for making an obstacle. By default, all points are enabled.
</description>
</method>
<method name="update">
<return type="void" />
<description>
+ Updates the internal state of the grid according to the parameters to prepare it to search the path. Needs to be called if parameters like [member size], [member cell_size] or [member offset] are changed. [method is_dirty] will return [code]true[/code] if this is the case and this needs to be called.
</description>
</method>
</methods>
<members>
<member name="cell_size" type="Vector2" setter="set_cell_size" getter="get_cell_size" default="Vector2(1, 1)">
+ The size of the point cell which will be applied to calculate the resulting point position returned by [method get_point_path]. If changed, [method update] needs to be called before finding the next path.
</member>
<member name="default_heuristic" type="int" setter="set_default_heuristic" getter="get_default_heuristic" enum="AStarGrid2D.Heuristic" default="0">
+ The default [enum Heuristic] which will be used to calculate the path if [method _compute_cost] and/or [method _estimate_cost] were not overridden.
</member>
<member name="diagonal_mode" type="int" setter="set_diagonal_mode" getter="get_diagonal_mode" enum="AStarGrid2D.DiagonalMode" default="0">
+ A specific [enum DiagonalMode] mode which will force the path to avoid or accept the specified diagonals.
</member>
<member name="jumping_enabled" type="bool" setter="set_jumping_enabled" getter="is_jumping_enabled" default="false">
+ Enables or disables jumping to skip up the intermediate points and speeds up the searching algorithm.
</member>
<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
+ The offset of the grid which will be applied to calculate the resulting point position returned by [method get_point_path]. If changed, [method update] needs to be called before finding the next path.
</member>
<member name="size" type="Vector2i" setter="set_size" getter="get_size" default="Vector2i(0, 0)">
+ The size of the grid (number of cells of size [member cell_size] on each axis). If changed, [method update] needs to be called before finding the next path.
</member>
</members>
<constants>
<constant name="HEURISTIC_EUCLIDEAN" value="0" enum="Heuristic">
+ The Euclidean heuristic to be used for the pathfinding using the following formula:
+ [codeblock]
+ dx = abs(to_id.x - from_id.x)
+ dy = abs(to_id.y - from_id.y)
+ result = sqrt(dx * dx + dy * dy)
+ [/codeblock]
</constant>
<constant name="HEURISTIC_MANHATTAN" value="1" enum="Heuristic">
+ The Manhattan heuristic to be used for the pathfinding using the following formula:
+ [codeblock]
+ dx = abs(to_id.x - from_id.x)
+ dy = abs(to_id.y - from_id.y)
+ result = dx + dy
+ [/codeblock]
</constant>
<constant name="HEURISTIC_OCTILE" value="2" enum="Heuristic">
+ The Octile heuristic to be used for the pathfinding using the following formula:
+ [codeblock]
+ dx = abs(to_id.x - from_id.x)
+ dy = abs(to_id.y - from_id.y)
+ f = sqrt(2) - 1
+ result = (dx &lt; dy) ? f * dx + dy : f * dy + dx;
+ [/codeblock]
</constant>
<constant name="HEURISTIC_CHEBYSHEV" value="3" enum="Heuristic">
+ The Chebyshev heuristic to be used for the pathfinding using the following formula:
+ [codeblock]
+ dx = abs(to_id.x - from_id.x)
+ dy = abs(to_id.y - from_id.y)
+ result = max(dx, dy)
+ [/codeblock]
</constant>
<constant name="HEURISTIC_MAX" value="4" enum="Heuristic">
+ Represents the size of the [enum Heuristic] enum.
</constant>
<constant name="DIAGONAL_MODE_ALWAYS" value="0" enum="DiagonalMode">
+ The pathfinding algorithm will ignore solid neighbors around the target cell and allow passing using diagonals.
</constant>
<constant name="DIAGONAL_MODE_NEVER" value="1" enum="DiagonalMode">
+ The pathfinding algorithm will ignore all diagonals and the way will be always orthogonal.
</constant>
<constant name="DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE" value="2" enum="DiagonalMode">
+ The pathfinding algorithm will avoid using diagonals if at least two obstacles have been placed around the neighboring cells of the specific path segment.
</constant>
<constant name="DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES" value="3" enum="DiagonalMode">
+ The pathfinding algorithm will avoid using diagonals if any obstacle has been placed around the neighboring cells of the specific path segment.
</constant>
<constant name="DIAGONAL_MODE_MAX" value="4" enum="DiagonalMode">
+ Represents the size of the [enum DiagonalMode] enum.
</constant>
</constants>
</class>
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 5fcb7706f4..e54ecd51c4 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -352,6 +352,10 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
sd.attribute_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.attribute_buffer, s.attribute_buffer_size);
}
+ if (s.skin_buffer != 0) {
+ sd.skin_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.skin_buffer, s.skin_buffer_size);
+ }
+
sd.vertex_count = s.vertex_count;
sd.index_count = s.index_count;
sd.primitive = s.primitive;
diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp
index 1c9afa8be8..cfdf743bb0 100644
--- a/editor/progress_dialog.cpp
+++ b/editor/progress_dialog.cpp
@@ -207,7 +207,9 @@ bool ProgressDialog::task_step(const String &p_task, const String &p_state, int
DisplayServer::get_singleton()->process_events();
}
+#ifndef ANDROID_ENABLED
Main::iteration(); // this will not work on a lot of platforms, so it's only meant for the editor
+#endif
return cancelled;
}
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 6427346365..454bcd2eda 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -118,20 +118,31 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod
Input::get_singleton()->parse_input_event(ev);
}
-void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector<AndroidInputHandler::TouchPos> &p_points) {
+void AndroidInputHandler::_parse_all_touch(bool p_pressed) {
+ if (touch.size()) {
+ //end all if exist
+ for (int i = 0; i < touch.size(); i++) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instantiate();
+ ev->set_index(touch[i].id);
+ ev->set_pressed(p_pressed);
+ ev->set_position(touch[i].pos);
+ Input::get_singleton()->parse_input_event(ev);
+ }
+ }
+}
+
+void AndroidInputHandler::_release_all_touch() {
+ _parse_all_touch(false);
+ touch.clear();
+}
+
+void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points) {
switch (p_event) {
case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
- if (touch.size()) {
- //end all if exist
- for (int i = 0; i < touch.size(); i++) {
- Ref<InputEventScreenTouch> ev;
- ev.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
- }
- }
+ // Release any remaining touches or mouse event
+ _release_mouse_event_info();
+ _release_all_touch();
touch.resize(p_points.size());
for (int i = 0; i < p_points.size(); i++) {
@@ -140,18 +151,13 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
}
//send touch
- for (int i = 0; i < touch.size(); i++) {
- Ref<InputEventScreenTouch> ev;
- ev.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(true);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
- }
+ _parse_all_touch(true);
} break;
case AMOTION_EVENT_ACTION_MOVE: { //motion
- ERR_FAIL_COND(touch.size() != p_points.size());
+ if (touch.size() != p_points.size()) {
+ return;
+ }
for (int i = 0; i < touch.size(); i++) {
int idx = -1;
@@ -180,18 +186,7 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
} break;
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_UP: { //release
- if (touch.size()) {
- //end all if exist
- for (int i = 0; i < touch.size(); i++) {
- Ref<InputEventScreenTouch> ev;
- ev.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
- }
- touch.clear();
- }
+ _release_all_touch();
} break;
case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch
for (int i = 0; i < p_points.size(); i++) {
@@ -229,88 +224,118 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
}
}
-void AndroidInputHandler::process_hover(int p_type, Point2 p_pos) {
- // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
- switch (p_type) {
+void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
+ if (!mouse_event_info.valid) {
+ return;
+ }
+
+ Ref<InputEventMouseButton> ev;
+ ev.instantiate();
+ _set_key_modifier_state(ev);
+ if (p_source_mouse_relative) {
+ ev->set_position(hover_prev_pos);
+ ev->set_global_position(hover_prev_pos);
+ } else {
+ ev->set_position(mouse_event_info.pos);
+ ev->set_global_position(mouse_event_info.pos);
+ hover_prev_pos = mouse_event_info.pos;
+ }
+ ev->set_pressed(p_pressed);
+ MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
+
+ buttons_state = event_buttons_mask;
+
+ ev->set_button_index(_button_index_from_mask(changed_button_mask));
+ ev->set_button_mask(event_buttons_mask);
+ ev->set_double_click(p_double_click);
+ Input::get_singleton()->parse_input_event(ev);
+}
+
+void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
+ _parse_mouse_event_info(MouseButton::NONE, false, false, p_source_mouse_relative);
+ mouse_event_info.valid = false;
+}
+
+void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) {
+ MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
+ switch (p_event_action) {
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
+ // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
Ref<InputEventMouseMotion> ev;
ev.instantiate();
_set_key_modifier_state(ev);
- ev->set_position(p_pos);
- ev->set_global_position(p_pos);
- ev->set_relative(p_pos - hover_prev_pos);
+ ev->set_position(p_event_pos);
+ ev->set_global_position(p_event_pos);
+ ev->set_relative(p_event_pos - hover_prev_pos);
Input::get_singleton()->parse_input_event(ev);
- hover_prev_pos = p_pos;
+ hover_prev_pos = p_event_pos;
} break;
- }
-}
-void AndroidInputHandler::process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
- MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
- switch (event_action) {
- case AMOTION_EVENT_ACTION_BUTTON_PRESS:
- case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
- Ref<InputEventMouseButton> ev;
- ev.instantiate();
- _set_key_modifier_state(ev);
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- } else {
- ev->set_position(hover_prev_pos);
- ev->set_global_position(hover_prev_pos);
- }
- ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
- MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS: {
+ // Release any remaining touches or mouse event
+ _release_mouse_event_info();
+ _release_all_touch();
- buttons_state = event_buttons_mask;
+ mouse_event_info.valid = true;
+ mouse_event_info.pos = p_event_pos;
+ _parse_mouse_event_info(event_buttons_mask, true, p_double_click, p_source_mouse_relative);
+ } break;
- ev->set_button_index(_button_index_from_mask(changed_button_mask));
- ev->set_button_mask(event_buttons_mask);
- Input::get_singleton()->parse_input_event(ev);
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ _release_mouse_event_info(p_source_mouse_relative);
} break;
case AMOTION_EVENT_ACTION_MOVE: {
+ if (!mouse_event_info.valid) {
+ return;
+ }
+
Ref<InputEventMouseMotion> ev;
ev.instantiate();
_set_key_modifier_state(ev);
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- ev->set_relative(event_pos - hover_prev_pos);
- hover_prev_pos = event_pos;
- } else {
+ if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
- ev->set_relative(event_pos);
+ ev->set_relative(p_event_pos);
+ } else {
+ ev->set_position(p_event_pos);
+ ev->set_global_position(p_event_pos);
+ ev->set_relative(p_event_pos - hover_prev_pos);
+ mouse_event_info.pos = p_event_pos;
+ hover_prev_pos = p_event_pos;
}
ev->set_button_mask(event_buttons_mask);
Input::get_singleton()->parse_input_event(ev);
} break;
+
case AMOTION_EVENT_ACTION_SCROLL: {
Ref<InputEventMouseButton> ev;
ev.instantiate();
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- } else {
+ _set_key_modifier_state(ev);
+ if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
+ } else {
+ ev->set_position(p_event_pos);
+ ev->set_global_position(p_event_pos);
}
ev->set_pressed(true);
buttons_state = event_buttons_mask;
- if (event_vertical_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, event_vertical_factor);
- } else if (event_vertical_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -event_vertical_factor);
+ if (p_delta.y > 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, p_delta.y);
+ } else if (p_delta.y < 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -p_delta.y);
}
- if (event_horizontal_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, event_horizontal_factor);
- } else if (event_horizontal_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -event_horizontal_factor);
+ if (p_delta.x > 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, p_delta.x);
+ } else if (p_delta.x < 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -p_delta.x);
}
} break;
}
@@ -329,18 +354,22 @@ void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, co
Input::get_singleton()->parse_input_event(evdd);
}
-void AndroidInputHandler::process_double_tap(int event_android_button_mask, Point2 p_pos) {
- MouseButton event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask);
- Ref<InputEventMouseButton> ev;
- ev.instantiate();
- _set_key_modifier_state(ev);
- ev->set_position(p_pos);
- ev->set_global_position(p_pos);
- ev->set_pressed(event_button_mask != MouseButton::NONE);
- ev->set_button_index(_button_index_from_mask(event_button_mask));
- ev->set_button_mask(event_button_mask);
- ev->set_double_click(true);
- Input::get_singleton()->parse_input_event(ev);
+void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
+ Ref<InputEventMagnifyGesture> magnify_event;
+ magnify_event.instantiate();
+ _set_key_modifier_state(magnify_event);
+ magnify_event->set_position(p_pos);
+ magnify_event->set_factor(p_factor);
+ Input::get_singleton()->parse_input_event(magnify_event);
+}
+
+void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
+ Ref<InputEventPanGesture> pan_event;
+ pan_event.instantiate();
+ _set_key_modifier_state(pan_event);
+ pan_event->set_position(p_pos);
+ pan_event->set_delta(p_delta);
+ Input::get_singleton()->parse_input_event(pan_event);
}
MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) {
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 6dfab7def8..88490f0407 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -44,6 +44,11 @@ public:
Point2 pos;
};
+ struct MouseEventInfo {
+ bool valid = false;
+ Point2 pos;
+ };
+
enum {
JOY_EVENT_BUTTON = 0,
JOY_EVENT_AXIS = 1,
@@ -68,6 +73,7 @@ private:
MouseButton buttons_state = MouseButton::NONE;
Vector<TouchPos> touch;
+ MouseEventInfo mouse_event_info;
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
@@ -77,11 +83,19 @@ private:
void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
+ void _parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative);
+
+ void _release_mouse_event_info(bool p_source_mouse_relative = false);
+
+ void _parse_all_touch(bool p_pressed);
+
+ void _release_all_touch();
+
public:
- void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
- void process_hover(int p_type, Point2 p_pos);
- void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
- void process_double_tap(int event_android_button_mask, Point2 p_pos);
+ void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative);
+ void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
+ void process_magnify(Point2 p_pos, float p_factor);
+ void process_pan(Point2 p_pos, Vector2 p_delta);
void process_joy_event(JoypadEvent p_event);
void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed);
};
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 740f3f48d3..489a81fc1a 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -77,6 +77,12 @@ open class GodotEditor : FullScreenGodotApp() {
}
super.onCreate(savedInstanceState)
+
+ // Enable long press, panning and scaling gestures
+ godotFragment?.renderView?.inputHandler?.apply {
+ enableLongPress(enableLongPressGestures())
+ enablePanningAndScalingGestures(enablePanAndScaleGestures())
+ }
}
private fun updateCommandLineParams(args: Array<String>?) {
@@ -148,6 +154,16 @@ open class GodotEditor : FullScreenGodotApp() {
*/
protected open fun overrideOrientationRequest() = true
+ /**
+ * Enable long press gestures for the Godot Android editor.
+ */
+ protected open fun enableLongPressGestures() = true
+
+ /**
+ * Enable pan and scale gestures for the Godot Android editor.
+ */
+ protected open fun enablePanAndScaleGestures() = true
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Check if we got the MANAGE_EXTERNAL_STORAGE permission
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
index 783095f93a..b9536a7066 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
@@ -35,4 +35,8 @@ package org.godotengine.editor
*/
class GodotGame : GodotEditor() {
override fun overrideOrientationRequest() = false
+
+ override fun enableLongPressGestures() = false
+
+ override fun enablePanAndScaleGestures() = false
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 28e689e63a..6ebe7e4ee9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -1023,7 +1023,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
@Keep
- private GodotRenderView getRenderView() { // used by native side to get renderView
+ public GodotRenderView getRenderView() { // used by native side to get renderView
return mRenderView;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 08da1b1832..513021f1d1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -31,7 +31,6 @@
package org.godotengine.godot;
import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.gl.GodotRenderer;
-import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils;
import org.godotengine.godot.xr.XRMode;
@@ -46,7 +45,6 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
-import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
@@ -75,7 +73,6 @@ import androidx.annotation.Keep;
public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final Godot godot;
private final GodotInputHandler inputHandler;
- private final GestureDetector detector;
private final GodotRenderer godotRenderer;
private PointerIcon pointerIcon;
@@ -85,7 +82,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
this.godot = godot;
this.inputHandler = new GodotInputHandler(this);
- this.detector = new GestureDetector(context, new GodotGestureHandler(this));
this.godotRenderer = new GodotRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
@@ -132,7 +128,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
- this.detector.onTouchEvent(event);
return inputHandler.onTouchEvent(event);
}
@@ -156,6 +151,24 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return inputHandler.onGenericMotionEvent(event);
}
+ @Override
+ public void onPointerCaptureChange(boolean hasCapture) {
+ super.onPointerCaptureChange(hasCapture);
+ inputHandler.onPointerCaptureChange(hasCapture);
+ }
+
+ @Override
+ public void requestPointerCapture() {
+ super.requestPointerCapture();
+ inputHandler.onPointerCaptureChange(true);
+ }
+
+ @Override
+ public void releasePointerCapture() {
+ super.releasePointerCapture();
+ inputHandler.onPointerCaptureChange(false);
+ }
+
/**
* called from JNI to change pointer icon
*/
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index f855fc6cf6..d4fa12ed15 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -92,7 +92,7 @@ public class GodotLib {
public static native void newcontext(Surface p_surface);
/**
- * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.
+ * Forward {@link Activity#onBackPressed()} event.
*/
public static native void back();
@@ -108,63 +108,60 @@ public class GodotLib {
public static native void ttsCallback(int event, int id, int pos);
/**
- * Forward touch events from the main thread to the GL thread.
+ * Forward touch events.
*/
- public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions);
- public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask);
- public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask, float verticalFactor, float horizontalFactor);
+ public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions);
/**
- * Forward hover events from the main thread to the GL thread.
+ * Dispatch mouse events
*/
- public static native void hover(int type, float x, float y);
+ public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative);
- /**
- * Forward double_tap events from the main thread to the GL thread.
- */
- public static native void doubleTap(int buttonMask, int x, int y);
+ public static native void magnify(float x, float y, float factor);
+
+ public static native void pan(float x, float y, float deltaX, float deltaY);
/**
- * Forward accelerometer sensor events from the main thread to the GL thread.
+ * Forward accelerometer sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void accelerometer(float x, float y, float z);
/**
- * Forward gravity sensor events from the main thread to the GL thread.
+ * Forward gravity sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void gravity(float x, float y, float z);
/**
- * Forward magnetometer sensor events from the main thread to the GL thread.
+ * Forward magnetometer sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void magnetometer(float x, float y, float z);
/**
- * Forward gyroscope sensor events from the main thread to the GL thread.
+ * Forward gyroscope sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void gyroscope(float x, float y, float z);
/**
- * Forward regular key events from the main thread to the GL thread.
+ * Forward regular key events.
*/
public static native void key(int p_keycode, int p_physical_keycode, int p_unicode, boolean p_pressed);
/**
- * Forward game device's key events from the main thread to the GL thread.
+ * Forward game device's key events.
*/
public static native void joybutton(int p_device, int p_but, boolean p_pressed);
/**
- * Forward joystick devices axis motion events from the main thread to the GL thread.
+ * Forward joystick devices axis motion events.
*/
public static native void joyaxis(int p_device, int p_axis, float p_value);
/**
- * Forward joystick devices hat motion events from the main thread to the GL thread.
+ * Forward joystick devices hat motion events.
*/
public static native void joyhat(int p_device, int p_hat_x, int p_hat_y);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index c386a2d2eb..fa6c3280b9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -30,7 +30,6 @@
package org.godotengine.godot;
-import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.vulkan.VkRenderer;
import org.godotengine.godot.vulkan.VkSurfaceView;
@@ -38,7 +37,6 @@ import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
-import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
@@ -49,7 +47,6 @@ import androidx.annotation.Keep;
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final Godot godot;
private final GodotInputHandler mInputHandler;
- private final GestureDetector mGestureDetector;
private final VkRenderer mRenderer;
private PointerIcon pointerIcon;
@@ -58,7 +55,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
this.godot = godot;
mInputHandler = new GodotInputHandler(this);
- mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
mRenderer = new VkRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
@@ -106,7 +102,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
- mGestureDetector.onTouchEvent(event);
return mInputHandler.onTouchEvent(event);
}
@@ -130,6 +125,24 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
return mInputHandler.onGenericMotionEvent(event);
}
+ @Override
+ public void requestPointerCapture() {
+ super.requestPointerCapture();
+ mInputHandler.onPointerCaptureChange(true);
+ }
+
+ @Override
+ public void releasePointerCapture() {
+ super.releasePointerCapture();
+ mInputHandler.onPointerCaptureChange(false);
+ }
+
+ @Override
+ public void onPointerCaptureChange(boolean hasCapture) {
+ super.onPointerCaptureChange(hasCapture);
+ mInputHandler.onPointerCaptureChange(hasCapture);
+ }
+
/**
* called from JNI to change pointer icon
*/
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
deleted file mode 100644
index 778efa914a..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*************************************************************************/
-/* GodotGestureHandler.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 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.input;
-
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.GodotRenderView;
-
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-/**
- * Handles gesture input related events for the {@link GodotRenderView} view.
- * https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
- */
-public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener {
- private final GodotRenderView mRenderView;
-
- public GodotGestureHandler(GodotRenderView godotView) {
- mRenderView = godotView;
- }
-
- private void queueEvent(Runnable task) {
- mRenderView.queueOnRenderThread(task);
- }
-
- @Override
- public boolean onDown(MotionEvent event) {
- super.onDown(event);
- //Log.i("GodotGesture", "onDown");
- return true;
- }
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent event) {
- super.onSingleTapConfirmed(event);
- return true;
- }
-
- @Override
- public void onLongPress(MotionEvent event) {
- //Log.i("GodotGesture", "onLongPress");
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent event) {
- //Log.i("GodotGesture", "onDoubleTap");
- final int x = Math.round(event.getX());
- final int y = Math.round(event.getY());
- final int buttonMask = event.getButtonState();
- GodotLib.doubleTap(buttonMask, x, y);
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
- //Log.i("GodotGesture", "onFling");
- return true;
- }
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
new file mode 100644
index 0000000000..9715c31fc1
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -0,0 +1,289 @@
+/*************************************************************************/
+/* GodotGestureHandler.kt */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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.input
+
+import android.os.Build
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.ScaleGestureDetector
+import android.view.ScaleGestureDetector.OnScaleGestureListener
+import org.godotengine.godot.GodotLib
+
+/**
+ * Handles regular and scale gesture input related events for the [GodotView] view.
+ *
+ * @See https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
+ * @See https://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener
+ */
+internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureListener {
+
+ companion object {
+ private val TAG = GodotGestureHandler::class.java.simpleName
+ }
+
+ /**
+ * Enable pan and scale gestures
+ */
+ var panningAndScalingEnabled = false
+
+ private var doubleTapInProgress = false
+ private var dragInProgress = false
+ private var scaleInProgress = false
+ private var contextClickInProgress = false
+ private var pointerCaptureInProgress = false
+
+ override fun onDown(event: MotionEvent): Boolean {
+ // Don't send / register a down event while we're in the middle of a double-tap
+ if (!doubleTapInProgress) {
+ // Send the down event
+ GodotInputHandler.handleMotionEvent(event)
+ }
+ return true
+ }
+
+ override fun onSingleTapUp(event: MotionEvent): Boolean {
+ GodotInputHandler.handleMotionEvent(event)
+ return true
+ }
+
+ override fun onLongPress(event: MotionEvent) {
+ contextClickRouter(event)
+ }
+
+ private fun contextClickRouter(event: MotionEvent) {
+ if (scaleInProgress) {
+ return
+ }
+
+ // Cancel the previous down event
+ GodotInputHandler.handleMotionEvent(
+ event.source,
+ MotionEvent.ACTION_CANCEL,
+ event.buttonState,
+ event.x,
+ event.y
+ )
+
+ // Turn a context click into a single tap right mouse button click.
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_DOWN,
+ MotionEvent.BUTTON_SECONDARY,
+ event.x,
+ event.y
+ )
+ contextClickInProgress = true
+ }
+
+ fun onPointerCaptureChange(hasCapture: Boolean) {
+ if (pointerCaptureInProgress == hasCapture) {
+ return
+ }
+
+ if (!hasCapture) {
+ // Dispatch a mouse relative ACTION_UP event to signal the end of the capture
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_UP,
+ 0,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ false,
+ true
+ )
+ }
+ pointerCaptureInProgress = hasCapture
+ }
+
+ fun onMotionEvent(event: MotionEvent): Boolean {
+ return when (event.actionMasked) {
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_BUTTON_RELEASE -> {
+ onActionUp(event)
+ }
+ MotionEvent.ACTION_MOVE -> {
+ onActionMove(event)
+ }
+ else -> false
+ }
+ }
+
+ private fun onActionUp(event: MotionEvent): Boolean {
+ val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
+ } else {
+ false
+ }
+ when {
+ pointerCaptureInProgress -> {
+ return if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
+ // Don't dispatch the ACTION_CANCEL while a capture is in progress
+ true
+ } else {
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_UP,
+ event.buttonState,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ pointerCaptureInProgress = false
+ true
+ }
+ }
+ dragInProgress -> {
+ GodotInputHandler.handleMotionEvent(event)
+ dragInProgress = false
+ return true
+ }
+ contextClickInProgress -> {
+ GodotInputHandler.handleMouseEvent(
+ event.actionMasked,
+ 0,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ contextClickInProgress = false
+ return true
+ }
+ else -> return false
+ }
+ }
+
+ private fun onActionMove(event: MotionEvent): Boolean {
+ if (contextClickInProgress) {
+ val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
+ } else {
+ false
+ }
+ GodotInputHandler.handleMouseEvent(
+ event.actionMasked,
+ MotionEvent.BUTTON_SECONDARY,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ return true
+ }
+ return false
+ }
+
+ override fun onDoubleTapEvent(event: MotionEvent): Boolean {
+ if (event.actionMasked == MotionEvent.ACTION_UP) {
+ doubleTapInProgress = false
+ }
+ return true
+ }
+
+ override fun onDoubleTap(event: MotionEvent): Boolean {
+ doubleTapInProgress = true
+ val x = event.x
+ val y = event.y
+ val buttonMask =
+ if (GodotInputHandler.isMouseEvent(event)) {
+ event.buttonState
+ } else {
+ MotionEvent.BUTTON_PRIMARY
+ }
+ GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_DOWN, buttonMask, x, y, true)
+ GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, 0, x, y, false)
+
+ return true
+ }
+
+ override fun onScroll(
+ originEvent: MotionEvent,
+ terminusEvent: MotionEvent,
+ distanceX: Float,
+ distanceY: Float
+ ): Boolean {
+ if (scaleInProgress) {
+ if (dragInProgress) {
+ // Cancel the drag
+ GodotInputHandler.handleMotionEvent(
+ originEvent.source,
+ MotionEvent.ACTION_CANCEL,
+ originEvent.buttonState,
+ originEvent.x,
+ originEvent.y
+ )
+ dragInProgress = false
+ }
+ return true
+ }
+
+ dragInProgress = true
+
+ val x = terminusEvent.x
+ val y = terminusEvent.y
+ if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled) {
+ GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
+ } else {
+ GodotInputHandler.handleMotionEvent(terminusEvent)
+ }
+ return true
+ }
+
+ override fun onScale(detector: ScaleGestureDetector?): Boolean {
+ if (detector == null || !panningAndScalingEnabled) {
+ return false
+ }
+ GodotLib.magnify(
+ detector.focusX,
+ detector.focusY,
+ detector.scaleFactor
+ )
+ return true
+ }
+
+ override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
+ if (detector == null || !panningAndScalingEnabled) {
+ return false
+ }
+ scaleInProgress = true
+ return true
+ }
+
+ override fun onScaleEnd(detector: ScaleGestureDetector?) {
+ scaleInProgress = false
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index da15b2490c..03cb8034fa 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -41,13 +41,13 @@ import android.os.Build;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.GestureDetector;
import android.view.InputDevice;
-import android.view.InputDevice.MotionRange;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
@@ -55,21 +55,49 @@ import java.util.Set;
* Handles input related events for the {@link GodotRenderView} view.
*/
public class GodotInputHandler implements InputManager.InputDeviceListener {
- private final GodotRenderView mRenderView;
- private final InputManager mInputManager;
-
- private final String tag = this.getClass().getSimpleName();
+ private static final String TAG = GodotInputHandler.class.getSimpleName();
private final SparseIntArray mJoystickIds = new SparseIntArray(4);
private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
+ private final GodotRenderView mRenderView;
+ private final InputManager mInputManager;
+ private final GestureDetector gestureDetector;
+ private final ScaleGestureDetector scaleGestureDetector;
+ private final GodotGestureHandler godotGestureHandler;
+
public GodotInputHandler(GodotRenderView godotView) {
+ final Context context = godotView.getView().getContext();
mRenderView = godotView;
- mInputManager = (InputManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_SERVICE);
+ mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
mInputManager.registerInputDeviceListener(this, null);
+
+ this.godotGestureHandler = new GodotGestureHandler();
+ this.gestureDetector = new GestureDetector(context, godotGestureHandler);
+ this.gestureDetector.setIsLongpressEnabled(false);
+ this.scaleGestureDetector = new ScaleGestureDetector(context, godotGestureHandler);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ this.scaleGestureDetector.setStylusScaleEnabled(true);
+ }
}
- private boolean isKeyEvent_GameDevice(int source) {
+ /**
+ * Enable long press events. This is false by default.
+ */
+ public void enableLongPress(boolean enable) {
+ this.gestureDetector.setIsLongpressEnabled(enable);
+ }
+
+ /**
+ * Enable multi-fingers pan & scale gestures. This is false by default.
+ *
+ * Note: This may interfere with multi-touch handling / support.
+ */
+ public void enablePanningAndScalingGestures(boolean enable) {
+ this.godotGestureHandler.setPanningAndScalingEnabled(enable);
+ }
+
+ private boolean isKeyEventGameDevice(int source) {
// Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD)
if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD))
return false;
@@ -77,6 +105,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
}
+ public void onPointerCaptureChange(boolean hasCapture) {
+ godotGestureHandler.onPointerCaptureChange(hasCapture);
+ }
+
public boolean onKeyUp(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
@@ -87,7 +119,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
int source = event.getSource();
- if (isKeyEvent_GameDevice(source)) {
+ if (isKeyEventGameDevice(source)) {
// Check if the device exists
final int deviceId = event.getDeviceId();
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
@@ -121,11 +153,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
int source = event.getSource();
- //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
final int deviceId = event.getDeviceId();
// Check if source is a game device and that the device is a registered gamepad
- if (isKeyEvent_GameDevice(source)) {
+ if (isKeyEventGameDevice(source)) {
if (event.getRepeatCount() > 0) // ignore key echo
return true;
@@ -145,47 +176,41 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
public boolean onTouchEvent(final MotionEvent event) {
- // Mouse drag (mouse pressed and move) doesn't fire onGenericMotionEvent so this is needed
- if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
- if (event.getAction() != MotionEvent.ACTION_MOVE) {
- // we return true because every time a mouse event is fired, the event is already handled
- // in onGenericMotionEvent, so by touch event we can say that the event is also handled
- return true;
- }
- return handleMouseEvent(event);
+ this.scaleGestureDetector.onTouchEvent(event);
+ if (this.gestureDetector.onTouchEvent(event)) {
+ // The gesture detector has handled the event.
+ return true;
}
- final int evcount = event.getPointerCount();
- if (evcount == 0)
+ if (godotGestureHandler.onMotionEvent(event)) {
+ // The gesture handler has handled the event.
return true;
+ }
- if (mRenderView != null) {
- final float[] arr = new float[event.getPointerCount() * 3]; // pointerId1, x1, y1, pointerId2, etc...
+ // Drag events are handled by the [GodotGestureHandler]
+ if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
+ return true;
+ }
- for (int i = 0; i < event.getPointerCount(); i++) {
- arr[i * 3 + 0] = event.getPointerId(i);
- arr[i * 3 + 1] = event.getX(i);
- arr[i * 3 + 2] = event.getY(i);
- }
- final int action = event.getActionMasked();
- final int pointer_idx = event.getPointerId(event.getActionIndex());
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_POINTER_DOWN: {
- GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr);
- } break;
- }
+ if (isMouseEvent(event)) {
+ return handleMouseEvent(event);
}
- return true;
+
+ return handleTouchEvent(event);
}
public boolean onGenericMotionEvent(MotionEvent event) {
- if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getAction() == MotionEvent.ACTION_MOVE) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
+ // The gesture detector has handled the event.
+ return true;
+ }
+
+ if (godotGestureHandler.onMotionEvent(event)) {
+ // The gesture handler has handled the event.
+ return true;
+ }
+
+ if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Check if the device exists
final int deviceId = event.getDeviceId();
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
@@ -198,15 +223,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
for (int i = 0; i < joystick.axes.size(); i++) {
final int axis = joystick.axes.get(i);
final float value = event.getAxisValue(axis);
- /**
- * As all axes are polled for each event, only fire an axis event if the value has actually changed.
- * Prevents flooding Godot with repeated events.
+ /*
+ As all axes are polled for each event, only fire an axis event if the value has actually changed.
+ Prevents flooding Godot with repeated events.
*/
if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
// save value to prevent repeats
joystick.axesValues.put(axis, value);
- final int godotAxisIdx = i;
- GodotLib.joyaxis(godotJoyId, godotAxisIdx, value);
+ GodotLib.joyaxis(godotJoyId, i, value);
}
}
@@ -221,17 +245,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
return true;
}
- } else if (event.isFromSource(InputDevice.SOURCE_STYLUS)) {
- final float x = event.getX();
- final float y = event.getY();
- final int type = event.getAction();
- GodotLib.hover(type, x, y);
- return true;
-
- } else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return handleMouseEvent(event);
- }
+ } else if (isMouseEvent(event)) {
+ return handleMouseEvent(event);
}
return false;
@@ -243,7 +258,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
for (int deviceId : deviceIds) {
InputDevice device = mInputManager.getInputDevice(deviceId);
if (DEBUG) {
- Log.v("GodotInputHandler", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
+ Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
}
onInputDeviceAdded(deviceId);
}
@@ -288,13 +303,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
joystick.name = device.getName();
//Helps with creating new joypad mappings.
- Log.i(tag, "=== New Input Device: " + joystick.name);
+ Log.i(TAG, "=== New Input Device: " + joystick.name);
Set<Integer> already = new HashSet<>();
for (InputDevice.MotionRange range : device.getMotionRanges()) {
boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
- //Log.i(tag, "axis: "+range.getAxis()+ ", isJoystick: "+isJoystick+", isGamepad: "+isGamepad);
if (!isJoystick && !isGamepad) {
continue;
}
@@ -306,14 +320,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
already.add(axis);
joystick.axes.add(axis);
} else {
- Log.w(tag, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
+ Log.w(TAG, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
}
}
}
Collections.sort(joystick.axes);
for (int idx = 0; idx < joystick.axes.size(); idx++) {
//Helps with creating new joypad mappings.
- Log.i(tag, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
+ Log.i(TAG, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
}
mJoysticksDevices.put(deviceId, joystick);
@@ -338,13 +352,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
onInputDeviceAdded(deviceId);
}
- private static class RangeComparator implements Comparator<MotionRange> {
- @Override
- public int compare(MotionRange arg0, MotionRange arg1) {
- return arg0.getAxis() - arg1.getAxis();
- }
- }
-
public static int getGodotButton(int keyCode) {
int button;
switch (keyCode) {
@@ -410,39 +417,113 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return button;
}
- private boolean handleMouseEvent(final MotionEvent event) {
- switch (event.getActionMasked()) {
+ static boolean isMouseEvent(MotionEvent event) {
+ return isMouseEvent(event.getSource());
+ }
+
+ private static boolean isMouseEvent(int eventSource) {
+ boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE);
+ }
+ return mouseSource;
+ }
+
+ static boolean handleMotionEvent(final MotionEvent event) {
+ if (isMouseEvent(event)) {
+ return handleMouseEvent(event);
+ }
+
+ return handleTouchEvent(event);
+ }
+
+ static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) {
+ return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0);
+ }
+
+ static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY) {
+ if (isMouseEvent(eventSource)) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, false, false);
+ }
+
+ return handleTouchEvent(eventAction, x, y);
+ }
+
+ static boolean handleMouseEvent(final MotionEvent event) {
+ final int eventAction = event.getActionMasked();
+ final float x = event.getX();
+ final float y = event.getY();
+ final int buttonsMask = event.getButtonState();
+
+ final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ boolean sourceMouseRelative = false;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE);
+ }
+ return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick, false);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
+ switch (eventAction) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // Zero-up the button state
+ buttonsMask = 0;
+ // FALL THROUGH
+ case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_EXIT:
case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_HOVER_EXIT: {
- final float x = event.getX();
- final float y = event.getY();
- final int type = event.getAction();
- GodotLib.hover(type, x, y);
- return true;
- }
- case MotionEvent.ACTION_BUTTON_PRESS:
- case MotionEvent.ACTION_BUTTON_RELEASE:
- case MotionEvent.ACTION_MOVE: {
- final float x = event.getX();
- final float y = event.getY();
- final int buttonsMask = event.getButtonState();
- final int action = event.getAction();
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask);
- return true;
- }
+ case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: {
- final float x = event.getX();
- final float y = event.getY();
- final int buttonsMask = event.getButtonState();
- final int action = event.getAction();
- final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
- final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor);
+ GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative);
+ return true;
}
+ }
+ return false;
+ }
+
+ static boolean handleTouchEvent(final MotionEvent event) {
+ final int pointerCount = event.getPointerCount();
+ if (pointerCount == 0) {
+ return true;
+ }
+
+ final float[] positions = new float[pointerCount * 3]; // pointerId1, x1, y1, pointerId2, etc...
+
+ for (int i = 0; i < pointerCount; i++) {
+ positions[i * 3 + 0] = event.getPointerId(i);
+ positions[i * 3 + 1] = event.getX(i);
+ positions[i * 3 + 2] = event.getY(i);
+ }
+ final int action = event.getActionMasked();
+ final int actionPointerId = event.getPointerId(event.getActionIndex());
+
+ return handleTouchEvent(action, actionPointerId, pointerCount, positions);
+ }
+
+ static boolean handleTouchEvent(int eventAction, float x, float y) {
+ return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y });
+ }
+
+ static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions) {
+ switch (eventAction) {
case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_UP: {
- // we can safely ignore these cases because they are always come beside ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions);
return true;
}
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 422c05e5ce..f01bd9c72a 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -254,7 +254,17 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
return should_swap_buffers;
}
-void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
+// Called on the UI thread
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative) {
+ if (step.get() <= 0) {
+ return;
+ }
+
+ input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative);
+}
+
+// Called on the UI thread
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
if (step.get() <= 0) {
return;
}
@@ -262,50 +272,30 @@ void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev,
Vector<AndroidInputHandler::TouchPos> points;
for (int i = 0; i < pointer_count; i++) {
jfloat p[3];
- env->GetFloatArrayRegion(positions, i * 3, 3, p);
+ env->GetFloatArrayRegion(position, i * 3, 3, p);
AndroidInputHandler::TouchPos tp;
tp.pos = Point2(p[1], p[2]);
tp.id = (int)p[0];
points.push_back(tp);
}
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE || (input_device & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE) {
- input_handler->process_mouse_event(input_device, ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
- } else {
- input_handler->process_touch(ev, pointer, points);
- }
-}
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
- touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position);
+ input_handler->process_touch_event(ev, pointer, points);
}
// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask) {
- touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask);
-}
-
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
- touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask, vertical_factor, horizontal_factor);
-}
-
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor) {
if (step.get() <= 0) {
return;
}
-
- input_handler->process_hover(p_type, Point2(p_x, p_y));
+ input_handler->process_magnify(Point2(p_x, p_y), p_factor);
}
// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y) {
if (step.get() <= 0) {
return;
}
-
- input_handler->process_double_tap(p_button_mask, Point2(p_x, p_y));
+ input_handler->process_pan(Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y));
}
// Called on the UI thread
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 3c48ca0459..e2ed006462 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -45,12 +45,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
-void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 72f5de170c..c936fe9738 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2563,7 +2563,9 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
void RichTextLabel::_thread_function(void *self) {
RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self);
+ rtl->set_physics_process_internal(true);
rtl->_process_line_caches();
+ rtl->set_physics_process_internal(false);
rtl->updating.store(false);
rtl->call_deferred(SNAME("queue_redraw"));
}
@@ -2679,7 +2681,6 @@ bool RichTextLabel::_validate_line_caches() {
loaded.store(true);
thread.start(RichTextLabel::_thread_function, reinterpret_cast<void *>(this));
loading_started = OS::get_singleton()->get_ticks_msec();
- set_physics_process_internal(true);
return false;
} else {
_process_line_caches();
@@ -4367,12 +4368,12 @@ int RichTextLabel::get_visible_paragraph_count() const {
}
void RichTextLabel::scroll_to_line(int p_line) {
- _validate_line_caches();
-
if (p_line <= 0) {
vscroll->set_value(0);
return;
}
+ _validate_line_caches();
+
int line_count = 0;
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 2bbc5e4dfb..e2519ba8d1 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -257,6 +257,7 @@ enum ContextFlag : uint32_t {
CF_UNIFORM_KEYWORD = 2048U, // "uniform"
CF_CONST_KEYWORD = 4096U, // "const"
CF_UNIFORM_QUALIFIER = 8192U, // "<x> uniform float t;"
+ CF_SHADER_TYPE = 16384U, // "shader_type"
};
const uint32_t KCF_DATATYPE = CF_BLOCK | CF_GLOBAL_SPACE | CF_DATATYPE | CF_FUNC_DECL_PARAM_TYPE | CF_UNIFORM_TYPE;
@@ -318,7 +319,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_VARYING, "varying", CF_GLOBAL_SPACE, { "particles", "sky", "fog" }, {} },
{ TK_CONST, "const", CF_BLOCK | CF_GLOBAL_SPACE | CF_CONST_KEYWORD, {}, {} },
{ TK_STRUCT, "struct", CF_GLOBAL_SPACE, {}, {} },
- { TK_SHADER_TYPE, "shader_type", CF_GLOBAL_SPACE, {}, {} },
+ { TK_SHADER_TYPE, "shader_type", CF_SHADER_TYPE, {}, {} },
{ TK_RENDER_MODE, "render_mode", CF_GLOBAL_SPACE, {}, {} },
// uniform qualifiers
@@ -1183,7 +1184,7 @@ void ShaderLanguage::clear() {
include_positions.push_back(FilePosition());
#ifdef DEBUG_ENABLED
- keyword_completion_context = CF_GLOBAL_SPACE;
+ keyword_completion_context = CF_UNSPECIFIED;
used_constants.clear();
used_varyings.clear();
used_uniforms.clear();
@@ -7821,6 +7822,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
Token next;
if (!is_shader_inc) {
+#ifdef DEBUG_ENABLED
+ keyword_completion_context = CF_SHADER_TYPE;
+#endif // DEBUG_ENABLED
tk = _get_token();
if (tk.type != TK_SHADER_TYPE) {