diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 428f99e9592e..672a27e1d4e9 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -3,9 +3,13 @@ // Implemented features: // [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// 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: +// - 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 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. @@ -13,39 +17,35 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2021-03-02: Support for physical pointer device input (such as physical mouse) -// 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 +// 2021-03-04: Initial version. #include "imgui.h" #include "imgui_impl_android.h" #include #include #include - -// Android #include #include #include #include +// Android data static double g_Time = 0.0; static ANativeWindow* g_Window; -static char g_LogTag[] = "ImguiExample"; +static char g_LogTag[] = "ImGuiExample"; static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. -int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent) +int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) { ImGuiIO& io = ImGui::GetIO(); - int32_t event_type = AInputEvent_getType(inputEvent); + int32_t event_type = AInputEvent_getType(input_event); switch (event_type) { case AINPUT_EVENT_TYPE_KEY: { - int32_t event_key_code = AKeyEvent_getKeyCode(inputEvent); - int32_t event_action = AKeyEvent_getAction(inputEvent); - int32_t event_meta_state = AKeyEvent_getMetaState(inputEvent); + int32_t event_key_code = AKeyEvent_getKeyCode(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); @@ -53,10 +53,9 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent) switch (event_action) { - // FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once - // as soon as a touch pointer goes up from a key. We use a simple key event queue - // 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 + // FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer + // goes up from a key. We use a simple key event queue/ and process one event per key per frame in + // 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); @@ -68,7 +67,7 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent) } case AINPUT_EVENT_TYPE_MOTION: { - int32_t event_action = AMotionEvent_getAction(inputEvent); + int32_t event_action = AMotionEvent_getAction(input_event); int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; event_action &= AMOTION_EVENT_ACTION_MASK; switch (event_action) @@ -78,33 +77,29 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent) // Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP, // but we have to process them separately to identify the actual button pressed. This is done below via // AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback). - if((AMotionEvent_getToolType(inputEvent, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER) - || (AMotionEvent_getToolType(inputEvent, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) + if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER) + || (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) { io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN) ? true : false; - io.MousePos = ImVec2( - AMotionEvent_getX(inputEvent, event_pointer_index), - AMotionEvent_getY(inputEvent, event_pointer_index)); + io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); } break; case AMOTION_EVENT_ACTION_BUTTON_PRESS: case AMOTION_EVENT_ACTION_BUTTON_RELEASE: { - int32_t button_state = AMotionEvent_getButtonState(inputEvent); + int32_t button_state = AMotionEvent_getButtonState(input_event); io.MouseDown[0] = (button_state & AMOTION_EVENT_BUTTON_PRIMARY) ? true : false; io.MouseDown[1] = (button_state & AMOTION_EVENT_BUTTON_SECONDARY) ? true : false; io.MouseDown[2] = (button_state & AMOTION_EVENT_BUTTON_TERTIARY) ? true : false; } break; case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse) - case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN - io.MousePos = ImVec2( - AMotionEvent_getX(inputEvent, event_pointer_index), - AMotionEvent_getY(inputEvent, event_pointer_index)); + case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN + io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); break; case AMOTION_EVENT_ACTION_SCROLL: - io.MouseWheel = AMotionEvent_getAxisValue(inputEvent, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index); - io.MouseWheelH = AMotionEvent_getAxisValue(inputEvent, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index); + io.MouseWheel = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index); + io.MouseWheelH = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index); break; default: break; @@ -123,11 +118,11 @@ bool ImGui_ImplAndroid_Init(ANativeWindow* window) g_Window = window; g_Time = 0.0; - // Setup back-end capabilities flags + // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = "imgui_impl_android"; - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. + // 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 @@ -161,10 +156,10 @@ 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()."); + IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); // Process queued key events - // FIXME: This is a workaround for multiple key event actions occuring at once (see above) and can be removed once we use upcoming input queue. + // FIXME: This is a workaround for multiple key event actions occurring at once (see above) and can be removed once we use upcoming input queue. for (auto& key_queue : g_KeyEventQueues) { if (key_queue.second.empty()) diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h index 6ba3263feb2c..e2f1757360d6 100644 --- a/backends/imgui_impl_android.h +++ b/backends/imgui_impl_android.h @@ -3,9 +3,13 @@ // Implemented features: // [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// 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: +// - 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 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. @@ -16,7 +20,7 @@ struct ANativeWindow; struct AInputEvent; -IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent); IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window); +IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event); IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown(); IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame(); diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index fbc167d65423..cd0f51dbcdb1 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -149,7 +149,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendPlatformName = "imgui_impl_glfw"; - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. + // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; diff --git a/backends/imgui_impl_marmalade.cpp b/backends/imgui_impl_marmalade.cpp index c4f83a80488d..c0c06e1e62a0 100644 --- a/backends/imgui_impl_marmalade.cpp +++ b/backends/imgui_impl_marmalade.cpp @@ -220,7 +220,8 @@ bool ImGui_Marmalade_Init(bool install_callbacks) ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = io.BackendRendererName = "imgui_impl_marmalade"; - io.KeyMap[ImGuiKey_Tab] = s3eKeyTab; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. + // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. + io.KeyMap[ImGuiKey_Tab] = s3eKeyTab io.KeyMap[ImGuiKey_LeftArrow] = s3eKeyLeft; io.KeyMap[ImGuiKey_RightArrow] = s3eKeyRight; io.KeyMap[ImGuiKey_UpArrow] = s3eKeyUp; diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index bf273ac61f67..7ab70c57fa7b 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -138,7 +138,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendPlatformName = "imgui_impl_sdl"; - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. + // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 1eb290a3cc07..348818780559 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -731,7 +731,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects() bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format) { - // Setup back-end capabilities flags + // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendRendererName = "imgui_impl_webgpu"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index df5f50296b8b..04e462ce4ac0 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -91,7 +91,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.BackendPlatformName = "imgui_impl_win32"; io.ImeWindowHandle = hwnd; - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. + // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. io.KeyMap[ImGuiKey_Tab] = VK_TAB; io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0594222fa482..814b9b84375b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,12 +51,17 @@ Other Changes: - Window: Shrink close button hit-testing region when it covers an abnormally high portion of the window visible area (e.g. when window is collapsed + moved in a corner) to facilitate moving the window away. (#3825) - Window, Nav: Fixed crash when calling SetWindowFocus(NULL) as the time a new window appears. (#3865) [@nem0] +- Tables: Fixed unaligned accesses when using TableSetBgColor(ImGuiTableBgTarget_CellBg). (#3872) +- IsItemHovered(): fixed return value false positive when used after EndChild(), EndGroup() or widgets using + either of them, when the hovered location is located within a child window, e.g. InputTextMultiline(). + This is intended to have no side effects, but brace yourself for the possible comeback.. (#3851, #1370) - Added GetAllocatorFunctions() to facilitate sharing allocators accross DLL boundaries. (#3836) - ImFontAtlas: Added 'bool TexPixelsUseColors' output to help backend decide of underlying texture format. (#3369) This can currently only ever be set by the Freetype renderer. - ImDrawList: AddCircle, AddCircleFilled(): Tweaked default segment count calculation to honor MaxError with more accuracy. Made default segment count always even for better looking result. (#3808) [@thedmd] - ImDrawList: AddCircle, AddCircleFilled(): New default for style. +- Backends: Android: Added native Android backend. (#3446) [@duddel] - Backends: Win32: Added ImGui_ImplWin32_EnableAlphaCompositing() to facilitate experimenting with alpha compositing and transparent windows. (#2766, #3447 etc.). - Backends: OpenGL, Vulkan, DX9, DX10, DX11, DX12, Metal, WebGPU, Allegro: Rework blending equation to @@ -65,6 +70,7 @@ Other Changes: (#2693, #2764, #2766, #2873, #3447, #3813, #3816) [@ocornut, @thedmd, @ShawnM427, @Ubpa, @aiekick] - Backends: DX9: Fix to support IMGUI_USE_BGRA_PACKED_COLOR. (#3844) [@Xiliusha] - Backends: DX9: Fix to support colored glyphs, using newly introduced 'TexPixelsUseColors' info. (#3844) +- Examples: Android: Added Android + GL ES2 example. (#3446) [@duddel] - Examples: Reworked setup of clear color to be compatible with transparent values. - CI: Use a dedicated "scheduled" workflow to trigger scheduled builds. Forks may disable this workflow if scheduled builds builds are not required. [@rokups] diff --git a/examples/example_android_opengl3/CMakeLists.txt b/examples/example_android_opengl3/CMakeLists.txt index 0010b4bdb832..63531f4dc48b 100644 --- a/examples/example_android_opengl3/CMakeLists.txt +++ b/examples/example_android_opengl3/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6) -project(ImguiExample) +project(ImGuiExample) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml index ea838e6d7140..c4009e5291f9 100644 --- a/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml +++ b/examples/example_android_opengl3/android/app/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ package="imgui.example.android"> @@ -13,7 +13,7 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:configChanges="orientation|keyboardHidden|screenSize"> + android:value="ImGuiExample" /> diff --git a/examples/example_android_opengl3/main.cpp b/examples/example_android_opengl3/main.cpp index 9d38531b9a61..5ab6903489bc 100644 --- a/examples/example_android_opengl3/main.cpp +++ b/examples/example_android_opengl3/main.cpp @@ -10,98 +10,18 @@ #include #include +// Data static EGLDisplay g_EglDisplay = EGL_NO_DISPLAY; static EGLSurface g_EglSurface = EGL_NO_SURFACE; static EGLContext g_EglContext = EGL_NO_CONTEXT; static struct android_app* g_App = NULL; static bool g_Initialized = false; -static char g_LogTag[] = "ImguiExample"; +static char g_LogTag[] = "ImGuiExample"; -// Unfortunately, there is no way to show the on-screen input from native code. -// Therefore, we call showSoftInput() of the main activity implemented in MainActivity.kt via JNI. -static int showSoftInput() -{ - JavaVM* java_vm = g_App->activity->vm; - JNIEnv* java_env = NULL; - - jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); - if (jni_return == JNI_ERR) - return -1; - - jni_return = java_vm->AttachCurrentThread(&java_env, NULL); - if (jni_return != JNI_OK) - return -2; - - jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); - if (native_activity_clazz == NULL) - return -3; - - jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "showSoftInput", "()V"); - if (method_id == NULL) - return -4; - - java_env->CallVoidMethod(g_App->activity->clazz, method_id); - - jni_return = java_vm->DetachCurrentThread(); - if (jni_return != JNI_OK) - return -5; - - return 0; -} - -// Unfortunately, the native KeyEvent implementation has no getUnicodeChar() function. -// 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. -static int pollUnicodeChars() -{ - JavaVM* java_vm = g_App->activity->vm; - JNIEnv* java_env = NULL; - - jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); - if (jni_return == JNI_ERR) - return -1; - - jni_return = java_vm->AttachCurrentThread(&java_env, NULL); - if (jni_return != JNI_OK) - return -2; - - jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); - if (native_activity_clazz == NULL) - return -3; - - jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "pollUnicodeChar", "()I"); - if (method_id == NULL) - return -4; - - // Send the actual characters to Dear ImGui - ImGuiIO& io = ImGui::GetIO(); - jint unicode_character; - while ((unicode_character = java_env->CallIntMethod(g_App->activity->clazz, method_id)) != 0) - { - io.AddInputCharacter(unicode_character); - } - - jni_return = java_vm->DetachCurrentThread(); - if (jni_return != JNI_OK) - return -5; - - return 0; -} - -static int GetAssetData(const char* filename, void** outData) -{ - int num_bytes = 0; - AAsset* asset_descriptor = AAssetManager_open(g_App->activity->assetManager, filename, AASSET_MODE_BUFFER); - if(asset_descriptor) - { - num_bytes = AAsset_getLength(asset_descriptor); - *outData = IM_ALLOC(num_bytes); - int64_t num_bytes_read = AAsset_read(asset_descriptor, *outData, num_bytes); - AAsset_close(asset_descriptor); - IM_ASSERT(num_bytes_read == num_bytes); - } - return num_bytes; -} +// Forward declarations of helper functions +static int ShowSoftKeyboardInput(); +static int PollUnicodeChars(); +static int GetAssetData(const char* filename, void** out_data); void init(struct android_app* app) { @@ -113,65 +33,66 @@ void init(struct android_app* app) // Initialize EGL // This is mostly boilerplate code for EGL... - g_EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - - if (g_EglDisplay == EGL_NO_DISPLAY) - __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglGetDisplay(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY"); - - if (eglInitialize(g_EglDisplay, 0, 0) != EGL_TRUE) - __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglInitialize(..) returned with an error"); - - const EGLint egl_attributes[] = { - EGL_BLUE_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_RED_SIZE, 8, - EGL_DEPTH_SIZE, 24, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_NONE}; - - EGLint num_configs = 0; - if (eglChooseConfig(g_EglDisplay, egl_attributes, nullptr, 0, &num_configs) != EGL_TRUE) - __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig(..) returned with an error"); - - if (num_configs == 0) - __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig(..) returned 0 matching configs"); - - // Get the (first) matching config - EGLConfig egl_config; - eglChooseConfig(g_EglDisplay, egl_attributes, &egl_config, 1, &num_configs); - EGLint egl_format; - eglGetConfigAttrib(g_EglDisplay, egl_config, EGL_NATIVE_VISUAL_ID, &egl_format); - ANativeWindow_setBuffersGeometry(g_App->window, 0, 0, egl_format); - - const EGLint egl_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; - g_EglContext = eglCreateContext(g_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes); - - if (g_EglContext == EGL_NO_CONTEXT) - __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglCreateContext(..) returned EGL_NO_CONTEXT"); - - g_EglSurface = eglCreateWindowSurface(g_EglDisplay, egl_config, g_App->window, NULL); - eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext); + { + g_EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (g_EglDisplay == EGL_NO_DISPLAY) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglGetDisplay(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY"); + + if (eglInitialize(g_EglDisplay, 0, 0) != EGL_TRUE) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglInitialize() returned with an error"); + + const EGLint egl_attributes[] = { EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; + EGLint num_configs = 0; + if (eglChooseConfig(g_EglDisplay, egl_attributes, nullptr, 0, &num_configs) != EGL_TRUE) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned with an error"); + if (num_configs == 0) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned 0 matching config"); + + // Get the first matching config + EGLConfig egl_config; + eglChooseConfig(g_EglDisplay, egl_attributes, &egl_config, 1, &num_configs); + EGLint egl_format; + eglGetConfigAttrib(g_EglDisplay, egl_config, EGL_NATIVE_VISUAL_ID, &egl_format); + ANativeWindow_setBuffersGeometry(g_App->window, 0, 0, egl_format); + + const EGLint egl_context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; + g_EglContext = eglCreateContext(g_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes); + + if (g_EglContext == EGL_NO_CONTEXT) + __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglCreateContext() returned EGL_NO_CONTEXT"); + + g_EglSurface = eglCreateWindowSurface(g_EglDisplay, egl_config, g_App->window, NULL); + eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext); + } - // Dear Imgui + // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); + + // Disable loading/saving of .ini file from disk. + // FIXME: Consider using LoadIniSettingsFromMemory() / SaveIniSettingsToMemory() to save in appropriate location for Android. io.IniFilename = NULL; + + // Setup Dear ImGui style ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Setup Platform/Renderer backends ImGui_ImplAndroid_Init(g_App->window); ImGui_ImplOpenGL3_Init("#version 300 es"); // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - add_font_from_assets_ttf() will return the ImFont* so you can store it if you need to select the font among multiple. // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'docs/FONTS.md' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - // - The TTF files have to be placed into the assets/ directory (android/app/src/main/assets). + // - Android: The TTF files have to be placed into the assets/ directory (android/app/src/main/assets), we use our GetAssetData() helper to retrieve them. // We load the default font with increased size to improve readability on many devices with "high" DPI. - // FIXME: Put some effort into DPI awareness + // FIXME: Put some effort into DPI awareness. + // Important: when calling AddFontFromMemoryTTF(), ownership of font_data is transfered by Dear ImGui by default (deleted is handled by Dear ImGui), unless we set FontDataOwnedByAtlas=false in ImFontConfig ImFontConfig font_cfg; font_cfg.SizePixels = 22.0f; io.Fonts->AddFontDefault(&font_cfg); @@ -179,19 +100,19 @@ void init(struct android_app* app) //int font_data_size; //ImFont* font; //font_data_size = GetAssetData("Roboto-Medium.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); //IM_ASSERT(font != NULL); //font_data_size = GetAssetData("Cousine-Regular.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 15.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 15.0f); //IM_ASSERT(font != NULL); //font_data_size = GetAssetData("DroidSans.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); //IM_ASSERT(font != NULL); //font_data_size = GetAssetData("ProggyTiny.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 10.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 10.0f); //IM_ASSERT(font != NULL); //font_data_size = GetAssetData("ArialUni.ttf", &font_data); - //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. + //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != NULL); // Arbitrary scale-up @@ -203,75 +124,74 @@ void init(struct android_app* app) void tick() { - // Our state (Dear Imgui) + ImGuiIO& io = ImGui::GetIO(); + if (g_EglDisplay == EGL_NO_DISPLAY) + return; + + // Our state static bool show_demo_window = true; static bool show_another_window = false; static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - if (g_EglDisplay != EGL_NO_DISPLAY) - { - ImGuiIO& io = ImGui::GetIO(); - - // Poll Unicode characters via JNI - // FIXME: do not call this every frame because of JNI overhead - pollUnicodeChars(); - - // Open on-screen (soft) input if demanded by Dear ImGui - static bool WantTextInputLast = false; - if (io.WantTextInput && !WantTextInputLast) - showSoftInput(); - WantTextInputLast = io.WantTextInput; + // Poll Unicode characters via JNI + // FIXME: do not call this every frame because of JNI overhead + PollUnicodeChars(); - // Start the Dear ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplAndroid_NewFrame(); - ImGui::NewFrame(); + // Open on-screen (soft) input if requested by Dear ImGui + static bool WantTextInputLast = false; + if (io.WantTextInput && !WantTextInputLast) + ShowSoftKeyboardInput(); + WantTextInputLast = io.WantTextInput; - // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). - if (show_demo_window) - ImGui::ShowDemoWindow(&show_demo_window); + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplAndroid_NewFrame(); + ImGui::NewFrame(); - // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. - { - static float f = 0.0f; - static int counter = 0; + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); - ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. + { + static float f = 0.0f; + static int counter = 0; - ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) - ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state - ImGui::Checkbox("Another Window", &show_another_window); + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. - ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f - ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); - if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) - counter++; - ImGui::SameLine(); - ImGui::Text("counter = %d", counter); + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::End(); - } + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); - // 3. Show another simple window. - if (show_another_window) - { - ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) - ImGui::Text("Hello from another window!"); - if (ImGui::Button("Close Me")) - show_another_window = false; - ImGui::End(); - } + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); + } - // Rendering - ImGui::Render(); - glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - eglSwapBuffers(g_EglDisplay, g_EglSurface); + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); } + + // Rendering + ImGui::Render(); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + eglSwapBuffers(g_EglDisplay, g_EglSurface); } void shutdown() @@ -279,7 +199,7 @@ void shutdown() if (!g_Initialized) return; - // Cleanup (Dear Imgui) + // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplAndroid_Shutdown(); ImGui::DestroyContext(); @@ -362,3 +282,88 @@ void android_main(struct android_app* app) tick(); } } + +// Unfortunately, there is no way to show the on-screen input from native code. +// Therefore, we call ShowSoftKeyboardInput() of the main activity implemented in MainActivity.kt via JNI. +static int ShowSoftKeyboardInput() +{ + JavaVM* java_vm = g_App->activity->vm; + JNIEnv* java_env = NULL; + + jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); + if (jni_return == JNI_ERR) + return -1; + + jni_return = java_vm->AttachCurrentThread(&java_env, NULL); + if (jni_return != JNI_OK) + return -2; + + jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); + if (native_activity_clazz == NULL) + return -3; + + jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "showSoftInput", "()V"); + if (method_id == NULL) + return -4; + + java_env->CallVoidMethod(g_App->activity->clazz, method_id); + + jni_return = java_vm->DetachCurrentThread(); + if (jni_return != JNI_OK) + return -5; + + return 0; +} + +// Unfortunately, the native KeyEvent implementation has no getUnicodeChar() function. +// 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. +static int PollUnicodeChars() +{ + JavaVM* java_vm = g_App->activity->vm; + JNIEnv* java_env = NULL; + + jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); + if (jni_return == JNI_ERR) + return -1; + + jni_return = java_vm->AttachCurrentThread(&java_env, NULL); + if (jni_return != JNI_OK) + return -2; + + jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); + if (native_activity_clazz == NULL) + return -3; + + jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "pollUnicodeChar", "()I"); + if (method_id == NULL) + return -4; + + // Send the actual characters to Dear ImGui + ImGuiIO& io = ImGui::GetIO(); + jint unicode_character; + while ((unicode_character = java_env->CallIntMethod(g_App->activity->clazz, method_id)) != 0) + io.AddInputCharacter(unicode_character); + + jni_return = java_vm->DetachCurrentThread(); + if (jni_return != JNI_OK) + return -5; + + return 0; +} + +// Helper to retrieve data placed into the assets/ directory (android/app/src/main/assets) +static int GetAssetData(const char* filename, void** outData) +{ + int num_bytes = 0; + AAsset* asset_descriptor = AAssetManager_open(g_App->activity->assetManager, filename, AASSET_MODE_BUFFER); + if (asset_descriptor) + { + num_bytes = AAsset_getLength(asset_descriptor); + *outData = IM_ALLOC(num_bytes); + int64_t num_bytes_read = AAsset_read(asset_descriptor, *outData, num_bytes); + AAsset_close(asset_descriptor); + IM_ASSERT(num_bytes_read == num_bytes); + } + return num_bytes; +} diff --git a/imgui.cpp b/imgui.cpp index a749f991ad34..05281dbd7d3d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -946,8 +946,8 @@ static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_d static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } #endif -static ImGuiMemAllocFunc* GImAllocatorAllocFunc = MallocWrapper; -static ImGuiMemFreeFunc* GImAllocatorFreeFunc = FreeWrapper; +static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper; +static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper; static void* GImAllocatorUserData = NULL; //----------------------------------------------------------------------------- @@ -3122,20 +3122,22 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return IsItemFocused(); // Test for bounding box overlap, as updated as ItemAdd() - if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) + ImGuiItemStatusFlags status_flags = window->DC.LastItemStatusFlags; + if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function // Test if we are hovering the right window (our window could be behind another window) - // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself. - // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while. - //if (g.HoveredWindow != window) - // return false; - if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped)) - return false; + // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) + // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable + // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was + // the test that has been running for a long while. + if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + return false; // Test if another item is active (e.g. being dragged) - if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) return false; @@ -3290,7 +3292,7 @@ void* ImGui::MemAlloc(size_t size) { if (ImGuiContext* ctx = GImGui) ctx->IO.MetricsActiveAllocations++; - return GImAllocatorAllocFunc(size, GImAllocatorUserData); + return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData); } // IM_FREE() == ImGui::MemFree() @@ -3299,7 +3301,7 @@ void ImGui::MemFree(void* ptr) if (ptr) if (ImGuiContext* ctx = GImGui) ctx->IO.MetricsActiveAllocations--; - return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); + return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData); } const char* ImGui::GetClipboardText() @@ -3336,7 +3338,7 @@ void ImGui::SetCurrentContext(ImGuiContext* ctx) #endif } -void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc* alloc_func, ImGuiMemFreeFunc* free_func, void* user_data) +void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) { GImAllocatorAllocFunc = alloc_func; GImAllocatorFreeFunc = free_func; @@ -3344,7 +3346,7 @@ void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc* alloc_func, ImGuiMemFreeFun } // This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) -void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc** p_alloc_func, ImGuiMemFreeFunc** p_free_func, void** p_user_data) +void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) { *p_alloc_func = GImAllocatorAllocFunc; *p_free_func = GImAllocatorFreeFunc; @@ -5014,6 +5016,8 @@ void ImGui::EndChild() // Not navigable into ItemAdd(bb, 0); } + if (g.HoveredWindow == window) + parent_window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; } g.WithinEndChild = false; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return @@ -6432,6 +6436,7 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind FocusWindow(NULL); } +// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. void ImGui::SetCurrentFont(ImFont* font) { ImGuiContext& g = *GImGui; @@ -7646,6 +7651,7 @@ void ImGui::BeginGroup() group_data.BackupCurrLineSize = window->DC.CurrLineSize; group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; + group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; group_data.EmitItem = true; @@ -7699,6 +7705,11 @@ void ImGui::EndGroup() window->DC.LastItemId = g.ActiveIdPreviousFrame; window->DC.LastItemRect = group_bb; + // Forward Hovered flag + const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0; + if (group_contains_curr_hovered_id) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + // Forward Edited flag if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; diff --git a/imgui.h b/imgui.h index 079902093e77..7e6489ed2155 100644 --- a/imgui.h +++ b/imgui.h @@ -194,8 +194,8 @@ typedef void* ImTextureID; // User data for rendering backend to identi typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string. typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() -typedef void* (ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() -typedef void (ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() +typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() +typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() // Character types // (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) @@ -875,8 +875,8 @@ namespace ImGui // - Those functions are not reliant on the current context. // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. - IMGUI_API void SetAllocatorFunctions(ImGuiMemAllocFunc* alloc_func, ImGuiMemFreeFunc* free_func, void* user_data = NULL); - IMGUI_API void GetAllocatorFunctions(ImGuiMemAllocFunc** p_alloc_func, ImGuiMemFreeFunc** p_free_func, void** p_user_data); + IMGUI_API void SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data = NULL); + IMGUI_API void GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data); IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); @@ -2767,7 +2767,7 @@ enum ImGuiViewportFlags_ struct ImGuiViewport { ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ - ImVec2 Pos; // Main Area: Position of the viewport (Dear Imgui coordinates are the same as OS desktop/native coordinates) + ImVec2 Pos; // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates) ImVec2 Size; // Main Area: Size of the viewport. ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0d39557209a4..b7689ed0f13f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -135,6 +135,16 @@ Index of this file: #define vsnprintf _vsnprintf #endif +// Format specifiers, printing 64-bit hasn't been decently standardized... +// In a real application you should be using PRId64 and PRIu64 from (non-windows) and on Windows define them yourself. +#ifdef _MSC_VER +#define IM_PRId64 "I64d" +#define IM_PRIu64 "I64u" +#else +#define IM_PRId64 "lld" +#define IM_PRIu64 "llu" +#endif + // Helpers macros // We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste, // but making an exception here as those are largely simplifying code... @@ -1918,12 +1928,12 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); - ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d"); - ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d"); - ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d"); - ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms"); - ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms"); - ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms"); + ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%" IM_PRId64); + ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" IM_PRId64); + ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" IM_PRId64); + ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" IM_PRIu64 " ms"); + ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" IM_PRIu64 " ms"); + ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" IM_PRIu64 " ms"); ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic); ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); @@ -1932,12 +1942,12 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams"); ImGui::Text("Sliders (reverse)"); - ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d"); - ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u"); + ImGui::SliderScalar("slider s8 reverse", ImGuiDataType_S8, &s8_v, &s8_max, &s8_min, "%d"); + ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u"); ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d"); ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u"); - ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%I64d"); - ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%I64u ms"); + ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" IM_PRId64); + ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" IM_PRIu64 " ms"); static bool inputs_step = true; ImGui::Text("Inputs"); @@ -5403,29 +5413,34 @@ static void ShowDemoWindowMisc() ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos); ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); - // Display Keyboard/Mouse state - if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) + // Display Mouse state + if (ImGui::TreeNode("Mouse State")) { if (ImGui::IsMousePosValid()) ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); else ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); + ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused + ImGui::TreePop(); + } - ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); } - ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } - ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } + // Display Keyboard/Mouse state + if (ImGui::TreeNode("Keyboard & Navigation State")) + { + ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyDown(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); } + ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } + ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. + ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. - ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); } + ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]); } ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } - ImGui::Text("NavInputs duration:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputsDownDuration[i]); } ImGui::Button("Hovering me sets the\nkeyboard capture flag"); if (ImGui::IsItemHovered()) @@ -5434,7 +5449,6 @@ static void ShowDemoWindowMisc() ImGui::Button("Holding me clears the\nthe keyboard capture flag"); if (ImGui::IsItemActive()) ImGui::CaptureKeyboardFromApp(false); - ImGui::TreePop(); } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 051bdd6dcee8..30cb78e92d33 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1461,7 +1461,7 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi return; } - const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); + const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; if (push_texture_id) PushTextureID(user_texture_id); diff --git a/imgui_internal.h b/imgui_internal.h index 1292a7cdc547..dcb90988e430 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -214,6 +214,7 @@ namespace ImStb #define IM_NEWLINE "\n" #endif #define IM_TABSIZE (4) +#define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + (_ALIGN - 1)) & ~(_ALIGN - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 #define IM_FLOOR(_VAL) ((float)(int)(_VAL)) // ImFloor() is not inlined in MSVC debug builds @@ -545,20 +546,22 @@ struct ImSpan // Helper: ImSpanAllocator<> // Facilitate storing multiple chunks into a single large block (the "arena") +// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges. template struct ImSpanAllocator { char* BasePtr; - int TotalSize; - int CurrSpan; + int CurrOff; + int CurrIdx; int Offsets[CHUNKS]; + int Sizes[CHUNKS]; ImSpanAllocator() { memset(this, 0, sizeof(*this)); } - inline void ReserveBytes(int n, size_t sz) { IM_ASSERT(n == CurrSpan && n < CHUNKS); IM_UNUSED(n); Offsets[CurrSpan++] = TotalSize; TotalSize += (int)sz; } - inline int GetArenaSizeInBytes() { return TotalSize; } + inline void Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; } + inline int GetArenaSizeInBytes() { return CurrOff; } inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; } - inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (void*)(BasePtr + Offsets[n]); } - inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (n + 1 < CHUNKS) ? BasePtr + Offsets[n + 1] : (void*)(BasePtr + TotalSize); } + inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); } + inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); } template inline void GetSpan(int n, ImSpan* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); } }; @@ -592,7 +595,7 @@ struct IMGUI_API ImPool // Helper: ImChunkStream<> // Build and iterate a contiguous stream of variable-sized structures. // This is used by Settings to store persistent data while reducing allocation count. -// We store the chunk size first, and align the final size on 4 bytes boundaries (this what the '(X + 3) & ~3' statement is for) +// We store the chunk size first, and align the final size on 4 bytes boundaries. // The tedious/zealous amount of casting is to avoid -Wcast-align warnings. template struct IMGUI_API ImChunkStream @@ -602,7 +605,7 @@ struct IMGUI_API ImChunkStream void clear() { Buf.clear(); } bool empty() const { return Buf.Size == 0; } int size() const { return Buf.Size; } - T* alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = ((HDR_SZ + sz) + 3u) & ~3u; int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); } + T* alloc_chunk(size_t sz) { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); } T* begin() { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); } T* next_chunk(T* p) { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; } int chunk_size(const T* p) { return ((const int*)p)[-1]; } @@ -694,12 +697,13 @@ enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0, - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // LastItemDisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. - ImGuiItemStatusFlags_Deactivated = 1 << 6 // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. + ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. + ImGuiItemStatusFlags_HoveredWindow = 1 << 7 // Override the HoveredWindow test to allow cross-window hover testing. #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] @@ -921,7 +925,7 @@ struct ImGuiStyleMod }; // Stacked storage data for BeginGroup()/EndGroup() -struct ImGuiGroupData +struct IMGUI_API ImGuiGroupData { ImGuiID WindowID; ImVec2 BackupCursorPos; @@ -932,6 +936,7 @@ struct ImGuiGroupData float BackupCurrLineTextBaseOffset; ImGuiID BackupActiveIdIsAlive; bool BackupActiveIdPreviousFrameIsAlive; + bool BackupHoveredIdIsAlive; bool EmitItem; }; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index a34ab42f33ef..8c7f0682a44f 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -538,9 +538,9 @@ void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) { // Allocate single buffer for our arrays ImSpanAllocator<3> span_allocator; - span_allocator.ReserveBytes(0, columns_count * sizeof(ImGuiTableColumn)); - span_allocator.ReserveBytes(1, columns_count * sizeof(ImGuiTableColumnIdx)); - span_allocator.ReserveBytes(2, columns_count * sizeof(ImGuiTableCellData)); + span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn)); + span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx)); + span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4); table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes()); memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes()); span_allocator.SetArenaBasePtr(table->RawData);