You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-13 13:31:48 +00:00
Android: Implement support for native dialog
This adds support for DisplayServer::dialog_show() on Android, aligning it with the functionality already available on macOS and Windows.
This commit is contained in:
@@ -123,7 +123,7 @@
|
|||||||
<param index="3" name="callback" type="Callable" />
|
<param index="3" name="callback" type="Callable" />
|
||||||
<description>
|
<description>
|
||||||
Shows a text dialog which uses the operating system's native look-and-feel. [param callback] should accept a single [int] parameter which corresponds to the index of the pressed button.
|
Shows a text dialog which uses the operating system's native look-and-feel. [param callback] should accept a single [int] parameter which corresponds to the index of the pressed button.
|
||||||
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include macOS and Windows.
|
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include macOS, Windows, and Android.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="enable_for_stealing_focus">
|
<method name="enable_for_stealing_focus">
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ android_files = [
|
|||||||
"plugin/godot_plugin_jni.cpp",
|
"plugin/godot_plugin_jni.cpp",
|
||||||
"rendering_context_driver_vulkan_android.cpp",
|
"rendering_context_driver_vulkan_android.cpp",
|
||||||
"variant/callable_jni.cpp",
|
"variant/callable_jni.cpp",
|
||||||
|
"dialog_utils_jni.cpp",
|
||||||
]
|
]
|
||||||
|
|
||||||
env_android = env.Clone()
|
env_android = env.Clone()
|
||||||
|
|||||||
52
platform/android/dialog_utils_jni.cpp
Normal file
52
platform/android/dialog_utils_jni.cpp
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* dialog_utils_jni.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#include "dialog_utils_jni.h"
|
||||||
|
|
||||||
|
#include "display_server_android.h"
|
||||||
|
#include "jni_utils.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_dialogCallback(JNIEnv *env, jclass clazz, jint p_button_index) {
|
||||||
|
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||||
|
if (ds) {
|
||||||
|
ds->emit_dialog_callback(p_button_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text) {
|
||||||
|
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||||
|
if (ds) {
|
||||||
|
String text = jstring_to_string(p_text, env);
|
||||||
|
ds->emit_input_dialog_callback(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
platform/android/dialog_utils_jni.h
Normal file
41
platform/android/dialog_utils_jni.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* dialog_utils_jni.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef DIALOG_UTILS_JNI_H
|
||||||
|
#define DIALOG_UTILS_JNI_H
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_dialogCallback(JNIEnv *env, jclass clazz, jint p_button_index);
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_utils_DialogUtils_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DIALOG_UTILS_JNI_H
|
||||||
@@ -70,7 +70,7 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
|
|||||||
//case FEATURE_IME:
|
//case FEATURE_IME:
|
||||||
case FEATURE_MOUSE:
|
case FEATURE_MOUSE:
|
||||||
//case FEATURE_MOUSE_WARP:
|
//case FEATURE_MOUSE_WARP:
|
||||||
//case FEATURE_NATIVE_DIALOG:
|
case FEATURE_NATIVE_DIALOG:
|
||||||
case FEATURE_NATIVE_DIALOG_INPUT:
|
case FEATURE_NATIVE_DIALOG_INPUT:
|
||||||
case FEATURE_NATIVE_DIALOG_FILE:
|
case FEATURE_NATIVE_DIALOG_FILE:
|
||||||
//case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
//case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
||||||
@@ -177,6 +177,19 @@ bool DisplayServerAndroid::clipboard_has() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error DisplayServerAndroid::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
|
||||||
|
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||||
|
ERR_FAIL_NULL_V(godot_java, FAILED);
|
||||||
|
dialog_callback = p_callback;
|
||||||
|
return godot_java->show_dialog(p_title, p_description, p_buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayServerAndroid::emit_dialog_callback(int p_button_index) {
|
||||||
|
if (dialog_callback.is_valid()) {
|
||||||
|
dialog_callback.call_deferred(p_button_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error DisplayServerAndroid::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
|
Error DisplayServerAndroid::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
|
||||||
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
|
||||||
ERR_FAIL_NULL_V(godot_java, FAILED);
|
ERR_FAIL_NULL_V(godot_java, FAILED);
|
||||||
|
|||||||
@@ -87,7 +87,9 @@ class DisplayServerAndroid : public DisplayServer {
|
|||||||
|
|
||||||
Callable system_theme_changed;
|
Callable system_theme_changed;
|
||||||
|
|
||||||
|
Callable dialog_callback;
|
||||||
Callable input_dialog_callback;
|
Callable input_dialog_callback;
|
||||||
|
|
||||||
Callable file_picker_callback;
|
Callable file_picker_callback;
|
||||||
|
|
||||||
void _window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred = false) const;
|
void _window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred = false) const;
|
||||||
@@ -119,6 +121,9 @@ public:
|
|||||||
virtual String clipboard_get() const override;
|
virtual String clipboard_get() const override;
|
||||||
virtual bool clipboard_has() const override;
|
virtual bool clipboard_has() const override;
|
||||||
|
|
||||||
|
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override;
|
||||||
|
void emit_dialog_callback(int p_button_index);
|
||||||
|
|
||||||
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
|
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
|
||||||
void emit_input_dialog_callback(String p_text);
|
void emit_input_dialog_callback(String p_text);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<dimen name="text_edit_height">48dp</dimen>
|
<dimen name="text_edit_height">48dp</dimen>
|
||||||
<dimen name="input_dialog_padding_horizontal">10dp</dimen>
|
<dimen name="button_height">48dp</dimen>
|
||||||
<dimen name="input_dialog_padding_vertical">5dp</dimen>
|
<dimen name="button_padding">10dp</dimen>
|
||||||
|
<dimen name="dialog_padding_horizontal">16dp</dimen>
|
||||||
|
<dimen name="dialog_padding_vertical">8dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import android.os.*
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
@@ -65,6 +64,7 @@ import org.godotengine.godot.plugin.GodotPlugin
|
|||||||
import org.godotengine.godot.plugin.GodotPluginRegistry
|
import org.godotengine.godot.plugin.GodotPluginRegistry
|
||||||
import org.godotengine.godot.tts.GodotTTS
|
import org.godotengine.godot.tts.GodotTTS
|
||||||
import org.godotengine.godot.utils.CommandLineFileParser
|
import org.godotengine.godot.utils.CommandLineFileParser
|
||||||
|
import org.godotengine.godot.utils.DialogUtils
|
||||||
import org.godotengine.godot.utils.GodotNetUtils
|
import org.godotengine.godot.utils.GodotNetUtils
|
||||||
import org.godotengine.godot.utils.PermissionsUtil
|
import org.godotengine.godot.utils.PermissionsUtil
|
||||||
import org.godotengine.godot.utils.PermissionsUtil.requestPermission
|
import org.godotengine.godot.utils.PermissionsUtil.requestPermission
|
||||||
@@ -903,27 +903,27 @@ class Godot(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Popup a dialog to input text.
|
* This method shows a dialog with multiple buttons.
|
||||||
|
*
|
||||||
|
* @param title The title of the dialog.
|
||||||
|
* @param message The message displayed in the dialog.
|
||||||
|
* @param buttons An array of button labels to display.
|
||||||
|
*/
|
||||||
|
@Keep
|
||||||
|
private fun showDialog(title: String, message: String, buttons: Array<String>) {
|
||||||
|
getActivity()?.let { DialogUtils.showDialog(it, title, message, buttons) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method shows a dialog with a text input field, allowing the user to input text.
|
||||||
|
*
|
||||||
|
* @param title The title of the input dialog.
|
||||||
|
* @param message The message displayed in the input dialog.
|
||||||
|
* @param existingText The existing text that will be pre-filled in the input field.
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
private fun showInputDialog(title: String, message: String, existingText: String) {
|
private fun showInputDialog(title: String, message: String, existingText: String) {
|
||||||
val activity: Activity = getActivity() ?: return
|
getActivity()?.let { DialogUtils.showInputDialog(it, title, message, existingText) }
|
||||||
val inputField = EditText(activity)
|
|
||||||
val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.input_dialog_padding_horizontal)
|
|
||||||
val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.input_dialog_padding_vertical)
|
|
||||||
inputField.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical)
|
|
||||||
inputField.setText(existingText)
|
|
||||||
runOnUiThread {
|
|
||||||
val builder = AlertDialog.Builder(activity)
|
|
||||||
builder.setMessage(message).setTitle(title).setView(inputField)
|
|
||||||
builder.setPositiveButton(R.string.dialog_ok) {
|
|
||||||
dialog: DialogInterface, id: Int ->
|
|
||||||
GodotLib.inputDialogCallback(inputField.text.toString())
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
val dialog = builder.create()
|
|
||||||
dialog.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
|
|||||||
@@ -235,11 +235,6 @@ public class GodotLib {
|
|||||||
*/
|
*/
|
||||||
public static native void onNightModeChanged();
|
public static native void onNightModeChanged();
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked on the input dialog submitted.
|
|
||||||
*/
|
|
||||||
public static native void inputDialogCallback(String p_text);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked on the file picker closed.
|
* Invoked on the file picker closed.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* DialogUtils.kt */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* 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.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
|
||||||
|
import org.godotengine.godot.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for managing dialogs.
|
||||||
|
*/
|
||||||
|
internal class DialogUtils {
|
||||||
|
companion object {
|
||||||
|
private val TAG = DialogUtils::class.java.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked on dialog button press.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
private external fun dialogCallback(buttonIndex: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked on the input dialog submitted.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
private external fun inputDialogCallback(text: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a dialog with dynamically arranged buttons based on their text length.
|
||||||
|
*
|
||||||
|
* The buttons are laid out in rows, with a maximum of 2 buttons per row. If a button's text
|
||||||
|
* is too long to fit within half the screen width, it occupies the entire row.
|
||||||
|
*
|
||||||
|
* @param activity The activity where the dialog will be displayed.
|
||||||
|
* @param title The title of the dialog.
|
||||||
|
* @param message The message displayed in the dialog.
|
||||||
|
* @param buttons An array of button labels to display.
|
||||||
|
*/
|
||||||
|
fun showDialog(activity: Activity, title: String, message: String, buttons: Array<String>) {
|
||||||
|
var dismissDialog: () -> Unit = {} // Helper to dismiss the Dialog when a button is clicked.
|
||||||
|
activity.runOnUiThread {
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
builder.setTitle(title)
|
||||||
|
builder.setMessage(message)
|
||||||
|
|
||||||
|
val buttonHeight = activity.resources.getDimensionPixelSize(R.dimen.button_height)
|
||||||
|
val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_horizontal)
|
||||||
|
val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_vertical)
|
||||||
|
val buttonPadding = activity.resources.getDimensionPixelSize(R.dimen.button_padding)
|
||||||
|
|
||||||
|
// Create a vertical parent layout to hold all rows of buttons.
|
||||||
|
val parentLayout = LinearLayout(activity)
|
||||||
|
parentLayout.orientation = LinearLayout.VERTICAL
|
||||||
|
parentLayout.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical)
|
||||||
|
|
||||||
|
// Horizontal row layout for arranging buttons.
|
||||||
|
var rowLayout = LinearLayout(activity)
|
||||||
|
rowLayout.orientation = LinearLayout.HORIZONTAL
|
||||||
|
rowLayout.layoutParams = LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
|
||||||
|
// Calculate the maximum width for a button to allow two buttons per row.
|
||||||
|
val screenWidth = activity.resources.displayMetrics.widthPixels
|
||||||
|
val availableWidth = screenWidth - (2 * paddingHorizontal)
|
||||||
|
val maxButtonWidth = availableWidth / 2
|
||||||
|
|
||||||
|
buttons.forEachIndexed { index, buttonLabel ->
|
||||||
|
val button = Button(activity)
|
||||||
|
button.text = buttonLabel
|
||||||
|
button.isSingleLine = true
|
||||||
|
button.setPadding(buttonPadding, buttonPadding, buttonPadding, buttonPadding)
|
||||||
|
|
||||||
|
// Measure the button to determine its width.
|
||||||
|
button.measure(0, 0)
|
||||||
|
val buttonWidth = button.measuredWidth
|
||||||
|
|
||||||
|
val params = LinearLayout.LayoutParams(
|
||||||
|
if (buttonWidth > maxButtonWidth) LinearLayout.LayoutParams.MATCH_PARENT else 0,
|
||||||
|
buttonHeight
|
||||||
|
)
|
||||||
|
params.weight = if (buttonWidth > maxButtonWidth) 0f else 1f
|
||||||
|
button.layoutParams = params
|
||||||
|
|
||||||
|
// Handle full-width buttons by finalizing the current row, if needed.
|
||||||
|
if (buttonWidth > maxButtonWidth) {
|
||||||
|
if (rowLayout.childCount > 0) {
|
||||||
|
parentLayout.addView(rowLayout)
|
||||||
|
rowLayout = LinearLayout(activity)
|
||||||
|
rowLayout.orientation = LinearLayout.HORIZONTAL
|
||||||
|
}
|
||||||
|
// Add the full-width button directly to the parent layout.
|
||||||
|
parentLayout.addView(button)
|
||||||
|
} else {
|
||||||
|
rowLayout.addView(button)
|
||||||
|
|
||||||
|
// Finalize the row if it reaches 2 buttons.
|
||||||
|
if (rowLayout.childCount == 2) {
|
||||||
|
parentLayout.addView(rowLayout)
|
||||||
|
rowLayout = LinearLayout(activity)
|
||||||
|
rowLayout.orientation = LinearLayout.HORIZONTAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the last button with incomplete row.
|
||||||
|
if (index == buttons.size - 1 && rowLayout.childCount > 0) {
|
||||||
|
parentLayout.addView(rowLayout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button.setOnClickListener {
|
||||||
|
dialogCallback(index)
|
||||||
|
dismissDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach the parent layout to the dialog.
|
||||||
|
builder.setView(parentLayout)
|
||||||
|
val dialog = builder.create()
|
||||||
|
dismissDialog = {dialog.dismiss()}
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method shows a dialog with a text input field, allowing the user to input text.
|
||||||
|
*
|
||||||
|
* @param activity The activity where the input dialog will be displayed.
|
||||||
|
* @param title The title of the input dialog.
|
||||||
|
* @param message The message displayed in the input dialog.
|
||||||
|
* @param existingText The existing text that will be pre-filled in the input field.
|
||||||
|
*/
|
||||||
|
fun showInputDialog(activity: Activity, title: String, message: String, existingText: String) {
|
||||||
|
val inputField = EditText(activity)
|
||||||
|
val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_horizontal)
|
||||||
|
val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_vertical)
|
||||||
|
inputField.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical)
|
||||||
|
inputField.setText(existingText)
|
||||||
|
activity.runOnUiThread {
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
builder.setMessage(message).setTitle(title).setView(inputField)
|
||||||
|
builder.setPositiveButton(R.string.dialog_ok) {
|
||||||
|
dialog: DialogInterface, id: Int ->
|
||||||
|
inputDialogCallback(inputField.text.toString())
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -494,14 +494,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text) {
|
|
||||||
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
|
||||||
if (ds) {
|
|
||||||
String text = jstring_to_string(p_text, env);
|
|
||||||
ds->emit_input_dialog_callback(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths) {
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths) {
|
||||||
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
|
||||||
if (ds) {
|
if (ds) {
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getEditorSetting(J
|
|||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_inputDialogCallback(JNIEnv *env, jclass clazz, jstring p_text);
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
|
|||||||
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
|
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
|
||||||
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
|
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
|
||||||
_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
|
_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
|
||||||
|
_show_dialog = p_env->GetMethodID(godot_class, "showDialog", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V");
|
||||||
_show_input_dialog = p_env->GetMethodID(godot_class, "showInputDialog", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
_show_input_dialog = p_env->GetMethodID(godot_class, "showInputDialog", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||||
_show_file_picker = p_env->GetMethodID(godot_class, "showFilePicker", "(Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/String;)V");
|
_show_file_picker = p_env->GetMethodID(godot_class, "showFilePicker", "(Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/String;)V");
|
||||||
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
|
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
|
||||||
@@ -303,6 +304,28 @@ bool GodotJavaWrapper::has_clipboard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error GodotJavaWrapper::show_dialog(const String &p_title, const String &p_description, const Vector<String> &p_buttons) {
|
||||||
|
if (_show_input_dialog) {
|
||||||
|
JNIEnv *env = get_jni_env();
|
||||||
|
ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED);
|
||||||
|
jstring j_title = env->NewStringUTF(p_title.utf8().get_data());
|
||||||
|
jstring j_description = env->NewStringUTF(p_description.utf8().get_data());
|
||||||
|
jobjectArray j_buttons = env->NewObjectArray(p_buttons.size(), env->FindClass("java/lang/String"), nullptr);
|
||||||
|
for (int i = 0; i < p_buttons.size(); ++i) {
|
||||||
|
jstring j_button = env->NewStringUTF(p_buttons[i].utf8().get_data());
|
||||||
|
env->SetObjectArrayElement(j_buttons, i, j_button);
|
||||||
|
env->DeleteLocalRef(j_button);
|
||||||
|
}
|
||||||
|
env->CallVoidMethod(godot_instance, _show_dialog, j_title, j_description, j_buttons);
|
||||||
|
env->DeleteLocalRef(j_title);
|
||||||
|
env->DeleteLocalRef(j_description);
|
||||||
|
env->DeleteLocalRef(j_buttons);
|
||||||
|
return OK;
|
||||||
|
} else {
|
||||||
|
return ERR_UNCONFIGURED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error GodotJavaWrapper::show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text) {
|
Error GodotJavaWrapper::show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text) {
|
||||||
if (_show_input_dialog) {
|
if (_show_input_dialog) {
|
||||||
JNIEnv *env = get_jni_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ private:
|
|||||||
jmethodID _get_clipboard = nullptr;
|
jmethodID _get_clipboard = nullptr;
|
||||||
jmethodID _set_clipboard = nullptr;
|
jmethodID _set_clipboard = nullptr;
|
||||||
jmethodID _has_clipboard = nullptr;
|
jmethodID _has_clipboard = nullptr;
|
||||||
|
jmethodID _show_dialog = nullptr;
|
||||||
jmethodID _show_input_dialog = nullptr;
|
jmethodID _show_input_dialog = nullptr;
|
||||||
jmethodID _show_file_picker = nullptr;
|
jmethodID _show_file_picker = nullptr;
|
||||||
jmethodID _request_permission = nullptr;
|
jmethodID _request_permission = nullptr;
|
||||||
@@ -109,6 +110,7 @@ public:
|
|||||||
void set_clipboard(const String &p_text);
|
void set_clipboard(const String &p_text);
|
||||||
bool has_has_clipboard();
|
bool has_has_clipboard();
|
||||||
bool has_clipboard();
|
bool has_clipboard();
|
||||||
|
Error show_dialog(const String &p_title, const String &p_description, const Vector<String> &p_buttons);
|
||||||
Error show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text);
|
Error show_input_dialog(const String &p_title, const String &p_message, const String &p_existing_text);
|
||||||
Error show_file_picker(const String &p_current_directory, const String &p_filename, int p_mode, const Vector<String> &p_filters);
|
Error show_file_picker(const String &p_current_directory, const String &p_filename, int p_mode, const Vector<String> &p_filters);
|
||||||
bool request_permission(const String &p_name);
|
bool request_permission(const String &p_name);
|
||||||
|
|||||||
Reference in New Issue
Block a user