Skip to content

Commit

Permalink
Android text input: Add support for Unicode characters
Browse files Browse the repository at this point in the history
  • Loading branch information
duddel committed Sep 12, 2020
1 parent 1ee314d commit f8a0f03
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import android.app.NativeActivity
import android.os.Bundle
import android.content.Context
import android.view.inputmethod.InputMethodManager
import android.view.KeyEvent
import java.util.concurrent.LinkedBlockingQueue

class MainActivity : NativeActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -19,4 +21,30 @@ class MainActivity : NativeActivity() {
val inputMM = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMM.hideSoftInputFromWindow(this.window.decorView.windowToken, 0)
}

// Queue for the Unicode characters to be polled from native code (via pollUnicodeChar())
private var unicCharQueue: LinkedBlockingQueue<Int> = LinkedBlockingQueue()

// We assume dispatchKeyEvent() of the NativeActivity is actually called for every
// KeyEvent and not consumed by any View before it reaches here
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
var unic = event.getUnicodeChar(event.metaState)

if (event.action == KeyEvent.ACTION_DOWN) {
if (unic != 0) {
unicCharQueue.offer(Integer.valueOf(unic))
} else {
unicCharQueue.offer(Integer.valueOf(0))
}
} else if (event.action == KeyEvent.ACTION_MULTIPLE) {
unic = Character.codePointAt(event.characters, 0)
unicCharQueue.offer(Integer.valueOf(unic))
}

return super.dispatchKeyEvent(event)
}

fun pollUnicodeChar(): Int {
return if (!unicCharQueue.isEmpty()) unicCharQueue.poll().toInt() else 0
}
}
55 changes: 24 additions & 31 deletions examples/imgui_impl_android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Implemented features:
// [x] Basic mouse input via touch
// [x] Open soft keyboard if io.WantTextInput and perform proper keyboard input
// [ ] Handle Unicode characters
// [x] Handle Unicode characters
// [ ] Handle physical mouse input

// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
Expand All @@ -13,6 +13,7 @@

// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-09-13: Support for Unicode characters
// 2020-08-31: On-screen and physical keyboard input (ASCII characters only)
// 2020-03-02: basic draft, touch input

Expand Down Expand Up @@ -68,9 +69,9 @@ int ImGui_ImplAndroid_showSoftInput()
}

// Unfortunately, the native KeyEvent implementation has no getUnicodeChar() function.
// Therefore, we construct a non-native KeyEvent instance and call getUnicodeChar() here via JNI.
// todo: This whole procedure is done on every key press and deserves optimization
int ImGui_ImplAndroid_getCharacter(int event_type, int key_code, int meta_state)
// Therefore, we implement the processing of KeyEvents in MainActivity.kt and poll
// the resulting Unicode characters here via JNI and send them to Dear ImGui.
int ImGui_ImplAndroid_pollUnicodeChars()
{
JavaVM *jVM = g_Activity->vm;
JNIEnv *jEnv = NULL;
Expand All @@ -83,26 +84,27 @@ int ImGui_ImplAndroid_getCharacter(int event_type, int key_code, int meta_state)
if (jniRet != JNI_OK)
return -2;

jclass keyEventCls = jEnv->FindClass("android/view/KeyEvent");
if (keyEventCls == NULL)
jclass natActClazz = jEnv->GetObjectClass(g_Activity->clazz);
if (natActClazz == NULL)
return -3;

jmethodID keyEventCtor = jEnv->GetMethodID(keyEventCls, "<init>", "(II)V");
if (keyEventCtor == NULL)
jmethodID methodID = jEnv->GetMethodID(natActClazz, "pollUnicodeChar", "()I");
if (methodID == NULL)
return -4;

jmethodID keyEventGetUnicodeChar = jEnv->GetMethodID(keyEventCls, "getUnicodeChar", "(I)I");
if (keyEventGetUnicodeChar == NULL)
return -5;

jobject jKeyEvent = jEnv->NewObject(keyEventCls, keyEventCtor, event_type, key_code);
jint keyEventChar = jEnv->CallIntMethod(jKeyEvent, keyEventGetUnicodeChar, meta_state);
// Send the actual characters to Dear ImGui
ImGuiIO &io = ImGui::GetIO();
jint unicChar;
while ((unicChar = jEnv->CallIntMethod(g_Activity->clazz, methodID)) != 0)
{
io.AddInputCharacter(unicChar);
}

jVM->DetachCurrentThread();
jniRet = jVM->DetachCurrentThread();
if (jniRet != JNI_OK)
return -6;
return -5;

return keyEventChar;
return 0;
}

int32_t ImGui_ImplAndroid_handleInputEvent(AInputEvent *inputEvent)
Expand All @@ -128,22 +130,9 @@ int32_t ImGui_ImplAndroid_handleInputEvent(AInputEvent *inputEvent)
// and process one event per key per ImGui frame in ImGui_ImplAndroid_NewFrame().
// ...or consider ImGui IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
case AKEY_EVENT_ACTION_DOWN:
{
// todo: The procedure in ImGui_ImplAndroid_getCharacter() returns ASCII characters
// only. To get unicode characters, we may have to consider AKEY_EVENT_ACTION_MULTIPLE
// and feed the result into ImGui via io.AddInputCharactersUTF8()
int character = ImGui_ImplAndroid_getCharacter(evAction, evKeyCode, evMetaState);
// make sure the character really is ASCII only
if (character > 127)
character = 63; // questionmark
io.AddInputCharacter(character);
}
// intended fallthrough...
case AKEY_EVENT_ACTION_UP:
g_keyEventQueues[evKeyCode].push(evAction);
break;
case AKEY_EVENT_ACTION_MULTIPLE:
break;
default:
break;
}
Expand Down Expand Up @@ -216,13 +205,17 @@ bool ImGui_ImplAndroid_Init(ANativeActivity *activity, ANativeWindow *window)
return true;
}

void ImGui_ImplAndroid_Shutdown() { }
void ImGui_ImplAndroid_Shutdown() {}

void ImGui_ImplAndroid_NewFrame()
{
ImGuiIO &io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");

// Poll Unicode characters via JNI
// todo: do not call this every frame because of JNI overhead
ImGui_ImplAndroid_pollUnicodeChars();

// Process queued key events
for (auto &keyQueue : g_keyEventQueues)
{
Expand Down
4 changes: 3 additions & 1 deletion examples/imgui_impl_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

// Implemented features:
// [x] Basic mouse input via touch
// [x] open soft keyboard if io.WantTextInput and perform proper keyboard input
// [x] Open soft keyboard if io.WantTextInput and perform proper keyboard input
// [x] Handle Unicode characters
// [ ] Handle physical mouse input

// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
Expand Down

0 comments on commit f8a0f03

Please sign in to comment.