From 1797135db50825c8b9fe0e97da8b7e51f9cc3cee Mon Sep 17 00:00:00 2001 From: thedmd Date: Thu, 6 Jan 2022 19:57:34 +0100 Subject: [PATCH] Backends: Android: Update to use io.AddEventKey() will full key map (#2625, #4858) --- backends/imgui_impl_android.cpp | 192 ++++++++++++++++++++++++++------ backends/imgui_impl_android.h | 5 +- 2 files changed, 164 insertions(+), 33 deletions(-) diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 6a454a4e..7a09e017 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -2,12 +2,13 @@ // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) // Implemented features: -// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // Missing features: // [ ] Platform: Clipboard support. // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. // Important: +// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) @@ -18,6 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. #include "imgui.h" @@ -30,11 +32,135 @@ #include #include +struct KeyEvent +{ + ImGuiKey Key; + bool Down; + int NativeKeycode; + int NativeScancode; + + KeyEvent(): Key(ImGuiKey_None), Down(false), NativeKeycode(-1), NativeScancode(-1) {} +}; + // Android data static double g_Time = 0.0; static ANativeWindow* g_Window; static char g_LogTag[] = "ImGuiExample"; -static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. +static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. +static ImGuiKeyModFlags g_KeyModFlags = ImGuiKeyModFlags_None; + +static ImGuiKey ImGui_ImplAndroid_KeyCodeToImGuiKey(int32_t key_code) +{ + switch (key_code) + { + case AKEYCODE_TAB: return ImGuiKey_Tab; + case AKEYCODE_DPAD_LEFT: return ImGuiKey_LeftArrow; + case AKEYCODE_DPAD_RIGHT: return ImGuiKey_RightArrow; + case AKEYCODE_DPAD_UP: return ImGuiKey_UpArrow; + case AKEYCODE_DPAD_DOWN: return ImGuiKey_DownArrow; + case AKEYCODE_PAGE_UP: return ImGuiKey_PageUp; + case AKEYCODE_PAGE_DOWN: return ImGuiKey_PageDown; + case AKEYCODE_MOVE_HOME: return ImGuiKey_Home; + case AKEYCODE_MOVE_END: return ImGuiKey_End; + case AKEYCODE_INSERT: return ImGuiKey_Insert; + case AKEYCODE_FORWARD_DEL: return ImGuiKey_Delete; + case AKEYCODE_DEL: return ImGuiKey_Backspace; + case AKEYCODE_SPACE: return ImGuiKey_Space; + case AKEYCODE_ENTER: return ImGuiKey_Enter; + case AKEYCODE_ESCAPE: return ImGuiKey_Escape; + case AKEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case AKEYCODE_COMMA: return ImGuiKey_Comma; + case AKEYCODE_MINUS: return ImGuiKey_Minus; + case AKEYCODE_PERIOD: return ImGuiKey_Period; + case AKEYCODE_SLASH: return ImGuiKey_Slash; + case AKEYCODE_SEMICOLON: return ImGuiKey_Semicolon; + case AKEYCODE_EQUALS: return ImGuiKey_Equal; + case AKEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case AKEYCODE_BACKSLASH: return ImGuiKey_Backslash; + case AKEYCODE_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case AKEYCODE_GRAVE: return ImGuiKey_GraveAccent; + case AKEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock; + case AKEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case AKEYCODE_NUM_LOCK: return ImGuiKey_NumLock; + case AKEYCODE_SYSRQ: return ImGuiKey_PrintScreen; + case AKEYCODE_BREAK: return ImGuiKey_Pause; + case AKEYCODE_NUMPAD_0: return ImGuiKey_Keypad0; + case AKEYCODE_NUMPAD_1: return ImGuiKey_Keypad1; + case AKEYCODE_NUMPAD_2: return ImGuiKey_Keypad2; + case AKEYCODE_NUMPAD_3: return ImGuiKey_Keypad3; + case AKEYCODE_NUMPAD_4: return ImGuiKey_Keypad4; + case AKEYCODE_NUMPAD_5: return ImGuiKey_Keypad5; + case AKEYCODE_NUMPAD_6: return ImGuiKey_Keypad6; + case AKEYCODE_NUMPAD_7: return ImGuiKey_Keypad7; + case AKEYCODE_NUMPAD_8: return ImGuiKey_Keypad8; + case AKEYCODE_NUMPAD_9: return ImGuiKey_Keypad9; + case AKEYCODE_NUMPAD_DOT: return ImGuiKey_KeypadDecimal; + case AKEYCODE_NUMPAD_DIVIDE: return ImGuiKey_KeypadDivide; + case AKEYCODE_NUMPAD_MULTIPLY: return ImGuiKey_KeypadMultiply; + case AKEYCODE_NUMPAD_SUBTRACT: return ImGuiKey_KeypadSubtract; + case AKEYCODE_NUMPAD_ADD: return ImGuiKey_KeypadAdd; + case AKEYCODE_NUMPAD_ENTER: return ImGuiKey_KeypadEnter; + case AKEYCODE_NUMPAD_EQUALS: return ImGuiKey_KeypadEqual; + case AKEYCODE_SHIFT_LEFT: return ImGuiKey_LeftShift; + case AKEYCODE_CTRL_LEFT: return ImGuiKey_LeftControl; + case AKEYCODE_ALT_LEFT: return ImGuiKey_LeftAlt; + case AKEYCODE_META_LEFT: return ImGuiKey_LeftSuper; + case AKEYCODE_SHIFT_RIGHT: return ImGuiKey_RightShift; + case AKEYCODE_CTRL_RIGHT: return ImGuiKey_RightControl; + case AKEYCODE_ALT_RIGHT: return ImGuiKey_RightAlt; + case AKEYCODE_META_RIGHT: return ImGuiKey_RightSuper; + case AKEYCODE_MENU: return ImGuiKey_Menu; + case AKEYCODE_0: return ImGuiKey_0; + case AKEYCODE_1: return ImGuiKey_1; + case AKEYCODE_2: return ImGuiKey_2; + case AKEYCODE_3: return ImGuiKey_3; + case AKEYCODE_4: return ImGuiKey_4; + case AKEYCODE_5: return ImGuiKey_5; + case AKEYCODE_6: return ImGuiKey_6; + case AKEYCODE_7: return ImGuiKey_7; + case AKEYCODE_8: return ImGuiKey_8; + case AKEYCODE_9: return ImGuiKey_9; + case AKEYCODE_A: return ImGuiKey_A; + case AKEYCODE_B: return ImGuiKey_B; + case AKEYCODE_C: return ImGuiKey_C; + case AKEYCODE_D: return ImGuiKey_D; + case AKEYCODE_E: return ImGuiKey_E; + case AKEYCODE_F: return ImGuiKey_F; + case AKEYCODE_G: return ImGuiKey_G; + case AKEYCODE_H: return ImGuiKey_H; + case AKEYCODE_I: return ImGuiKey_I; + case AKEYCODE_J: return ImGuiKey_J; + case AKEYCODE_K: return ImGuiKey_K; + case AKEYCODE_L: return ImGuiKey_L; + case AKEYCODE_M: return ImGuiKey_M; + case AKEYCODE_N: return ImGuiKey_N; + case AKEYCODE_O: return ImGuiKey_O; + case AKEYCODE_P: return ImGuiKey_P; + case AKEYCODE_Q: return ImGuiKey_Q; + case AKEYCODE_R: return ImGuiKey_R; + case AKEYCODE_S: return ImGuiKey_S; + case AKEYCODE_T: return ImGuiKey_T; + case AKEYCODE_U: return ImGuiKey_U; + case AKEYCODE_V: return ImGuiKey_V; + case AKEYCODE_W: return ImGuiKey_W; + case AKEYCODE_X: return ImGuiKey_X; + case AKEYCODE_Y: return ImGuiKey_Y; + case AKEYCODE_Z: return ImGuiKey_Z; + case AKEYCODE_F1: return ImGuiKey_F1; + case AKEYCODE_F2: return ImGuiKey_F2; + case AKEYCODE_F3: return ImGuiKey_F3; + case AKEYCODE_F4: return ImGuiKey_F4; + case AKEYCODE_F5: return ImGuiKey_F5; + case AKEYCODE_F6: return ImGuiKey_F6; + case AKEYCODE_F7: return ImGuiKey_F7; + case AKEYCODE_F8: return ImGuiKey_F8; + case AKEYCODE_F9: return ImGuiKey_F9; + case AKEYCODE_F10: return ImGuiKey_F10; + case AKEYCODE_F11: return ImGuiKey_F11; + case AKEYCODE_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) { @@ -45,12 +171,19 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) case AINPUT_EVENT_TYPE_KEY: { int32_t event_key_code = AKeyEvent_getKeyCode(input_event); + int32_t event_scan_code = AKeyEvent_getScanCode(input_event); int32_t event_action = AKeyEvent_getAction(input_event); int32_t event_meta_state = AKeyEvent_getMetaState(input_event); - io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0); - io.KeyShift = ((event_meta_state & AMETA_SHIFT_ON) != 0); - io.KeyAlt = ((event_meta_state & AMETA_ALT_ON) != 0); + g_KeyModFlags = ImGuiKeyModFlags_None; + if ((event_meta_state & AMETA_CTRL_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Ctrl; + if ((event_meta_state & AMETA_SHIFT_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Shift; + if ((event_meta_state & AMETA_ALT_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Alt; + if ((event_meta_state & AMETA_META_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Super; switch (event_action) { @@ -59,8 +192,21 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) // ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787 case AKEY_EVENT_ACTION_DOWN: case AKEY_EVENT_ACTION_UP: - g_KeyEventQueues[event_key_code].push(event_action); + { + ImGuiKey key = ImGui_ImplAndroid_KeyCodeToImGuiKey(event_key_code); + if (key != ImGuiKey_None && (event_action == AKEY_EVENT_ACTION_DOWN || event_action == AKEY_EVENT_ACTION_UP)) + { + KeyEvent io_event; + io_event.Key = key; + io_event.Down = event_action == AKEY_EVENT_ACTION_DOWN; + io_event.NativeKeycode = event_key_code; + io_event.NativeScancode = event_scan_code; + + g_KeyEventQueues[key].push(io_event); + } + break; + } default: break; } @@ -123,30 +269,6 @@ bool ImGui_ImplAndroid_Init(ANativeWindow* window) ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = "imgui_impl_android"; - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. - io.KeyMap[ImGuiKey_Tab] = AKEYCODE_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_RightArrow] = AKEYCODE_DPAD_RIGHT; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_UpArrow] = AKEYCODE_DPAD_UP; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_DownArrow] = AKEYCODE_DPAD_DOWN; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_PageUp] = AKEYCODE_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = AKEYCODE_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = AKEYCODE_MOVE_HOME; - io.KeyMap[ImGuiKey_End] = AKEYCODE_MOVE_END; - io.KeyMap[ImGuiKey_Insert] = AKEYCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = AKEYCODE_FORWARD_DEL; - io.KeyMap[ImGuiKey_Backspace] = AKEYCODE_DEL; - io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE; - io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER; - io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE; - io.KeyMap[ImGuiKey_KeypadEnter] = AKEYCODE_NUMPAD_ENTER; - io.KeyMap[ImGuiKey_A] = AKEYCODE_A; - io.KeyMap[ImGuiKey_C] = AKEYCODE_C; - io.KeyMap[ImGuiKey_V] = AKEYCODE_V; - io.KeyMap[ImGuiKey_X] = AKEYCODE_X; - io.KeyMap[ImGuiKey_Y] = AKEYCODE_Y; - io.KeyMap[ImGuiKey_Z] = AKEYCODE_Z; - return true; } @@ -164,10 +286,18 @@ void ImGui_ImplAndroid_NewFrame() { if (key_queue.second.empty()) continue; - io.KeysDown[key_queue.first] = (key_queue.second.front() == AKEY_EVENT_ACTION_DOWN); + + auto& key_event = key_queue.second.front(); + io.AddKeyEvent(key_event.Key, key_event.Down); + io.SetKeyEventNativeData(key_event.Key, key_event.NativeKeycode, key_event.NativeScancode); // To support legacy indexing (<1.87 user code) key_queue.second.pop(); } + io.KeyCtrl = ((g_KeyModFlags & ImGuiKeyModFlags_Ctrl) != 0); + io.KeyShift = ((g_KeyModFlags & ImGuiKeyModFlags_Shift) != 0); + io.KeyAlt = ((g_KeyModFlags & ImGuiKeyModFlags_Alt) != 0); + io.KeySuper = ((g_KeyModFlags & ImGuiKeyModFlags_Super) != 0); + // Setup display size (every frame to accommodate for window resizing) int32_t window_width = ANativeWindow_getWidth(g_Window); int32_t window_height = ANativeWindow_getHeight(g_Window); diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h index 92b466b6..8bfa1860 100644 --- a/backends/imgui_impl_android.h +++ b/backends/imgui_impl_android.h @@ -2,16 +2,17 @@ // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) // Implemented features: -// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // Missing features: // [ ] Platform: Clipboard support. // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. // Important: +// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs