mirror of https://github.com/buggins/dlangui.git
Android basic keyboard input support (incl. virtual keyboard)
Normal English input works fine. Punctuation and navigation are partially supported. IME from virtual keyboard doesn't work at all, seems like the native activity wrapper(android_app class) issues after Android 4.0.
This commit is contained in:
parent
108d7ce74c
commit
c79fca652a
|
@ -2,9 +2,16 @@ PLATFORM_DIR=armeabi-v7a
|
||||||
|
|
||||||
echo "\nLOCAL_MODULE: $LOCAL_MODULE"
|
echo "\nLOCAL_MODULE: $LOCAL_MODULE"
|
||||||
|
|
||||||
|
|
||||||
|
OS_TYPE="unknown"
|
||||||
|
case "$OSTYPE" in
|
||||||
|
msys*|cygwin*) OS_TYPE="windows" ;;
|
||||||
|
linux*) OS_TYPE="linux" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
LDC_PARAMS="-mtriple=armv7-none-linux-androideabi -relocation-model=pic "
|
LDC_PARAMS="-mtriple=armv7-none-linux-androideabi -relocation-model=pic "
|
||||||
export LD=$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/llvm-link
|
export LD=$NDK/toolchains/llvm/prebuilt/$OS_TYPE-$NDK_ARCH/bin/llvm-link
|
||||||
export CC=$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/clang
|
export CC=$NDK/toolchains/llvm/prebuilt/$OS_TYPE-$NDK_ARCH/bin/clang
|
||||||
|
|
||||||
SOURCES="$LOCAL_SRC_FILES $DLANGUI_SOURCES"
|
SOURCES="$LOCAL_SRC_FILES $DLANGUI_SOURCES"
|
||||||
SOURCE_PATHS="-I./jni $DLANGUI_SOURCE_PATHS $DLANGUI_IMPORT_PATHS"
|
SOURCE_PATHS="-I./jni $DLANGUI_SOURCE_PATHS $DLANGUI_IMPORT_PATHS"
|
||||||
|
@ -24,7 +31,7 @@ LINK_OPTIONS="\
|
||||||
-Wl,-soname,libnative-activity.so \
|
-Wl,-soname,libnative-activity.so \
|
||||||
-shared \
|
-shared \
|
||||||
--sysroot=$NDK/platforms/$ANDROID_TARGET/arch-arm \
|
--sysroot=$NDK/platforms/$ANDROID_TARGET/arch-arm \
|
||||||
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-$NDK_ARCH \
|
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/$OS_TYPE-$NDK_ARCH \
|
||||||
-no-canonical-prefixes \
|
-no-canonical-prefixes \
|
||||||
-target armv7-none-linux-androideabi \
|
-target armv7-none-linux-androideabi \
|
||||||
-Wl,--fix-cortex-a8 \
|
-Wl,--fix-cortex-a8 \
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
DLANGUI_SOURCES="\
|
DLANGUI_SOURCES="\
|
||||||
$DLANGUI_DIR/src/dlangui/platforms/android/androidapp.d \
|
$DLANGUI_DIR/src/dlangui/platforms/android/androidapp.d \
|
||||||
|
$DLANGUI_DIR/src/dlangui/platforms/android/imm.d \
|
||||||
$DLANGUI_DIR/src/dlangui/platforms/common/startup.d \
|
$DLANGUI_DIR/src/dlangui/platforms/common/startup.d \
|
||||||
$DLANGUI_DIR/src/dlangui/platforms/common/platform.d \
|
$DLANGUI_DIR/src/dlangui/platforms/common/platform.d \
|
||||||
$DLANGUI_DIR/src/dlangui/dialogs/filedlg.d \
|
$DLANGUI_DIR/src/dlangui/dialogs/filedlg.d \
|
||||||
|
|
|
@ -16,6 +16,7 @@ import dlangui.platforms.common.platform;
|
||||||
import android.input, android.looper : ALooper_pollAll;
|
import android.input, android.looper : ALooper_pollAll;
|
||||||
import android.native_window : ANativeWindow_setBuffersGeometry;
|
import android.native_window : ANativeWindow_setBuffersGeometry;
|
||||||
import android.configuration;
|
import android.configuration;
|
||||||
|
import android.keycodes;
|
||||||
import android.log, android.android_native_app_glue;
|
import android.log, android.android_native_app_glue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,6 +140,8 @@ class AndroidWindow : Window {
|
||||||
* Process the next input event.
|
* Process the next input event.
|
||||||
*/
|
*/
|
||||||
int handle_input(AInputEvent* event) {
|
int handle_input(AInputEvent* event) {
|
||||||
|
import imm = dlangui.platforms.android.imm;
|
||||||
|
import std.conv : to;
|
||||||
Log.i("handle input, event=", AInputEvent_getType(event));
|
Log.i("handle input, event=", AInputEvent_getType(event));
|
||||||
auto et = AInputEvent_getType(event);
|
auto et = AInputEvent_getType(event);
|
||||||
if (et == AINPUT_EVENT_TYPE_MOTION) {
|
if (et == AINPUT_EVENT_TYPE_MOTION) {
|
||||||
|
@ -173,7 +176,37 @@ class AndroidWindow : Window {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (et == AINPUT_EVENT_TYPE_KEY) {
|
} else if (et == AINPUT_EVENT_TYPE_KEY) {
|
||||||
Log.d("AINPUT_EVENT_TYPE_KEY");
|
Log.d("AINPUT_EVENT_TYPE_KEY");
|
||||||
return 0;
|
KeyEvent evt;
|
||||||
|
auto app = (cast(AndroidPlatform)platform)._appstate;
|
||||||
|
int _keyFlags = AKeyEvent_getMetaState(event).toKeyFlag();
|
||||||
|
int sysKeyCode = AKeyEvent_getKeyCode(event);
|
||||||
|
int sysMeta = AKeyEvent_getMetaState(event);
|
||||||
|
int keyCode = androidKeyMap.get(sysKeyCode, KeyCode.init);
|
||||||
|
auto action = toKeyAction(AKeyEvent_getAction(event));
|
||||||
|
int char_ = imm.GetUnicodeChar(app, action, sysKeyCode, sysMeta);
|
||||||
|
dchar[] text;
|
||||||
|
if (!isTextEditControl(sysKeyCode)) {
|
||||||
|
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) {
|
||||||
|
// it's a string from IME
|
||||||
|
if (sysKeyCode == AKEYCODE_UNKNOWN) {
|
||||||
|
text = cast(dchar[]) to!dstring(imm.GetUnicodeString(app, event));
|
||||||
|
action = KeyAction.Text;
|
||||||
|
}
|
||||||
|
// else repeat character AKeyEvent_getRepeatCount() times
|
||||||
|
}
|
||||||
|
else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN && (char_ || isASCIIChar(sysKeyCode))) {
|
||||||
|
text ~= cast(dchar)(char_ == 0 ? sysKeyCode : char_);
|
||||||
|
action = KeyAction.Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d("ACTION: ", action, " syskeyCode: ", sysKeyCode, " sysMeta: ", sysMeta, "meta: ", _keyFlags, " char '", cast(dchar)char_, "' str:", cast(dstring)text);
|
||||||
|
if (action == KeyAction.Text)
|
||||||
|
evt = new KeyEvent(KeyAction.Text, 0, 0, cast(dstring)text);
|
||||||
|
else
|
||||||
|
evt = new KeyEvent(action, keyCode, _keyFlags);
|
||||||
|
if (evt && dispatchKeyEvent(evt))
|
||||||
|
update();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -225,6 +258,12 @@ class AndroidPlatform : Platform {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void showSoftKeyboard(bool shouldShow) {
|
||||||
|
import imm = dlangui.platforms.android.imm;
|
||||||
|
imm.showSoftKeyboard(_appstate, shouldShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize an EGL context for the current display.
|
* Initialize an EGL context for the current display.
|
||||||
*/
|
*/
|
||||||
|
@ -706,3 +745,82 @@ extern (C) void android_main(android_app* state) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private KeyAction toKeyAction(int androidKeyAction) {
|
||||||
|
switch(androidKeyAction) {
|
||||||
|
case AKEY_EVENT_ACTION_DOWN: return KeyAction.KeyDown;
|
||||||
|
case AKEY_EVENT_ACTION_UP: return KeyAction.KeyUp;
|
||||||
|
case AKEY_EVENT_ACTION_MULTIPLE: return KeyAction.Repeat; // can also be text
|
||||||
|
default:
|
||||||
|
assert(0, "should never reach this");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private bool isASCIIChar(int ch) {
|
||||||
|
return 31 < ch && ch < 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text editor controls such as move caret or (de)select
|
||||||
|
private bool isTextEditControl(int keyCode) {
|
||||||
|
switch (keyCode){
|
||||||
|
case AKEYCODE_DEL: // backspace ("DEL" or "<x" arrow on soft keyboard)
|
||||||
|
case 112: // delete
|
||||||
|
case 59: // lshift
|
||||||
|
case 60: // rshift
|
||||||
|
case 113: // lcontrol
|
||||||
|
case 114: // rcontrol
|
||||||
|
case 57: // lalt
|
||||||
|
case 58: // ralt
|
||||||
|
case 19: // up arrow
|
||||||
|
case 20: // down arrow
|
||||||
|
case 21: // left arrow
|
||||||
|
case 22: // right arrow
|
||||||
|
case 92: // page up?
|
||||||
|
case 93: // page down?
|
||||||
|
case 122: // home
|
||||||
|
case 123: // end
|
||||||
|
case 124: // insert
|
||||||
|
return true;
|
||||||
|
static foreach(fn; 131..143) // f1-f12
|
||||||
|
case fn: return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int toKeyFlag(int keyMeta) {
|
||||||
|
int state;
|
||||||
|
if (keyMeta & AMETA_ALT_ON) state |= KeyFlag.Alt;
|
||||||
|
if (keyMeta & AMETA_SHIFT_ON) state |= KeyFlag.Shift;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Android to dlangui key mapping
|
||||||
|
private static immutable KeyCode[int] androidKeyMap;
|
||||||
|
|
||||||
|
static this() {
|
||||||
|
import std.conv : text;
|
||||||
|
androidKeyMap = [
|
||||||
|
AKEYCODE_DEL: KeyCode.BACK, // Delete(key code 67) on Android seems to work as backspace(key 112)
|
||||||
|
AKEYCODE_BACK: KeyCode.BACK,
|
||||||
|
AKEYCODE_SPACE: KeyCode.SPACE,
|
||||||
|
AKEYCODE_ENTER: KeyCode.RETURN,
|
||||||
|
AKEYCODE_TAB: KeyCode.TAB,
|
||||||
|
AKEYCODE_DPAD_LEFT: KeyCode.LEFT,
|
||||||
|
AKEYCODE_DPAD_RIGHT: KeyCode.RIGHT,
|
||||||
|
AKEYCODE_DPAD_UP: KeyCode.UP,
|
||||||
|
AKEYCODE_DPAD_DOWN: KeyCode.DOWN,
|
||||||
|
AKEYCODE_PAGE_UP: KeyCode.PAGEUP,
|
||||||
|
AKEYCODE_PAGE_DOWN: KeyCode.PAGEDOWN,
|
||||||
|
112: KeyCode.DEL,
|
||||||
|
122: KeyCode.HOME,
|
||||||
|
123: KeyCode.END,
|
||||||
|
];
|
||||||
|
static foreach(n; 0..10) // keys 0-9
|
||||||
|
androidKeyMap[mixin("AKEYCODE_" ~ n.text)] = mixin("KeyCode.KEY_" ~ n.text);
|
||||||
|
static foreach(char n; 'A'..'Z'+1) // A-Z
|
||||||
|
androidKeyMap[mixin("AKEYCODE_" ~ n)] = mixin("KeyCode.KEY_" ~ n);
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
module dlangui.platforms.android.imm;
|
||||||
|
|
||||||
|
import jni;
|
||||||
|
import android.android_native_app_glue;
|
||||||
|
import android.input;
|
||||||
|
|
||||||
|
import dlangui.core.logger;
|
||||||
|
|
||||||
|
|
||||||
|
alias IMMResult = int;
|
||||||
|
// values from InputMethodManager.java
|
||||||
|
private enum : IMMResult
|
||||||
|
{
|
||||||
|
RESULT_UNCHANGED_SHOWN = 0,
|
||||||
|
RESULT_UNCHANGED_HIDDEN = 1,
|
||||||
|
RESULT_SHOWN = 2,
|
||||||
|
RESULT_HIDDEN = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
alias IMMFlags = int;
|
||||||
|
// values from InputMethodManager.java
|
||||||
|
private enum : IMMFlags
|
||||||
|
{
|
||||||
|
SHOW_IMPLICIT = 0x0001,
|
||||||
|
SHOW_FORCED = 0x0002,
|
||||||
|
|
||||||
|
HIDE_IMPLICIT_ONLY = 0x0001,
|
||||||
|
HIDE_NOT_ALWAYS = 0x0002,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JNI wrapper used with native actitiy to show/hide software keyboard
|
||||||
|
* It relies on java reflection and it might be slow.
|
||||||
|
*/
|
||||||
|
version(Android)
|
||||||
|
void showSoftKeyboard(android_app* app, bool shouldShow)
|
||||||
|
{
|
||||||
|
// The code is based on https://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity
|
||||||
|
// Attaches the current thread to the JVM.
|
||||||
|
jint result;
|
||||||
|
IMMFlags flags;
|
||||||
|
|
||||||
|
auto javaVM = app.activity.vm;
|
||||||
|
auto env = app.activity.env;
|
||||||
|
|
||||||
|
JavaVMAttachArgs attachArgs;
|
||||||
|
attachArgs.version_ = JNI_VERSION_1_6;
|
||||||
|
attachArgs.name = "NativeThread";
|
||||||
|
attachArgs.group = null;
|
||||||
|
|
||||||
|
if ((*javaVM).AttachCurrentThread(javaVM, &env, &attachArgs) == JNI_ERR)
|
||||||
|
{
|
||||||
|
Log.e("showSoftKeyboard Unable to attach to JVM");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves NativeActivity.
|
||||||
|
jobject nativeActivity = app.activity.clazz;
|
||||||
|
jclass nativeActivityClass = (*env).GetObjectClass(env, nativeActivity);
|
||||||
|
|
||||||
|
// Retrieves Context.INPUT_METHOD_SERVICE.
|
||||||
|
jclass contextClass = (*env).FindClass(env, "android/content/Context");
|
||||||
|
jfieldID FieldINPUT_METHOD_SERVICE =
|
||||||
|
(*env).GetStaticFieldID(env, contextClass,
|
||||||
|
"INPUT_METHOD_SERVICE", "Ljava/lang/String;");
|
||||||
|
jobject INPUT_METHOD_SERVICE =
|
||||||
|
(*env).GetStaticObjectField(env, contextClass, FieldINPUT_METHOD_SERVICE);
|
||||||
|
//jniCheck(INPUT_METHOD_SERVICE);
|
||||||
|
|
||||||
|
// Runs getSystemService(Context.INPUT_METHOD_SERVICE).
|
||||||
|
jclass immClass = (*env).FindClass(
|
||||||
|
env, "android/view/inputmethod/InputMethodManager");
|
||||||
|
jmethodID MethodGetSystemService = (*env).GetMethodID(
|
||||||
|
env, nativeActivityClass, "getSystemService",
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/Object;");
|
||||||
|
jobject imm = (*env).CallObjectMethod(
|
||||||
|
env, nativeActivity, MethodGetSystemService,
|
||||||
|
INPUT_METHOD_SERVICE);
|
||||||
|
|
||||||
|
// Runs getWindow().getDecorView().
|
||||||
|
jmethodID MethodGetWindow = (*env).GetMethodID(
|
||||||
|
env, nativeActivityClass, "getWindow", "()Landroid/view/Window;");
|
||||||
|
jobject window = (*env).CallObjectMethod(
|
||||||
|
env, nativeActivity, MethodGetWindow);
|
||||||
|
jclass windowClass = (*env).FindClass(
|
||||||
|
env, "android/view/Window");
|
||||||
|
jmethodID MethodGetDecorView = (*env).GetMethodID(
|
||||||
|
env, windowClass, "getDecorView", "()Landroid/view/View;");
|
||||||
|
jobject decorView = (*env).CallObjectMethod(
|
||||||
|
env, window, MethodGetDecorView);
|
||||||
|
|
||||||
|
if (shouldShow) {
|
||||||
|
// Runs imm.showSoftInput(...).
|
||||||
|
jmethodID MethodShowSoftInput = (*env).GetMethodID(
|
||||||
|
env, immClass, "showSoftInput", "(Landroid/view/View;I)Z");
|
||||||
|
jboolean res = (*env).CallBooleanMethod(
|
||||||
|
env, imm, MethodShowSoftInput, decorView, flags);
|
||||||
|
} else {
|
||||||
|
// Runs lWindow.getViewToken()
|
||||||
|
jclass viewClass = (*env).FindClass(
|
||||||
|
env, "android/view/View");
|
||||||
|
jmethodID MethodGetWindowToken = (*env).GetMethodID(
|
||||||
|
env, viewClass, "getWindowToken", "()Landroid/os/IBinder;");
|
||||||
|
jobject binder = (*env).CallObjectMethod(
|
||||||
|
env, decorView, MethodGetWindowToken);
|
||||||
|
|
||||||
|
// lInputMethodManager.hideSoftInput(...).
|
||||||
|
jmethodID MethodHideSoftInput = (*env).GetMethodID(
|
||||||
|
env, immClass, "hideSoftInputFromWindow",
|
||||||
|
"(Landroid/os/IBinder;I)Z");
|
||||||
|
jboolean res = (*env).CallBooleanMethod(
|
||||||
|
env, imm, MethodHideSoftInput, binder, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished with the JVM.
|
||||||
|
(*javaVM).DetachCurrentThread(javaVM);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int GetUnicodeChar(android_app* app, int eventType, int keyCode, int metaState)
|
||||||
|
{
|
||||||
|
auto javaVM = app.activity.vm;
|
||||||
|
auto env = app.activity.env;
|
||||||
|
|
||||||
|
JavaVMAttachArgs attachArgs;
|
||||||
|
attachArgs.version_ = JNI_VERSION_1_6;
|
||||||
|
attachArgs.name = "NativeThread";
|
||||||
|
attachArgs.group = null;
|
||||||
|
|
||||||
|
if ((*javaVM).AttachCurrentThread(javaVM, &env, &attachArgs) == JNI_ERR)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
jclass class_key_event = (*env).FindClass(env, "android/view/KeyEvent");
|
||||||
|
int unicodeKey;
|
||||||
|
|
||||||
|
if(metaState == 0)
|
||||||
|
{
|
||||||
|
jmethodID method_get_unicode_char = (*env).GetMethodID(env, class_key_event, "getUnicodeChar", "()I");
|
||||||
|
jmethodID eventConstructor = (*env).GetMethodID(env, class_key_event, "<init>", "(II)V");
|
||||||
|
jobject eventObj = (*env).NewObject(env, class_key_event, eventConstructor, eventType, keyCode);
|
||||||
|
|
||||||
|
unicodeKey = (*env).CallIntMethod(env, eventObj, method_get_unicode_char);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jmethodID method_get_unicode_char = (*env).GetMethodID(env, class_key_event, "getUnicodeChar", "(I)I");
|
||||||
|
jmethodID eventConstructor = (*env).GetMethodID(env, class_key_event, "<init>", "(II)V");
|
||||||
|
jobject eventObj = (*env).NewObject(env, class_key_event, eventConstructor, eventType, keyCode);
|
||||||
|
|
||||||
|
unicodeKey = (*env).CallIntMethod(env, eventObj, method_get_unicode_char, metaState);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*javaVM).DetachCurrentThread(javaVM);
|
||||||
|
|
||||||
|
return unicodeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Issue: native app glue seems to mess up the input.
|
||||||
|
// It is clearly seen in debugger that initally key event do have real input,
|
||||||
|
// but second time it is called it is all messed up
|
||||||
|
string GetUnicodeString(android_app* app, AInputEvent* event)
|
||||||
|
{
|
||||||
|
string str;
|
||||||
|
auto javaVM = app.activity.vm;
|
||||||
|
auto env = app.activity.env;
|
||||||
|
|
||||||
|
JavaVMAttachArgs attachArgs;
|
||||||
|
attachArgs.version_ = JNI_VERSION_1_6;
|
||||||
|
attachArgs.name = "NativeThread";
|
||||||
|
attachArgs.group = null;
|
||||||
|
|
||||||
|
if ((*javaVM).AttachCurrentThread(javaVM, &env, &attachArgs) == JNI_ERR)
|
||||||
|
{
|
||||||
|
Log.e("showSoftKeyboard Unable to attach to JVM");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jclass class_key_event = (*env).FindClass(env, "android/view/KeyEvent");
|
||||||
|
|
||||||
|
jmethodID eventConstructor = (*env).GetMethodID(env, class_key_event, "<init>", "(JJIIIIIIII)V");
|
||||||
|
jobject eventObj = (*env).NewObject(env, class_key_event, eventConstructor,
|
||||||
|
AKeyEvent_getDownTime(event),
|
||||||
|
AKeyEvent_getEventTime(event),
|
||||||
|
AKeyEvent_getAction(event),
|
||||||
|
AKeyEvent_getKeyCode(event),
|
||||||
|
AKeyEvent_getRepeatCount(event),
|
||||||
|
AKeyEvent_getMetaState(event),
|
||||||
|
AInputEvent_getDeviceId(event),
|
||||||
|
AKeyEvent_getScanCode(event),
|
||||||
|
AKeyEvent_getFlags(event),
|
||||||
|
AInputEvent_getSource(event)
|
||||||
|
);
|
||||||
|
|
||||||
|
// this won't work because characters is a member passed on construction and getCharacter() is just a getter
|
||||||
|
jmethodID method_get_characters = (*env).GetMethodID(env, class_key_event, "getCharacters", "()Ljava/lang/String;");
|
||||||
|
if (auto jstr = (*env).CallObjectMethod(env, eventObj, method_get_characters)) {
|
||||||
|
str.length = (*env).GetStringUTFLength(env, jstr);
|
||||||
|
(*env).GetStringUTFRegion(env, jstr, 0, str.length, cast(char*)str.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
jmethodID method_get_unicode_char = (*env).GetMethodID(env, class_key_event, "getUnicodeChar", "()I");
|
||||||
|
int unicodeKey = (*env).CallIntMethod(env, eventObj, method_get_unicode_char);
|
||||||
|
if (str.length == 0) {
|
||||||
|
import std.conv : to;
|
||||||
|
dchar[] tmp;
|
||||||
|
tmp ~= unicodeKey;
|
||||||
|
str = to!string(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*javaVM).DetachCurrentThread(javaVM);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
|
@ -345,8 +345,10 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
/// sets focus to this widget or suitable focusable child, returns previously focused widget
|
/// sets focus to this widget or suitable focusable child, returns previously focused widget
|
||||||
override Widget setFocus(FocusReason reason = FocusReason.Unspecified) {
|
override Widget setFocus(FocusReason reason = FocusReason.Unspecified) {
|
||||||
Widget res = super.setFocus(reason);
|
Widget res = super.setFocus(reason);
|
||||||
if (focused)
|
if (focused) {
|
||||||
|
showSoftKeyboard();
|
||||||
handleEditorStateChange();
|
handleEditorStateChange();
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1099,6 +1099,7 @@ public:
|
||||||
// try to find focusable child
|
// try to find focusable child
|
||||||
return window.focusedWidget;
|
return window.focusedWidget;
|
||||||
}
|
}
|
||||||
|
hideSoftKeyboard();
|
||||||
return window.setFocus(this, reason);
|
return window.setFocus(this, reason);
|
||||||
}
|
}
|
||||||
/// searches children for first focusable item, returns null if not found
|
/// searches children for first focusable item, returns null if not found
|
||||||
|
@ -1116,6 +1117,24 @@ public:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
final void hideSoftKeyboard() {
|
||||||
|
version(Android) {
|
||||||
|
import dlangui.platforms.android.androidapp;
|
||||||
|
if (auto androidPlatform = cast(AndroidPlatform)platform)
|
||||||
|
androidPlatform.showSoftKeyboard(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows system virtual keyabord where applicable
|
||||||
|
final void showSoftKeyboard() {
|
||||||
|
version(Android) {
|
||||||
|
import dlangui.platforms.android.androidapp;
|
||||||
|
if (auto androidPlatform = cast(AndroidPlatform)platform)
|
||||||
|
androidPlatform.showSoftKeyboard(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// Events
|
// Events
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue