summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/Control.xml12
-rw-r--r--doc/classes/Viewport.xml2
-rw-r--r--editor/editor_run.cpp4
-rw-r--r--editor/editor_settings.cpp8
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt50
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java30
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java38
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java22
-rw-r--r--platform/android/java_godot_wrapper.cpp19
-rw-r--r--platform/android/java_godot_wrapper.h4
-rw-r--r--platform/android/os_android.cpp12
-rw-r--r--platform/android/os_android.h1
-rw-r--r--scene/gui/control.cpp24
-rw-r--r--scene/main/canvas_item.cpp12
-rw-r--r--scene/main/viewport.cpp72
-rw-r--r--scene/main/viewport.h2
-rw-r--r--scene/main/window.cpp13
-rw-r--r--scene/main/window.h1
18 files changed, 207 insertions, 119 deletions
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 6f665ff98a..0d675112b8 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -383,7 +383,9 @@
<method name="get_global_rect" qualifiers="const">
<return type="Rect2" />
<description>
- Returns the position and size of the control relative to the [CanvasLayer]. See [member global_position] and [member size].
+ Returns the position and size of the control relative to the containing canvas. See [member global_position] and [member size].
+ [b]Note:[/b] If the node itself or any parent [CanvasItem] between the node and the canvas have a non default rotation or skew, the resulting size is likely not meaningful.
+ [b]Note:[/b] Setting [member Viewport.gui_snap_controls_to_pixels] to [code]true[/code] can lead to rounding inaccuracies between the displayed control and the returned [Rect2].
</description>
</method>
<method name="get_minimum_size" qualifiers="const">
@@ -414,7 +416,9 @@
<method name="get_rect" qualifiers="const">
<return type="Rect2" />
<description>
- Returns the position and size of the control relative to the top-left corner of the parent Control. See [member position] and [member size].
+ Returns the position and size of the control in the coordinate system of the containing node. See [member position], [member scale] and [member size].
+ [b]Note:[/b] If [member rotation] is not the default rotation, the resulting size is not meaningful.
+ [b]Note:[/b] Setting [member Viewport.gui_snap_controls_to_pixels] to [code]true[/code] can lead to rounding inaccuracies between the displayed control and the returned [Rect2].
</description>
</method>
<method name="get_screen_position" qualifiers="const">
@@ -992,7 +996,7 @@
By default, the node's pivot is its top-left corner. When you change its [member rotation] or [member scale], it will rotate or scale around this pivot. Set this property to [member size] / 2 to pivot around the Control's center.
</member>
<member name="position" type="Vector2" setter="_set_position" getter="get_position" default="Vector2(0, 0)">
- The node's position, relative to its parent. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset].
+ The node's position, relative to its containing node. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset].
</member>
<member name="rotation" type="float" setter="set_rotation" getter="get_rotation" default="0.0">
The node's rotation around its pivot, in radians. See [member pivot_offset] to change the pivot's position.
@@ -1009,7 +1013,7 @@
The [Node] which must be a parent of the focused [Control] for the shortcut to be activated. If [code]null[/code], the shortcut can be activated when any control is focused (a global shortcut). This allows shortcuts to be accepted only when the user has a certain area of the GUI focused.
</member>
<member name="size" type="Vector2" setter="_set_size" getter="get_size" default="Vector2(0, 0)">
- The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically.
+ The size of the node's bounding rectangle, in the node's coordinate system. [Container] nodes update this property automatically.
</member>
<member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" enum="Control.SizeFlags" default="1">
Tells the parent [Container] nodes how they should resize and place the node on the X axis. Use one of the [enum SizeFlags] constants to change the flags. See the constants to learn what each does.
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 84d3d818c3..ab2de14638 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -55,7 +55,7 @@
<method name="get_final_transform" qualifiers="const">
<return type="Transform2D" />
<description>
- Returns the total transform of the viewport.
+ Returns the transform from the viewport's coordinate system to the embedder's coordinate system.
</description>
</method>
<method name="get_mouse_position" qualifiers="const">
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index 4bcd91376a..d3cceee1a3 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -272,7 +272,9 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
OS::ProcessID pid = 0;
Error err = OS::get_singleton()->create_instance(args, &pid);
ERR_FAIL_COND_V(err, err);
- pids.push_back(pid);
+ if (pid != 0) {
+ pids.push_back(pid);
+ }
}
status = STATUS_PLAY;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 4879790b74..21e15bc996 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -474,10 +474,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
/* Filesystem */
// External Programs
- EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/raster_image_editor", "", "")
- EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/vector_image_editor", "", "")
- EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/audio_editor", "", "")
- EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/3d_model_editor", "", "")
+ EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/raster_image_editor", "", "")
+ EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/vector_image_editor", "", "")
+ EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/audio_editor", "", "")
+ EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/3d_model_editor", "", "")
// Directories
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/directories/autoscan_project_path", "", "")
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 f848089aa8..71385315ae 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
@@ -31,12 +31,11 @@
package org.godotengine.editor
import android.Manifest
+import android.app.ActivityManager
+import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.os.Debug
-import android.os.Environment
+import android.os.*
import android.util.Log
import android.widget.Toast
import androidx.window.layout.WindowMetricsCalculator
@@ -64,11 +63,18 @@ open class GodotEditor : FullScreenGodotApp() {
private const val COMMAND_LINE_PARAMS = "command_line_params"
+ private const val EDITOR_ID = 777
private const val EDITOR_ARG = "--editor"
private const val EDITOR_ARG_SHORT = "-e"
+ private const val EDITOR_PROCESS_NAME_SUFFIX = ":GodotEditor"
+ private const val GAME_ID = 667
+ private const val GAME_PROCESS_NAME_SUFFIX = ":GodotGame"
+
+ private const val PROJECT_MANAGER_ID = 555
private const val PROJECT_MANAGER_ARG = "--project-manager"
private const val PROJECT_MANAGER_ARG_SHORT = "-p"
+ private const val PROJECT_MANAGER_PROCESS_NAME_SUFFIX = ":GodotProjectManager"
}
private val commandLineParams = ArrayList<String>()
@@ -102,9 +108,10 @@ open class GodotEditor : FullScreenGodotApp() {
override fun getCommandLine() = commandLineParams
- override fun onNewGodotInstanceRequested(args: Array<String>) {
+ override fun onNewGodotInstanceRequested(args: Array<String>): Int {
// Parse the arguments to figure out which activity to start.
var targetClass: Class<*> = GodotGame::class.java
+ var instanceId = GAME_ID
// Whether we should launch the new godot instance in an adjacent window
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
@@ -115,12 +122,14 @@ open class GodotEditor : FullScreenGodotApp() {
if (EDITOR_ARG == arg || EDITOR_ARG_SHORT == arg) {
targetClass = GodotEditor::class.java
launchAdjacent = false
+ instanceId = EDITOR_ID
break
}
if (PROJECT_MANAGER_ARG == arg || PROJECT_MANAGER_ARG_SHORT == arg) {
targetClass = GodotProjectManager::class.java
launchAdjacent = false
+ instanceId = PROJECT_MANAGER_ID
break
}
}
@@ -139,6 +148,37 @@ open class GodotEditor : FullScreenGodotApp() {
Log.d(TAG, "Starting $targetClass")
startActivity(newInstance)
}
+ return instanceId
+ }
+
+ override fun onGodotForceQuit(godotInstanceId: Int): Boolean {
+ val processNameSuffix = when (godotInstanceId) {
+ GAME_ID -> {
+ GAME_PROCESS_NAME_SUFFIX
+ }
+ EDITOR_ID -> {
+ EDITOR_PROCESS_NAME_SUFFIX
+ }
+ PROJECT_MANAGER_ID -> {
+ PROJECT_MANAGER_PROCESS_NAME_SUFFIX
+ }
+ else -> ""
+ }
+ if (processNameSuffix.isBlank()) {
+ return false
+ }
+
+ val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+ val runningProcesses = activityManager.runningAppProcesses
+ for (runningProcess in runningProcesses) {
+ if (runningProcess.processName.endsWith(processNameSuffix)) {
+ Log.v(TAG, "Killing Godot process ${runningProcess.processName}")
+ Process.killProcess(runningProcess.pid)
+ return true
+ }
+ }
+
+ return false
}
// Get the screen's density scale
diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
index 863e67f1e9..65032d6a68 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
@@ -74,28 +74,36 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God
public void onDestroy() {
Log.v(TAG, "Destroying Godot app...");
super.onDestroy();
- onGodotForceQuit(godotFragment);
+ terminateGodotInstance(godotFragment);
}
@Override
public final void onGodotForceQuit(Godot instance) {
+ runOnUiThread(() -> {
+ terminateGodotInstance(instance);
+ });
+ }
+
+ private void terminateGodotInstance(Godot instance) {
if (instance == godotFragment) {
Log.v(TAG, "Force quitting Godot instance");
- ProcessPhoenix.forceQuit(this);
+ ProcessPhoenix.forceQuit(FullScreenGodotApp.this);
}
}
@Override
public final void onGodotRestartRequested(Godot instance) {
- if (instance == godotFragment) {
- // It's very hard to properly de-initialize Godot on Android to restart the game
- // from scratch. Therefore, we need to kill the whole app process and relaunch it.
- //
- // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
- // releasing and reloading native libs or resetting their state somehow and clearing statics).
- Log.v(TAG, "Restarting Godot instance...");
- ProcessPhoenix.triggerRebirth(this);
- }
+ runOnUiThread(() -> {
+ if (instance == godotFragment) {
+ // It's very hard to properly de-initialize Godot on Android to restart the game
+ // from scratch. Therefore, we need to kill the whole app process and relaunch it.
+ //
+ // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
+ // releasing and reloading native libs or resetting their state somehow and clearing statics).
+ Log.v(TAG, "Restarting Godot instance...");
+ ProcessPhoenix.triggerRebirth(FullScreenGodotApp.this);
+ }
+ });
}
@Override
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 905db13c85..50263bc392 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -348,11 +348,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void restart() {
- runOnUiThread(() -> {
- if (godotHost != null) {
- godotHost.onGodotRestartRequested(this);
- }
- });
+ if (godotHost != null) {
+ godotHost.onGodotRestartRequested(this);
+ }
}
public void alert(final String message, final String title) {
@@ -889,11 +887,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private void forceQuit() {
// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each
// native Godot components that is started in Godot#onVideoInit.
- runOnUiThread(() -> {
- if (godotHost != null) {
- godotHost.onGodotForceQuit(this);
- }
- });
+ forceQuit(0);
+ }
+
+ @Keep
+ private boolean forceQuit(int instanceId) {
+ if (godotHost == null) {
+ return false;
+ }
+ if (instanceId == 0) {
+ godotHost.onGodotForceQuit(this);
+ return true;
+ } else {
+ return godotHost.onGodotForceQuit(instanceId);
+ }
}
private boolean obbIsCorrupted(String f, String main_pack_md5) {
@@ -1052,11 +1059,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
@Keep
- private void createNewGodotInstance(String[] args) {
- runOnUiThread(() -> {
- if (godotHost != null) {
- godotHost.onNewGodotInstanceRequested(args);
- }
- });
+ private int createNewGodotInstance(String[] args) {
+ if (godotHost != null) {
+ return godotHost.onNewGodotInstanceRequested(args);
+ }
+ return 0;
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
index 256d04e3a5..7700b9b628 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -55,21 +55,35 @@ public interface GodotHost {
default void onGodotMainLoopStarted() {}
/**
- * Invoked on the UI thread as the last step of the Godot instance clean up phase.
+ * Invoked on the render thread to terminate the given Godot instance.
*/
default void onGodotForceQuit(Godot instance) {}
/**
- * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host
+ * Invoked on the render thread to terminate the Godot instance with the given id.
+ * @param godotInstanceId id of the Godot instance to terminate. See {@code onNewGodotInstanceRequested}
+ *
+ * @return true if successful, false otherwise.
+ */
+ default boolean onGodotForceQuit(int godotInstanceId) {
+ return false;
+ }
+
+ /**
+ * Invoked on the render thread when the Godot instance wants to be restarted. It's up to the host
* to perform the appropriate action(s).
*/
default void onGodotRestartRequested(Godot instance) {}
/**
- * Invoked on the UI thread when a new Godot instance is requested. It's up to the host to
+ * Invoked on the render thread when a new Godot instance is requested. It's up to the host to
* perform the appropriate action(s).
*
* @param args Arguments used to initialize the new instance.
+ *
+ * @return the id of the new instance. See {@code onGodotForceQuit}
*/
- default void onNewGodotInstanceRequested(String[] args) {}
+ default int onNewGodotInstanceRequested(String[] args) {
+ return 0;
+ }
}
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 03548d11f6..9d9d087896 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -60,7 +60,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
// get some Godot method pointers...
_on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z");
_restart = p_env->GetMethodID(godot_class, "restart", "()V");
- _finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
+ _finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z");
_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
_alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
_get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I");
@@ -77,7 +77,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
_on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
- _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V");
+ _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I");
_get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
// get some Activity method pointers...
@@ -179,14 +179,15 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) {
}
}
-void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
+bool GodotJavaWrapper::force_quit(JNIEnv *p_env, int p_instance_id) {
if (_finish) {
if (p_env == nullptr) {
p_env = get_jni_env();
}
- ERR_FAIL_NULL(p_env);
- p_env->CallVoidMethod(godot_instance, _finish);
+ ERR_FAIL_NULL_V(p_env, false);
+ return p_env->CallBooleanMethod(godot_instance, _finish, p_instance_id);
}
+ return false;
}
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
@@ -345,14 +346,16 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) {
}
}
-void GodotJavaWrapper::create_new_godot_instance(List<String> args) {
+int GodotJavaWrapper::create_new_godot_instance(List<String> args) {
if (_create_new_godot_instance) {
JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL(env);
+ ERR_FAIL_NULL_V(env, 0);
jobjectArray jargs = env->NewObjectArray(args.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
for (int i = 0; i < args.size(); i++) {
env->SetObjectArrayElement(jargs, i, env->NewStringUTF(args[i].utf8().get_data()));
}
- env->CallVoidMethod(godot_instance, _create_new_godot_instance, jargs);
+ return env->CallIntMethod(godot_instance, _create_new_godot_instance, jargs);
+ } else {
+ return 0;
}
}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 5dad2a3eb9..1bd79584d8 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -85,7 +85,7 @@ public:
void on_godot_setup_completed(JNIEnv *p_env = nullptr);
void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
void restart(JNIEnv *p_env = nullptr);
- void force_quit(JNIEnv *p_env = nullptr);
+ bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0);
void set_keep_screen_on(bool p_enabled);
void alert(const String &p_message, const String &p_title);
int get_gles_version_code();
@@ -103,7 +103,7 @@ public:
bool is_activity_resumed();
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
- void create_new_godot_instance(List<String> args);
+ int create_new_godot_instance(List<String> args);
};
#endif // JAVA_GODOT_WRAPPER_H
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 29b91983bf..725fea8d54 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -739,9 +739,19 @@ Error OS_Android::create_process(const String &p_path, const List<String> &p_arg
}
Error OS_Android::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
- godot_java->create_new_godot_instance(p_arguments);
+ int instance_id = godot_java->create_new_godot_instance(p_arguments);
+ if (r_child_id) {
+ *r_child_id = instance_id;
+ }
return OK;
}
+Error OS_Android::kill(const ProcessID &p_pid) {
+ if (godot_java->force_quit(nullptr, p_pid)) {
+ return OK;
+ }
+ return OS_Unix::kill(p_pid);
+}
+
OS_Android::~OS_Android() {
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 9b43797580..53910b1498 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -158,6 +158,7 @@ public:
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ virtual Error kill(const ProcessID &p_pid) override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index fead0878fb..f7c056316d 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1387,13 +1387,7 @@ Point2 Control::get_global_position() const {
Point2 Control::get_screen_position() const {
ERR_FAIL_COND_V(!is_inside_tree(), Point2());
- Point2 global_pos = get_global_transform_with_canvas().get_origin();
- Window *w = Object::cast_to<Window>(get_viewport());
- if (w && !w->is_embedding_subwindows()) {
- global_pos += w->get_position();
- }
-
- return global_pos;
+ return get_screen_transform().get_origin();
}
void Control::_set_size(const Size2 &p_size) {
@@ -1444,24 +1438,20 @@ void Control::set_rect(const Rect2 &p_rect) {
}
Rect2 Control::get_rect() const {
- return Rect2(get_position(), get_size());
+ Transform2D xform = get_transform();
+ return Rect2(xform.get_origin(), xform.get_scale() * get_size());
}
Rect2 Control::get_global_rect() const {
- return Rect2(get_global_position(), get_size());
+ Transform2D xform = get_global_transform();
+ return Rect2(xform.get_origin(), xform.get_scale() * get_size());
}
Rect2 Control::get_screen_rect() const {
ERR_FAIL_COND_V(!is_inside_tree(), Rect2());
- Rect2 r(get_global_position(), get_size());
-
- Window *w = Object::cast_to<Window>(get_viewport());
- if (w && !w->is_embedding_subwindows()) {
- r.position += w->get_position();
- }
-
- return r;
+ Transform2D xform = get_screen_transform();
+ return Rect2(xform.get_origin(), xform.get_scale() * get_size());
}
Rect2 Control::get_anchorable_rect() const {
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 7b0554442c..3323c0f848 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -154,17 +154,7 @@ Transform2D CanvasItem::get_global_transform_with_canvas() const {
Transform2D CanvasItem::get_screen_transform() const {
ERR_FAIL_COND_V(!is_inside_tree(), Transform2D());
- Transform2D xform = get_global_transform_with_canvas();
-
- Window *w = Object::cast_to<Window>(get_viewport());
- if (w && !w->is_embedding_subwindows()) {
- Transform2D s;
- s.set_origin(w->get_position());
-
- xform = s * xform;
- }
-
- return xform;
+ return get_viewport()->get_popup_base_transform() * get_global_transform_with_canvas();
}
Transform2D CanvasItem::get_global_transform() const {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 07bcf45899..70ec22e297 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -704,25 +704,15 @@ void Viewport::_process_picking() {
}
#ifndef _3D_DISABLED
- bool captured = false;
-
+ CollisionObject3D *capture_object = nullptr;
if (physics_object_capture.is_valid()) {
- CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture));
- if (co && camera_3d) {
- _collision_object_3d_input_event(co, camera_3d, ev, Vector3(), Vector3(), 0);
- captured = true;
- if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
- physics_object_capture = ObjectID();
- }
-
- } else {
+ capture_object = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture));
+ if (!capture_object || !camera_3d || (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed())) {
physics_object_capture = ObjectID();
}
}
- if (captured) {
- // None.
- } else if (pos == last_pos) {
+ if (pos == last_pos) {
if (last_id.is_valid()) {
if (ObjectDB::get_instance(last_id) && last_object) {
// Good, exists.
@@ -748,13 +738,12 @@ void Viewport::_process_picking() {
bool col = space->intersect_ray(ray_params, result);
ObjectID new_collider;
- if (col) {
- CollisionObject3D *co = Object::cast_to<CollisionObject3D>(result.collider);
- if (co && co->can_process()) {
- _collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape);
+ CollisionObject3D *co = col ? Object::cast_to<CollisionObject3D>(result.collider) : nullptr;
+ if (co && co->can_process()) {
+ new_collider = result.collider_id;
+ if (!capture_object) {
last_object = co;
last_id = result.collider_id;
- new_collider = last_id;
if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
physics_object_capture = last_id;
}
@@ -763,21 +752,23 @@ void Viewport::_process_picking() {
if (is_mouse && new_collider != physics_object_over) {
if (physics_object_over.is_valid()) {
- CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over));
- if (co) {
- co->_mouse_exit();
+ CollisionObject3D *previous_co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over));
+ if (previous_co) {
+ previous_co->_mouse_exit();
}
}
if (new_collider.is_valid()) {
- CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(new_collider));
- if (co) {
- co->_mouse_enter();
- }
+ co->_mouse_enter();
}
physics_object_over = new_collider;
}
+ if (capture_object) {
+ _collision_object_3d_input_event(capture_object, camera_3d, ev, result.position, result.normal, result.shape);
+ } else if (new_collider.is_valid()) {
+ _collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape);
+ }
}
last_pos = pos;
@@ -1050,7 +1041,7 @@ Camera2D *Viewport::get_camera_2d() const {
}
Transform2D Viewport::get_final_transform() const {
- return stretch_transform * global_canvas_transform;
+ return _get_input_pre_xform().affine_inverse() * stretch_transform * global_canvas_transform;
}
void Viewport::_update_canvas_items(Node *p_node) {
@@ -1133,7 +1124,7 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
return ev; // No transformation defined for null event
}
- Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform();
+ Transform2D ai = get_final_transform().affine_inverse();
return ev->xformed_by(ai);
}
@@ -1795,9 +1786,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (w->is_embedded()) {
embedder = w->_get_embedder();
- Transform2D ai = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse();
-
- viewport_pos = ai.xform(mpos) + w->get_position(); // To parent coords.
+ viewport_pos = get_final_transform().xform(mpos) + w->get_position(); // To parent coords.
}
}
}
@@ -1847,7 +1836,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (viewport_under) {
if (viewport_under != this) {
- Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform());
+ Transform2D ai = viewport_under->get_final_transform().affine_inverse();
viewport_pos = ai.xform(viewport_pos);
}
// Find control under at position.
@@ -2853,7 +2842,7 @@ bool Viewport::get_physics_object_picking() {
}
Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const {
- Transform2D xf = get_final_transform();
+ Transform2D xf = stretch_transform * global_canvas_transform;
return xf.xform(p_viewport_coords);
}
@@ -3245,7 +3234,7 @@ Viewport::SDFScale Viewport::get_sdf_scale() const {
}
Transform2D Viewport::get_screen_transform() const {
- return _get_input_pre_xform().affine_inverse() * get_final_transform();
+ return get_final_transform();
}
void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) {
@@ -4188,6 +4177,21 @@ Transform2D SubViewport::get_screen_transform() const {
return container_transform * Viewport::get_screen_transform();
}
+Transform2D SubViewport::get_popup_base_transform() const {
+ if (is_embedding_subwindows()) {
+ return Transform2D();
+ }
+ SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
+ if (!c) {
+ return Viewport::get_screen_transform();
+ }
+ Transform2D container_transform;
+ if (c->is_stretch_enabled()) {
+ container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
+ }
+ return c->get_screen_transform() * container_transform * Viewport::get_screen_transform();
+}
+
void SubViewport::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 603e92b071..d5d5201e9a 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -649,6 +649,7 @@ public:
bool get_canvas_cull_mask_bit(uint32_t p_layer) const;
virtual Transform2D get_screen_transform() const;
+ virtual Transform2D get_popup_base_transform() const { return Transform2D(); }
#ifndef _3D_DISABLED
bool use_xr = false;
@@ -775,6 +776,7 @@ public:
ClearMode get_clear_mode() const;
virtual Transform2D get_screen_transform() const override;
+ virtual Transform2D get_popup_base_transform() const override;
SubViewport();
~SubViewport();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 2c6599d849..3ceb27b3e6 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -2104,6 +2104,19 @@ Transform2D Window::get_screen_transform() const {
return embedder_transform * Viewport::get_screen_transform();
}
+Transform2D Window::get_popup_base_transform() const {
+ if (is_embedding_subwindows()) {
+ return Transform2D();
+ }
+ Transform2D window_transform;
+ window_transform.set_origin(get_position());
+ window_transform *= Viewport::get_screen_transform();
+ if (_get_embedder()) {
+ return _get_embedder()->get_popup_base_transform() * window_transform;
+ }
+ return window_transform;
+}
+
void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
diff --git a/scene/main/window.h b/scene/main/window.h
index e9c217f973..18841a4f1a 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -373,6 +373,7 @@ public:
//
virtual Transform2D get_screen_transform() const override;
+ virtual Transform2D get_popup_base_transform() const override;
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;