diff --git a/extern/lib/glfw3.lib b/extern/lib/glfw3.lib
index 54cd8b14..b9cbf770 100644
Binary files a/extern/lib/glfw3.lib and b/extern/lib/glfw3.lib differ
diff --git a/extern/lib/glfw3.pdb b/extern/lib/glfw3.pdb
index 07434599..8a465242 100644
Binary files a/extern/lib/glfw3.pdb and b/extern/lib/glfw3.pdb differ
diff --git a/extern/lib/glfw3d.lib b/extern/lib/glfw3d.lib
index d1a51229..55dd22b8 100644
Binary files a/extern/lib/glfw3d.lib and b/extern/lib/glfw3d.lib differ
diff --git a/extern/lib/glfw3d.pdb b/extern/lib/glfw3d.pdb
index c50d98fe..4cd2eb49 100644
Binary files a/extern/lib/glfw3d.pdb and b/extern/lib/glfw3d.pdb differ
diff --git a/extern/lib/libdeflatestatic.lib b/extern/lib/libdeflatestatic.lib
index 2c457d1d..7b9d664c 100644
Binary files a/extern/lib/libdeflatestatic.lib and b/extern/lib/libdeflatestatic.lib differ
diff --git a/extern/lib/libdeflatestatic.pdb b/extern/lib/libdeflatestatic.pdb
index 19fdb49a..e2bdf00f 100644
Binary files a/extern/lib/libdeflatestatic.pdb and b/extern/lib/libdeflatestatic.pdb differ
diff --git a/extern/src/GLFW/glfw3.h b/extern/src/GLFW/glfw3.h
index 4a2b7e23..3c20563a 100644
--- a/extern/src/GLFW/glfw3.h
+++ b/extern/src/GLFW/glfw3.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * GLFW 3.3 - www.glfw.org
+ * GLFW 3.4 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
@@ -262,13 +262,12 @@ extern "C" {
/* We are building GLFW as a Win32 DLL */
#define GLFWAPI __declspec(dllexport)
#elif defined(_WIN32) && defined(GLFW_DLL)
- /* We are calling GLFW as a Win32 DLL */
+ /* We are calling a GLFW Win32 DLL */
#define GLFWAPI __declspec(dllimport)
#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)
- /* We are building GLFW as a shared / dynamic library */
+ /* We are building GLFW as a Unix shared library */
#define GLFWAPI __attribute__((visibility("default")))
#else
- /* We are building or calling GLFW as a static library */
#define GLFWAPI
#endif
@@ -292,14 +291,14 @@ extern "C" {
* features are added to the API but it remains backward-compatible.
* @ingroup init
*/
-#define GLFW_VERSION_MINOR 3
+#define GLFW_VERSION_MINOR 4
/*! @brief The revision number of the GLFW header.
*
* The revision number of the GLFW header. This is incremented when a bug fix
* release is made that does not contain any API changes.
* @ingroup init
*/
-#define GLFW_VERSION_REVISION 7
+#define GLFW_VERSION_REVISION 0
/*! @} */
/*! @brief One.
@@ -344,10 +343,15 @@ extern "C" {
* @ingroup input
*/
#define GLFW_REPEAT 2
+
+/*! @ingroup input
+ */
+#define GLFW_KEY_UNKNOWN -1
+
/*! @} */
-/*! @defgroup keys Keyboard keys
- * @brief Keyboard key IDs.
+/*! @defgroup keys Keyboard key tokens
+ * @brief Keyboard key tokens.
*
* See [key input](@ref input_key) for how these are used.
*
@@ -357,7 +361,7 @@ extern "C" {
*
* The naming of the key codes follow these rules:
* - The US keyboard layout is used
- * - Names of printable alpha-numeric characters are used (e.g. "A", "R",
+ * - Names of printable alphanumeric characters are used (e.g. "A", "R",
* "3", etc.)
* - For non-alphanumeric characters, Unicode:ish names are used (e.g.
* "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not
@@ -370,9 +374,6 @@ extern "C" {
* @{
*/
-/* The unknown key */
-#define GLFW_KEY_UNKNOWN -1
-
/* Printable keys */
#define GLFW_KEY_SPACE 32
#define GLFW_KEY_APOSTROPHE 39 /* ' */
@@ -630,7 +631,7 @@ extern "C" {
* GLFW could not find support for the requested API on the system.
*
* @analysis The installed graphics driver does not support the requested
- * API, or does not support it via the chosen context creation backend.
+ * API, or does not support it via the chosen context creation API.
* Below are a few examples.
*
* @par
@@ -696,6 +697,66 @@ extern "C" {
* @analysis Application programmer error. Fix the offending call.
*/
#define GLFW_NO_WINDOW_CONTEXT 0x0001000A
+/*! @brief The specified cursor shape is not available.
+ *
+ * The specified standard cursor shape is not available, either because the
+ * current platform cursor theme does not provide it or because it is not
+ * available on the platform.
+ *
+ * @analysis Platform or system settings limitation. Pick another
+ * [standard cursor shape](@ref shapes) or create a
+ * [custom cursor](@ref cursor_custom).
+ */
+#define GLFW_CURSOR_UNAVAILABLE 0x0001000B
+/*! @brief The requested feature is not provided by the platform.
+ *
+ * The requested feature is not provided by the platform, so GLFW is unable to
+ * implement it. The documentation for each function notes if it could emit
+ * this error.
+ *
+ * @analysis Platform or platform version limitation. The error can be ignored
+ * unless the feature is critical to the application.
+ *
+ * @par
+ * A function call that emits this error has no effect other than the error and
+ * updating any existing out parameters.
+ */
+#define GLFW_FEATURE_UNAVAILABLE 0x0001000C
+/*! @brief The requested feature is not implemented for the platform.
+ *
+ * The requested feature has not yet been implemented in GLFW for this platform.
+ *
+ * @analysis An incomplete implementation of GLFW for this platform, hopefully
+ * fixed in a future release. The error can be ignored unless the feature is
+ * critical to the application.
+ *
+ * @par
+ * A function call that emits this error has no effect other than the error and
+ * updating any existing out parameters.
+ */
+#define GLFW_FEATURE_UNIMPLEMENTED 0x0001000D
+/*! @brief Platform unavailable or no matching platform was found.
+ *
+ * If emitted during initialization, no matching platform was found. If the @ref
+ * GLFW_PLATFORM init hint was set to `GLFW_ANY_PLATFORM`, GLFW could not detect any of
+ * the platforms supported by this library binary, except for the Null platform. If the
+ * init hint was set to a specific platform, it is either not supported by this library
+ * binary or GLFW was not able to detect it.
+ *
+ * If emitted by a native access function, GLFW was initialized for a different platform
+ * than the function is for.
+ *
+ * @analysis Failure to detect any platform usually only happens on non-macOS Unix
+ * systems, either when no window system is running or the program was run from
+ * a terminal that does not have the necessary environment variables. Fall back to
+ * a different platform if possible or notify the user that no usable platform was
+ * detected.
+ *
+ * Failure to detect a specific platform may have the same cause as above or be because
+ * support for that platform was not compiled in. Call @ref glfwPlatformSupported to
+ * check whether a specific platform is supported by a library binary.
+ */
+#define GLFW_PLATFORM_UNAVAILABLE 0x0001000E
/*! @} */
/*! @addtogroup window
@@ -771,6 +832,25 @@ extern "C" {
*/
#define GLFW_FOCUS_ON_SHOW 0x0002000C
+/*! @brief Mouse input transparency window hint and attribute
+ *
+ * Mouse input transparency [window hint](@ref GLFW_MOUSE_PASSTHROUGH_hint) or
+ * [window attribute](@ref GLFW_MOUSE_PASSTHROUGH_attrib).
+ */
+#define GLFW_MOUSE_PASSTHROUGH 0x0002000D
+
+/*! @brief Initial position x-coordinate window hint.
+ *
+ * Initial position x-coordinate [window hint](@ref GLFW_POSITION_X).
+ */
+#define GLFW_POSITION_X 0x0002000E
+
+/*! @brief Initial position y-coordinate window hint.
+ *
+ * Initial position y-coordinate [window hint](@ref GLFW_POSITION_Y).
+ */
+#define GLFW_POSITION_Y 0x0002000F
+
/*! @brief Framebuffer bit depth hint.
*
* Framebuffer bit depth [hint](@ref GLFW_RED_BITS).
@@ -846,9 +926,10 @@ extern "C" {
* Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE).
*/
#define GLFW_REFRESH_RATE 0x0002100F
-/*! @brief Framebuffer double buffering hint.
+/*! @brief Framebuffer double buffering hint and attribute.
*
- * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER).
+ * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER_hint) and
+ * [attribute](@ref GLFW_DOUBLEBUFFER_attrib).
*/
#define GLFW_DOUBLEBUFFER 0x00021010
@@ -890,10 +971,15 @@ extern "C" {
#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006
/*! @brief Debug mode context hint and attribute.
*
- * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and
- * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib).
+ * Debug mode context [hint](@ref GLFW_CONTEXT_DEBUG_hint) and
+ * [attribute](@ref GLFW_CONTEXT_DEBUG_attrib).
*/
-#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007
+#define GLFW_CONTEXT_DEBUG 0x00022007
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for compatibility with earlier versions.
+ */
+#define GLFW_OPENGL_DEBUG_CONTEXT GLFW_CONTEXT_DEBUG
/*! @brief OpenGL profile hint and attribute.
*
* OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and
@@ -922,8 +1008,15 @@ extern "C" {
* [window hint](@ref GLFW_SCALE_TO_MONITOR).
*/
#define GLFW_SCALE_TO_MONITOR 0x0002200C
-/*! @brief macOS specific
- * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint).
+/*! @brief Window framebuffer scaling
+ * [window hint](@ref GLFW_SCALE_FRAMEBUFFER_hint).
+ */
+#define GLFW_SCALE_FRAMEBUFFER 0x0002200D
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for the
+ * [GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint) window hint for
+ * compatibility with earlier versions.
*/
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
/*! @brief macOS specific
@@ -942,6 +1035,16 @@ extern "C" {
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
*/
#define GLFW_X11_INSTANCE_NAME 0x00024002
+#define GLFW_WIN32_KEYBOARD_MENU 0x00025001
+/*! @brief Win32 specific [window hint](@ref GLFW_WIN32_SHOWDEFAULT_hint).
+ */
+#define GLFW_WIN32_SHOWDEFAULT 0x00025002
+/*! @brief Wayland specific
+ * [window hint](@ref GLFW_WAYLAND_APP_ID_hint).
+ *
+ * Allows specification of the Wayland app_id.
+ */
+#define GLFW_WAYLAND_APP_ID 0x00026001
/*! @} */
#define GLFW_NO_API 0
@@ -965,6 +1068,7 @@ extern "C" {
#define GLFW_CURSOR_NORMAL 0x00034001
#define GLFW_CURSOR_HIDDEN 0x00034002
#define GLFW_CURSOR_DISABLED 0x00034003
+#define GLFW_CURSOR_CAPTURED 0x00034004
#define GLFW_ANY_RELEASE_BEHAVIOR 0
#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001
@@ -974,17 +1078,31 @@ extern "C" {
#define GLFW_EGL_CONTEXT_API 0x00036002
#define GLFW_OSMESA_CONTEXT_API 0x00036003
+#define GLFW_ANGLE_PLATFORM_TYPE_NONE 0x00037001
+#define GLFW_ANGLE_PLATFORM_TYPE_OPENGL 0x00037002
+#define GLFW_ANGLE_PLATFORM_TYPE_OPENGLES 0x00037003
+#define GLFW_ANGLE_PLATFORM_TYPE_D3D9 0x00037004
+#define GLFW_ANGLE_PLATFORM_TYPE_D3D11 0x00037005
+#define GLFW_ANGLE_PLATFORM_TYPE_VULKAN 0x00037007
+#define GLFW_ANGLE_PLATFORM_TYPE_METAL 0x00037008
+
+#define GLFW_WAYLAND_PREFER_LIBDECOR 0x00038001
+#define GLFW_WAYLAND_DISABLE_LIBDECOR 0x00038002
+
+#define GLFW_ANY_POSITION 0x80000000
+
/*! @defgroup shapes Standard cursor shapes
* @brief Standard system cursor shapes.
*
- * See [standard cursor creation](@ref cursor_standard) for how these are used.
+ * These are the [standard cursor shapes](@ref cursor_standard) that can be
+ * requested from the platform (window system).
*
* @ingroup input
* @{ */
/*! @brief The regular arrow cursor shape.
*
- * The regular arrow cursor.
+ * The regular arrow cursor shape.
*/
#define GLFW_ARROW_CURSOR 0x00036001
/*! @brief The text input I-beam cursor shape.
@@ -992,31 +1110,106 @@ extern "C" {
* The text input I-beam cursor shape.
*/
#define GLFW_IBEAM_CURSOR 0x00036002
-/*! @brief The crosshair shape.
+/*! @brief The crosshair cursor shape.
*
- * The crosshair shape.
+ * The crosshair cursor shape.
*/
#define GLFW_CROSSHAIR_CURSOR 0x00036003
-/*! @brief The hand shape.
+/*! @brief The pointing hand cursor shape.
*
- * The hand shape.
+ * The pointing hand cursor shape.
*/
-#define GLFW_HAND_CURSOR 0x00036004
-/*! @brief The horizontal resize arrow shape.
+#define GLFW_POINTING_HAND_CURSOR 0x00036004
+/*! @brief The horizontal resize/move arrow shape.
*
- * The horizontal resize arrow shape.
+ * The horizontal resize/move arrow shape. This is usually a horizontal
+ * double-headed arrow.
*/
-#define GLFW_HRESIZE_CURSOR 0x00036005
-/*! @brief The vertical resize arrow shape.
+#define GLFW_RESIZE_EW_CURSOR 0x00036005
+/*! @brief The vertical resize/move arrow shape.
*
- * The vertical resize arrow shape.
+ * The vertical resize/move shape. This is usually a vertical double-headed
+ * arrow.
*/
-#define GLFW_VRESIZE_CURSOR 0x00036006
+#define GLFW_RESIZE_NS_CURSOR 0x00036006
+/*! @brief The top-left to bottom-right diagonal resize/move arrow shape.
+ *
+ * The top-left to bottom-right diagonal resize/move shape. This is usually
+ * a diagonal double-headed arrow.
+ *
+ * @note @macos This shape is provided by a private system API and may fail
+ * with @ref GLFW_CURSOR_UNAVAILABLE in the future.
+ *
+ * @note @wayland This shape is provided by a newer standard not supported by
+ * all cursor themes.
+ *
+ * @note @x11 This shape is provided by a newer standard not supported by all
+ * cursor themes.
+ */
+#define GLFW_RESIZE_NWSE_CURSOR 0x00036007
+/*! @brief The top-right to bottom-left diagonal resize/move arrow shape.
+ *
+ * The top-right to bottom-left diagonal resize/move shape. This is usually
+ * a diagonal double-headed arrow.
+ *
+ * @note @macos This shape is provided by a private system API and may fail
+ * with @ref GLFW_CURSOR_UNAVAILABLE in the future.
+ *
+ * @note @wayland This shape is provided by a newer standard not supported by
+ * all cursor themes.
+ *
+ * @note @x11 This shape is provided by a newer standard not supported by all
+ * cursor themes.
+ */
+#define GLFW_RESIZE_NESW_CURSOR 0x00036008
+/*! @brief The omni-directional resize/move cursor shape.
+ *
+ * The omni-directional resize cursor/move shape. This is usually either
+ * a combined horizontal and vertical double-headed arrow or a grabbing hand.
+ */
+#define GLFW_RESIZE_ALL_CURSOR 0x00036009
+/*! @brief The operation-not-allowed shape.
+ *
+ * The operation-not-allowed shape. This is usually a circle with a diagonal
+ * line through it.
+ *
+ * @note @wayland This shape is provided by a newer standard not supported by
+ * all cursor themes.
+ *
+ * @note @x11 This shape is provided by a newer standard not supported by all
+ * cursor themes.
+ */
+#define GLFW_NOT_ALLOWED_CURSOR 0x0003600A
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for compatibility with earlier versions.
+ */
+#define GLFW_HRESIZE_CURSOR GLFW_RESIZE_EW_CURSOR
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for compatibility with earlier versions.
+ */
+#define GLFW_VRESIZE_CURSOR GLFW_RESIZE_NS_CURSOR
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for compatibility with earlier versions.
+ */
+#define GLFW_HAND_CURSOR GLFW_POINTING_HAND_CURSOR
/*! @} */
#define GLFW_CONNECTED 0x00040001
#define GLFW_DISCONNECTED 0x00040002
+/*! @brief ANGLE rendering backend init hint.
+ *
+ * ANGLE rendering backend [init hint](@ref GLFW_ANGLE_PLATFORM_TYPE_hint).
+ */
+#define GLFW_ANGLE_PLATFORM_TYPE 0x00050002
+/*! @brief Platform selection init hint.
+ *
+ * Platform selection [init hint](@ref GLFW_PLATFORM).
+ */
+#define GLFW_PLATFORM 0x00050003
/*! @brief macOS specific init hint.
*
* macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).
@@ -1027,6 +1220,30 @@ extern "C" {
* macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint).
*/
#define GLFW_COCOA_MENUBAR 0x00051002
+/*! @brief X11 specific init hint.
+ *
+ * X11 specific [init hint](@ref GLFW_X11_XCB_VULKAN_SURFACE_hint).
+ */
+#define GLFW_X11_XCB_VULKAN_SURFACE 0x00052001
+/*! @brief Wayland specific init hint.
+ *
+ * Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint).
+ */
+#define GLFW_WAYLAND_LIBDECOR 0x00053001
+/*! @} */
+
+/*! @addtogroup init
+ * @{ */
+/*! @brief Hint value that enables automatic platform selection.
+ *
+ * Hint value for @ref GLFW_PLATFORM that enables automatic platform selection.
+ */
+#define GLFW_ANY_PLATFORM 0x00060000
+#define GLFW_PLATFORM_WIN32 0x00060001
+#define GLFW_PLATFORM_COCOA 0x00060002
+#define GLFW_PLATFORM_WAYLAND 0x00060003
+#define GLFW_PLATFORM_X11 0x00060004
+#define GLFW_PLATFORM_NULL 0x00060005
/*! @} */
#define GLFW_DONT_CARE -1
@@ -1100,6 +1317,157 @@ typedef struct GLFWwindow GLFWwindow;
*/
typedef struct GLFWcursor GLFWcursor;
+/*! @brief The function pointer type for memory allocation callbacks.
+ *
+ * This is the function pointer type for memory allocation callbacks. A memory
+ * allocation callback function has the following signature:
+ * @code
+ * void* function_name(size_t size, void* user)
+ * @endcode
+ *
+ * This function must return either a memory block at least `size` bytes long,
+ * or `NULL` if allocation failed. Note that not all parts of GLFW handle allocation
+ * failures gracefully yet.
+ *
+ * This function must support being called during @ref glfwInit but before the library is
+ * flagged as initialized, as well as during @ref glfwTerminate after the library is no
+ * longer flagged as initialized.
+ *
+ * Any memory allocated via this function will be deallocated via the same allocator
+ * during library termination or earlier.
+ *
+ * Any memory allocated via this function must be suitably aligned for any object type.
+ * If you are using C99 or earlier, this alignment is platform-dependent but will be the
+ * same as what `malloc` provides. If you are using C11 or later, this is the value of
+ * `alignof(max_align_t)`.
+ *
+ * The size will always be greater than zero. Allocations of size zero are filtered out
+ * before reaching the custom allocator.
+ *
+ * If this function returns `NULL`, GLFW will emit @ref GLFW_OUT_OF_MEMORY.
+ *
+ * This function must not call any GLFW function.
+ *
+ * @param[in] size The minimum size, in bytes, of the memory block.
+ * @param[in] user The user-defined pointer from the allocator.
+ * @return The address of the newly allocated memory block, or `NULL` if an
+ * error occurred.
+ *
+ * @pointer_lifetime The returned memory block must be valid at least until it
+ * is deallocated.
+ *
+ * @reentrancy This function should not call any GLFW function.
+ *
+ * @thread_safety This function must support being called from any thread that calls GLFW
+ * functions.
+ *
+ * @sa @ref init_allocator
+ * @sa @ref GLFWallocator
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+typedef void* (* GLFWallocatefun)(size_t size, void* user);
+
+/*! @brief The function pointer type for memory reallocation callbacks.
+ *
+ * This is the function pointer type for memory reallocation callbacks.
+ * A memory reallocation callback function has the following signature:
+ * @code
+ * void* function_name(void* block, size_t size, void* user)
+ * @endcode
+ *
+ * This function must return a memory block at least `size` bytes long, or
+ * `NULL` if allocation failed. Note that not all parts of GLFW handle allocation
+ * failures gracefully yet.
+ *
+ * This function must support being called during @ref glfwInit but before the library is
+ * flagged as initialized, as well as during @ref glfwTerminate after the library is no
+ * longer flagged as initialized.
+ *
+ * Any memory allocated via this function will be deallocated via the same allocator
+ * during library termination or earlier.
+ *
+ * Any memory allocated via this function must be suitably aligned for any object type.
+ * If you are using C99 or earlier, this alignment is platform-dependent but will be the
+ * same as what `realloc` provides. If you are using C11 or later, this is the value of
+ * `alignof(max_align_t)`.
+ *
+ * The block address will never be `NULL` and the size will always be greater than zero.
+ * Reallocations of a block to size zero are converted into deallocations before reaching
+ * the custom allocator. Reallocations of `NULL` to a non-zero size are converted into
+ * regular allocations before reaching the custom allocator.
+ *
+ * If this function returns `NULL`, GLFW will emit @ref GLFW_OUT_OF_MEMORY.
+ *
+ * This function must not call any GLFW function.
+ *
+ * @param[in] block The address of the memory block to reallocate.
+ * @param[in] size The new minimum size, in bytes, of the memory block.
+ * @param[in] user The user-defined pointer from the allocator.
+ * @return The address of the newly allocated or resized memory block, or
+ * `NULL` if an error occurred.
+ *
+ * @pointer_lifetime The returned memory block must be valid at least until it
+ * is deallocated.
+ *
+ * @reentrancy This function should not call any GLFW function.
+ *
+ * @thread_safety This function must support being called from any thread that calls GLFW
+ * functions.
+ *
+ * @sa @ref init_allocator
+ * @sa @ref GLFWallocator
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+typedef void* (* GLFWreallocatefun)(void* block, size_t size, void* user);
+
+/*! @brief The function pointer type for memory deallocation callbacks.
+ *
+ * This is the function pointer type for memory deallocation callbacks.
+ * A memory deallocation callback function has the following signature:
+ * @code
+ * void function_name(void* block, void* user)
+ * @endcode
+ *
+ * This function may deallocate the specified memory block. This memory block
+ * will have been allocated with the same allocator.
+ *
+ * This function must support being called during @ref glfwInit but before the library is
+ * flagged as initialized, as well as during @ref glfwTerminate after the library is no
+ * longer flagged as initialized.
+ *
+ * The block address will never be `NULL`. Deallocations of `NULL` are filtered out
+ * before reaching the custom allocator.
+ *
+ * If this function returns `NULL`, GLFW will emit @ref GLFW_OUT_OF_MEMORY.
+ *
+ * This function must not call any GLFW function.
+ *
+ * @param[in] block The address of the memory block to deallocate.
+ * @param[in] user The user-defined pointer from the allocator.
+ *
+ * @pointer_lifetime The specified memory block will not be accessed by GLFW
+ * after this function is called.
+ *
+ * @reentrancy This function should not call any GLFW function.
+ *
+ * @thread_safety This function must support being called from any thread that calls GLFW
+ * functions.
+ *
+ * @sa @ref init_allocator
+ * @sa @ref GLFWallocator
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+typedef void (* GLFWdeallocatefun)(void* block, void* user);
+
/*! @brief The function pointer type for error callbacks.
*
* This is the function pointer type for error callbacks. An error callback
@@ -1415,7 +1783,7 @@ typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffse
*
* @param[in] window The window that received the event.
* @param[in] key The [keyboard key](@ref keys) that was pressed or released.
- * @param[in] scancode The system-specific scancode of the key.
+ * @param[in] scancode The platform-specific scancode of the key.
* @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future
* releases may add more actions.
* @param[in] mods Bit field describing which [modifier keys](@ref mods) were
@@ -1613,6 +1981,39 @@ typedef struct GLFWimage
unsigned char* pixels;
} GLFWimage;
+/*! @brief Custom heap memory allocator.
+ *
+ * This describes a custom heap memory allocator for GLFW. To set an allocator, pass it
+ * to @ref glfwInitAllocator before initializing the library.
+ *
+ * @sa @ref init_allocator
+ * @sa @ref glfwInitAllocator
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+typedef struct GLFWallocator
+{
+ /*! The memory allocation function. See @ref GLFWallocatefun for details about
+ * allocation function.
+ */
+ GLFWallocatefun allocate;
+ /*! The memory reallocation function. See @ref GLFWreallocatefun for details about
+ * reallocation function.
+ */
+ GLFWreallocatefun reallocate;
+ /*! The memory deallocation function. See @ref GLFWdeallocatefun for details about
+ * deallocation function.
+ */
+ GLFWdeallocatefun deallocate;
+ /*! The user pointer for this custom allocator. This value will be passed to the
+ * allocator functions.
+ */
+ void* user;
+} GLFWallocator;
+
+
/*************************************************************************
* GLFW API functions
*************************************************************************/
@@ -1630,16 +2031,36 @@ typedef struct GLFWimage
* Additional calls to this function after successful initialization but before
* termination will return `GLFW_TRUE` immediately.
*
+ * The @ref GLFW_PLATFORM init hint controls which platforms are considered during
+ * initialization. This also depends on which platforms the library was compiled to
+ * support.
+ *
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_PLATFORM_UNAVAILABLE and @ref
+ * GLFW_PLATFORM_ERROR.
*
* @remark @macos This function will change the current directory of the
* application to the `Contents/Resources` subdirectory of the application's
* bundle, if present. This can be disabled with the @ref
* GLFW_COCOA_CHDIR_RESOURCES init hint.
*
+ * @remark @macos This function will create the main menu and dock icon for the
+ * application. If GLFW finds a `MainMenu.nib` it is loaded and assumed to
+ * contain a menu bar. Otherwise a minimal menu bar is created manually with
+ * common commands like Hide, Quit and About. The About entry opens a minimal
+ * about dialog with information from the application's bundle. The menu bar
+ * and dock icon can be disabled entirely with the @ref GLFW_COCOA_MENUBAR init
+ * hint.
+ *
+ * @remark __Wayland, X11:__ If the library was compiled with support for both
+ * Wayland and X11, and the @ref GLFW_PLATFORM init hint is set to
+ * `GLFW_ANY_PLATFORM`, the `XDG_SESSION_TYPE` environment variable affects
+ * which platform is picked. If the environment variable is not set, or is set
+ * to something other than `wayland` or `x11`, the regular detection mechanism
+ * will be used instead.
+ *
* @remark @x11 This function will set the `LC_CTYPE` category of the
* application locale according to the current environment if that category is
* still "C". This is because the "C" locale breaks Unicode text input.
@@ -1647,6 +2068,8 @@ typedef struct GLFWimage
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref intro_init
+ * @sa @ref glfwInitHint
+ * @sa @ref glfwInitAllocator
* @sa @ref glfwTerminate
*
* @since Added in version 1.0.
@@ -1721,6 +2144,85 @@ GLFWAPI void glfwTerminate(void);
*/
GLFWAPI void glfwInitHint(int hint, int value);
+/*! @brief Sets the init allocator to the desired value.
+ *
+ * To use the default allocator, call this function with a `NULL` argument.
+ *
+ * If you specify an allocator struct, every member must be a valid function
+ * pointer. If any member is `NULL`, this function will emit @ref
+ * GLFW_INVALID_VALUE and the init allocator will be unchanged.
+ *
+ * The functions in the allocator must fulfil a number of requirements. See the
+ * documentation for @ref GLFWallocatefun, @ref GLFWreallocatefun and @ref
+ * GLFWdeallocatefun for details.
+ *
+ * @param[in] allocator The allocator to use at the next initialization, or
+ * `NULL` to use the default one.
+ *
+ * @errors Possible errors include @ref GLFW_INVALID_VALUE.
+ *
+ * @pointer_lifetime The specified allocator is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref init_allocator
+ * @sa @ref glfwInit
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator);
+
+#if defined(VK_VERSION_1_0)
+
+/*! @brief Sets the desired Vulkan `vkGetInstanceProcAddr` function.
+ *
+ * This function sets the `vkGetInstanceProcAddr` function that GLFW will use for all
+ * Vulkan related entry point queries.
+ *
+ * This feature is mostly useful on macOS, if your copy of the Vulkan loader is in
+ * a location where GLFW cannot find it through dynamic loading, or if you are still
+ * using the static library version of the loader.
+ *
+ * If set to `NULL`, GLFW will try to load the Vulkan loader dynamically by its standard
+ * name and get this function from there. This is the default behavior.
+ *
+ * The standard name of the loader is `vulkan-1.dll` on Windows, `libvulkan.so.1` on
+ * Linux and other Unix-like systems and `libvulkan.1.dylib` on macOS. If your code is
+ * also loading it via these names then you probably don't need to use this function.
+ *
+ * The function address you set is never reset by GLFW, but it only takes effect during
+ * initialization. Once GLFW has been initialized, any updates will be ignored until the
+ * library is terminated and initialized again.
+ *
+ * @param[in] loader The address of the function to use, or `NULL`.
+ *
+ * @par Loader function signature
+ * @code
+ * PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance instance, const char* name)
+ * @endcode
+ * For more information about this function, see the
+ * [Vulkan Registry](https://www.khronos.org/registry/vulkan/).
+ *
+ * @errors None.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref vulkan_loader
+ * @sa @ref glfwInit
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+GLFWAPI void glfwInitVulkanLoader(PFN_vkGetInstanceProcAddr loader);
+
+#endif /*VK_VERSION_1_0*/
+
/*! @brief Retrieves the version of the GLFW library.
*
* This function retrieves the major, minor and revision numbers of the GLFW
@@ -1751,15 +2253,18 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev);
/*! @brief Returns a string describing the compile-time configuration.
*
* This function returns the compile-time generated
- * [version string](@ref intro_version_string) of the GLFW library binary. It
- * describes the version, platform, compiler and any platform-specific
- * compile-time options. It should not be confused with the OpenGL or OpenGL
- * ES version string, queried with `glGetString`.
+ * [version string](@ref intro_version_string) of the GLFW library binary. It describes
+ * the version, platforms, compiler and any platform or operating system specific
+ * compile-time options. It should not be confused with the OpenGL or OpenGL ES version
+ * string, queried with `glGetString`.
*
* __Do not use the version string__ to parse the GLFW library version. The
* @ref glfwGetVersion function provides the version of the running library
* binary in numerical format.
*
+ * __Do not use the version string__ to parse what platforms are supported. The @ref
+ * glfwPlatformSupported function lets you query platform support.
+ *
* @return The ASCII encoded GLFW version string.
*
* @errors None.
@@ -1856,6 +2361,51 @@ GLFWAPI int glfwGetError(const char** description);
*/
GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback);
+/*! @brief Returns the currently selected platform.
+ *
+ * This function returns the platform that was selected during initialization. The
+ * returned value will be one of `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`,
+ * `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`.
+ *
+ * @return The currently selected platform, or zero if an error occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref platform
+ * @sa @ref glfwPlatformSupported
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+GLFWAPI int glfwGetPlatform(void);
+
+/*! @brief Returns whether the library includes support for the specified platform.
+ *
+ * This function returns whether the library was compiled with support for the specified
+ * platform. The platform must be one of `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`,
+ * `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`.
+ *
+ * @param[in] platform The platform to query.
+ * @return `GLFW_TRUE` if the platform is supported, or `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_INVALID_ENUM.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref platform
+ * @sa @ref glfwGetPlatform
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup init
+ */
+GLFWAPI int glfwPlatformSupported(int platform);
+
/*! @brief Returns the currently connected monitors.
*
* This function returns an array of handles for all currently connected
@@ -1939,7 +2489,7 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos);
* This function returns the position, in screen coordinates, of the upper-left
* corner of the work area of the specified monitor along with the work area
* size in screen coordinates. The work area is defined as the area of the
- * monitor not occluded by the operating system task bar where present. If no
+ * monitor not occluded by the window system task bar where present. If no
* task bar exists then the work area is the monitor resolution in screen
* coordinates.
*
@@ -1970,10 +2520,11 @@ GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos,
* This function returns the size, in millimetres, of the display area of the
* specified monitor.
*
- * Some systems do not provide accurate monitor size information, either
- * because the monitor
- * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data)
- * data is incorrect or because the driver does not report it accurately.
+ * Some platforms do not provide accurate monitor size information, either
+ * because the monitor [EDID][] data is incorrect or because the driver does
+ * not report it accurately.
+ *
+ * [EDID]: https://en.wikipedia.org/wiki/Extended_display_identification_data
*
* Any or all of the size arguments may be `NULL`. If an error occurs, all
* non-`NULL` size arguments will be set to zero.
@@ -2020,6 +2571,9 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
+ * @remark @wayland Fractional scaling information is not yet available for
+ * monitors, so this function only returns integer content scales.
+ *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref monitor_scale
@@ -2216,11 +2770,11 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor);
* @param[in] monitor The monitor whose gamma ramp to set.
* @param[in] gamma The desired exponent.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
- * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref GLFW_INVALID_VALUE,
+ * @ref GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland Gamma handling is a privileged protocol, this function
- * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR.
+ * will thus never be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -2240,11 +2794,11 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma);
* @return The current gamma ramp, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref GLFW_PLATFORM_ERROR
+ * and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland Gamma handling is a privileged protocol, this function
- * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while
+ * will thus never be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE while
* returning `NULL`.
*
* @pointer_lifetime The returned structure and its arrays are allocated and
@@ -2279,8 +2833,8 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor);
* @param[in] monitor The monitor whose gamma ramp to set.
* @param[in] ramp The gamma ramp to use.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref GLFW_PLATFORM_ERROR
+ * and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark The size of the specified gamma ramp should match the size of the
* current ramp for that monitor.
@@ -2288,7 +2842,7 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor);
* @remark @win32 The gamma ramp size must be 256.
*
* @remark @wayland Gamma handling is a privileged protocol, this function
- * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR.
+ * will thus never be implemented and emits @ref GLFW_FEATURE_UNAVAILABLE.
*
* @pointer_lifetime The specified gamma ramp is copied before this function
* returns.
@@ -2431,10 +2985,10 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
* OpenGL or OpenGL ES context.
*
* By default, newly created windows use the placement recommended by the
- * window system. To create the window at a specific position, make it
- * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window
- * hint, set its [position](@ref window_pos) and then [show](@ref window_hide)
- * it.
+ * window system. To create the window at a specific position, set the @ref
+ * GLFW_POSITION_X and @ref GLFW_POSITION_Y window hints before creation. To
+ * restore the default behavior, set either or both hints back to
+ * `GLFW_ANY_POSITION`.
*
* As long as at least one full screen window is not iconified, the screensaver
* is prohibited from starting.
@@ -2460,8 +3014,8 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref
- * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref
- * GLFW_PLATFORM_ERROR.
+ * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE, @ref
+ * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.
*
* @remark @win32 Window creation will fail if the Microsoft GDI software
* OpenGL implementation is the only one available.
@@ -2474,40 +3028,44 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
* @remark @win32 The context to share resources with must not be current on
* any other thread.
*
- * @remark @macos The OS only supports forward-compatible core profile contexts
- * for OpenGL versions 3.2 and later. Before creating an OpenGL context of
- * version 3.2 or later you must set the
- * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and
- * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly.
- * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS.
+ * @remark @macos The OS only supports core profile contexts for OpenGL
+ * versions 3.2 and later. Before creating an OpenGL context of version 3.2 or
+ * later you must set the [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint)
+ * hint accordingly. OpenGL 3.0 and 3.1 contexts are not supported at all
+ * on macOS.
*
* @remark @macos The GLFW window has no icon, as it is not a document
* window, but the dock icon will be the same as the application bundle's icon.
* For more information on bundles, see the
- * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
- * in the Mac Developer Library.
+ * [Bundle Programming Guide][bundle-guide] in the Mac Developer Library.
*
- * @remark @macos The first time a window is created the menu bar is created.
- * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu
- * bar. Otherwise a minimal menu bar is created manually with common commands
- * like Hide, Quit and About. The About entry opens a minimal about dialog
- * with information from the application's bundle. Menu bar creation can be
- * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint.
+ * [bundle-guide]: https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/
*
* @remark @macos On OS X 10.10 and later the window frame will not be rendered
* at full resolution on Retina displays unless the
- * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint)
+ * [GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint)
* hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the
* application bundle's `Info.plist`. For more information, see
- * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html)
- * in the Mac Developer Library. The GLFW test and example programs use
- * a custom `Info.plist` template for this, which can be found as
- * `CMake/MacOSXBundleInfo.plist.in` in the source tree.
+ * [High Resolution Guidelines for OS X][hidpi-guide] in the Mac Developer
+ * Library. The GLFW test and example programs use a custom `Info.plist`
+ * template for this, which can be found as `CMake/Info.plist.in` in the source
+ * tree.
+ *
+ * [hidpi-guide]: https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
*
* @remark @macos When activating frame autosaving with
* [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified
* window size and position may be overridden by previously saved values.
*
+ * @remark @wayland GLFW uses [libdecor][] where available to create its window
+ * decorations. This in turn uses server-side XDG decorations where available
+ * and provides high quality client-side decorations on compositors like GNOME.
+ * If both XDG decorations and libdecor are unavailable, GLFW falls back to
+ * a very simple set of window decorations that only support moving, resizing
+ * and the window manager's right-click menu.
+ *
+ * [libdecor]: https://gitlab.freedesktop.org/libdecor/libdecor
+ *
* @remark @x11 Some window managers will not respect the placement of
* initially hidden windows.
*
@@ -2524,20 +3082,6 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
* [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to
* override this.
*
- * @remark @wayland Compositors should implement the xdg-decoration protocol
- * for GLFW to decorate the window properly. If this protocol isn't
- * supported, or if the compositor prefers client-side decorations, a very
- * simple fallback frame will be drawn using the wp_viewporter protocol. A
- * compositor can still emit close, maximize or fullscreen events, using for
- * instance a keybind mechanism. If neither of these protocols is supported,
- * the window won't be decorated.
- *
- * @remark @wayland A full screen window will not attempt to change the mode,
- * no matter what the requested size or refresh rate.
- *
- * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol
- * to be implemented in the user's compositor.
- *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_creation
@@ -2620,6 +3164,38 @@ GLFWAPI int glfwWindowShouldClose(GLFWwindow* window);
*/
GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value);
+/*! @brief Returns the title of the specified window.
+ *
+ * This function returns the window title, encoded as UTF-8, of the specified
+ * window. This is the title set previously by @ref glfwCreateWindow
+ * or @ref glfwSetWindowTitle.
+ *
+ * @param[in] window The window to query.
+ * @return The UTF-8 encoded window title, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark The returned title is currently a copy of the title last set by @ref
+ * glfwCreateWindow or @ref glfwSetWindowTitle. It does not include any
+ * additional text which may be appended by the platform or another program.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the next call to @ref
+ * glfwGetWindowTitle or @ref glfwSetWindowTitle, or until the library is
+ * terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_title
+ * @sa @ref glfwSetWindowTitle
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup window
+ */
+GLFWAPI const char* glfwGetWindowTitle(GLFWwindow* window);
+
/*! @brief Sets the title of the specified window.
*
* This function sets the window title, encoded as UTF-8, of the specified
@@ -2637,6 +3213,7 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value);
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_title
+ * @sa @ref glfwGetWindowTitle
*
* @since Added in version 1.0.
* @glfw3 Added window handle parameter.
@@ -2666,21 +3243,23 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
* @param[in] images The images to create the icon from. This is ignored if
* count is zero.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE, @ref GLFW_PLATFORM_ERROR and @ref
+ * GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @pointer_lifetime The specified image data is copied before this function
* returns.
*
- * @remark @macos The GLFW window has no icon, as it is not a document
- * window, so this function does nothing. The dock icon will be the same as
+ * @remark @macos Regular windows do not have icons on macOS. This function
+ * will emit @ref GLFW_FEATURE_UNAVAILABLE. The dock icon will be the same as
* the application bundle's icon. For more information on bundles, see the
- * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
- * in the Mac Developer Library.
+ * [Bundle Programming Guide][bundle-guide] in the Mac Developer Library.
+ *
+ * [bundle-guide]: https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/
*
* @remark @wayland There is no existing protocol to change an icon, the
* window will thus inherit the one defined in the application's desktop file.
- * This function always emits @ref GLFW_PLATFORM_ERROR.
+ * This function will emit @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -2706,12 +3285,12 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i
* @param[out] ypos Where to store the y-coordinate of the upper-left corner of
* the content area, or `NULL`.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland There is no way for an application to retrieve the global
- * position of its windows, this function will always emit @ref
- * GLFW_PLATFORM_ERROR.
+ * position of its windows. This function will emit @ref
+ * GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -2740,12 +3319,12 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos);
* @param[in] xpos The x-coordinate of the upper-left corner of the content area.
* @param[in] ypos The y-coordinate of the upper-left corner of the content area.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland There is no way for an application to set the global
- * position of its windows, this function will always emit @ref
- * GLFW_PLATFORM_ERROR.
+ * position of its windows. This function will emit @ref
+ * GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -2900,9 +3479,6 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
- * @remark @wayland A full screen window will not attempt to change the mode,
- * no matter what the requested size.
- *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_size
@@ -2992,7 +3568,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int
* regardless of their DPI and scaling settings. This relies on the system DPI
* and scaling settings being somewhat correct.
*
- * On systems where each monitors can have its own content scale, the window
+ * On platforms where each monitors can have its own content scale, the window
* content scale will depend on which monitor the system considers the window
* to be on.
*
@@ -3057,8 +3633,11 @@ GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window);
* @param[in] window The window to set the opacity for.
* @param[in] opacity The desired opacity of the specified window.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
+ *
+ * @remark @wayland There is no way to set an opacity factor for a window.
+ * This function will emit @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -3077,16 +3656,17 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity);
* previously restored. If the window is already iconified, this function does
* nothing.
*
- * If the specified window is a full screen window, the original monitor
- * resolution is restored until the window is restored.
+ * If the specified window is a full screen window, GLFW restores the original
+ * video mode of the monitor. The window's desired video mode is set again
+ * when the window is restored.
*
* @param[in] window The window to iconify.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
- * @remark @wayland There is no concept of iconification in wl_shell, this
- * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated
+ * @remark @wayland Once a window is iconified, @ref glfwRestoreWindow won’t
+ * be able to restore it. This is a design decision of the xdg-shell
* protocol.
*
* @thread_safety This function must only be called from the main thread.
@@ -3108,8 +3688,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window);
* (minimized) or maximized. If the window is already restored, this function
* does nothing.
*
- * If the specified window is a full screen window, the resolution chosen for
- * the window is restored on the selected monitor.
+ * If the specified window is an iconified full screen window, its desired
+ * video mode is set again for its monitor when the window is restored.
*
* @param[in] window The window to restore.
*
@@ -3233,8 +3813,8 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
- * @remark @wayland It is not possible for an application to bring its windows
- * to front, this function will always emit @ref GLFW_PLATFORM_ERROR.
+ * @remark @wayland The compositor will likely ignore focus requests unless
+ * another window created by the same application already has input focus.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -3339,9 +3919,6 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window);
* @remark @wayland The desired window position is ignored, as there is no way
* for an application to set this property.
*
- * @remark @wayland Setting the window to full screen will not attempt to
- * change the mode, no matter what the requested size or refresh rate.
- *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_monitor
@@ -3377,6 +3954,9 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int
* errors. However, this function should not fail as long as it is passed
* valid arguments and the library has been [initialized](@ref intro_init).
*
+ * @remark @wayland The Wayland protocol provides no way to check whether a
+ * window is iconfied, so @ref GLFW_ICONIFIED always returns `GLFW_FALSE`.
+ *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_attribs
@@ -3398,6 +3978,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib),
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib).
+ * [GLFW_MOUSE_PASSTHROUGH](@ref GLFW_MOUSE_PASSTHROUGH_attrib)
*
* Some of these attributes are ignored for full screen windows. The new
* value will take effect if the window is later made windowed.
@@ -3410,11 +3991,15 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
* @param[in] value `GLFW_TRUE` or `GLFW_FALSE`.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
- * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_PLATFORM_ERROR and @ref
+ * GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark Calling @ref glfwGetWindowAttrib will always return the latest
* value, even if that value is ignored by the current mode of the window.
*
+ * @remark @wayland The [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) window attribute is
+ * not supported. Setting this will emit @ref GLFW_FEATURE_UNAVAILABLE.
+ *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_attribs
@@ -3668,9 +4253,6 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
- * @remark @wayland The wl_shell protocol has no concept of iconification,
- * this callback will never be called when using this deprecated protocol.
- *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_iconify
@@ -3958,6 +4540,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
* - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual
* and unlimited cursor movement. This is useful for implementing for
* example 3D camera controls.
+ * - `GLFW_CURSOR_CAPTURED` makes the cursor visible and confines it to the
+ * content area of the window.
*
* If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to
* enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are
@@ -3983,7 +4567,7 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
* If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE`
* to enable raw (unscaled and unaccelerated) mouse motion when the cursor is
* disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported,
- * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref
+ * attempting to set this will emit @ref GLFW_FEATURE_UNAVAILABLE. Call @ref
* glfwRawMouseMotionSupported to check for support.
*
* @param[in] window The window whose input mode to set.
@@ -3993,7 +4577,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
* @param[in] value The new value of the specified input mode.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
- * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ * GLFW_INVALID_ENUM, @ref GLFW_PLATFORM_ERROR and @ref
+ * GLFW_FEATURE_UNAVAILABLE (see above).
*
* @thread_safety This function must only be called from the main thread.
*
@@ -4083,8 +4668,8 @@ GLFWAPI int glfwRawMouseMotionSupported(void);
* @param[in] scancode The scancode of the key to query.
* @return The UTF-8 encoded, layout-specific name of the key, or `NULL`.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE, @ref GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
*
* @remark The contents of the returned string may change when a keyboard
* layout change event is received.
@@ -4106,15 +4691,18 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode);
*
* This function returns the platform-specific scancode of the specified key.
*
- * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this
- * method will return `-1`.
+ * If the specified [key token](@ref keys) corresponds to a physical key not
+ * supported on the current platform then this method will return `-1`.
+ * Calling this function with anything other than a key token will return `-1`
+ * and generate a @ref GLFW_INVALID_ENUM error.
*
- * @param[in] key Any [named key](@ref keys).
- * @return The platform-specific scancode for the key, or `-1` if an
- * [error](@ref error_handling) occurred.
+ * @param[in] key Any [key token](@ref keys).
+ * @return The platform-specific scancode for the key, or `-1` if the key is
+ * not supported on the current platform or an [error](@ref error_handling)
+ * occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
- * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
*
* @thread_safety This function may be called from any thread.
*
@@ -4131,8 +4719,7 @@ GLFWAPI int glfwGetKeyScancode(int key);
*
* This function returns the last state reported for the specified key to the
* specified window. The returned state is one of `GLFW_PRESS` or
- * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to
- * the key callback.
+ * `GLFW_RELEASE`. The action `GLFW_REPEAT` is only reported to the key callback.
*
* If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns
* `GLFW_PRESS` the first time you call it for a key that was pressed, even if
@@ -4256,11 +4843,11 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos);
* @param[in] ypos The desired y-coordinate, relative to the top edge of the
* content area.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_ERROR and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @wayland This function will only work when the cursor mode is
- * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing.
+ * `GLFW_CURSOR_DISABLED`, otherwise it will emit @ref GLFW_FEATURE_UNAVAILABLE.
*
* @thread_safety This function must only be called from the main thread.
*
@@ -4293,8 +4880,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos);
* @return The handle of the created cursor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified image data is copied before this function
* returns.
@@ -4313,19 +4900,44 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot)
/*! @brief Creates a cursor with a standard shape.
*
- * Returns a cursor with a [standard shape](@ref shapes), that can be set for
- * a window with @ref glfwSetCursor.
+ * Returns a cursor with a standard shape, that can be set for a window with
+ * @ref glfwSetCursor. The images for these cursors come from the system
+ * cursor theme and their exact appearance will vary between platforms.
+ *
+ * Most of these shapes are guaranteed to exist on every supported platform but
+ * a few may not be present. See the table below for details.
+ *
+ * Cursor shape | Windows | macOS | X11 | Wayland
+ * ------------------------------ | ------- | ----- | ------ | -------
+ * @ref GLFW_ARROW_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_IBEAM_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_CROSSHAIR_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_POINTING_HAND_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_RESIZE_EW_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_RESIZE_NS_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_RESIZE_NWSE_CURSOR | Yes | Yes1 | Maybe2 | Maybe2
+ * @ref GLFW_RESIZE_NESW_CURSOR | Yes | Yes1 | Maybe2 | Maybe2
+ * @ref GLFW_RESIZE_ALL_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_NOT_ALLOWED_CURSOR | Yes | Yes | Maybe2 | Maybe2
+ *
+ * 1) This uses a private system API and may fail in the future.
+ *
+ * 2) This uses a newer standard that not all cursor themes support.
+ *
+ * If the requested shape is not available, this function emits a @ref
+ * GLFW_CURSOR_UNAVAILABLE error and returns `NULL`.
*
* @param[in] shape One of the [standard shapes](@ref shapes).
* @return A new cursor ready to use or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
- * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ * GLFW_INVALID_ENUM, @ref GLFW_CURSOR_UNAVAILABLE and @ref
+ * GLFW_PLATFORM_ERROR.
*
* @thread_safety This function must only be called from the main thread.
*
- * @sa @ref cursor_object
+ * @sa @ref cursor_standard
* @sa @ref glfwCreateCursor
*
* @since Added in version 3.1.
@@ -4399,9 +5011,9 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
* [character callback](@ref glfwSetCharCallback) instead.
*
* When a window loses input focus, it will generate synthetic key release
- * events for all pressed keys. You can tell these events from user-generated
- * events by the fact that the synthetic ones are generated after the focus
- * loss event has been processed, i.e. after the
+ * events for all pressed keys with associated key tokens. You can tell these
+ * events from user-generated events by the fact that the synthetic ones are
+ * generated after the focus loss event has been processed, i.e. after the
* [window focus callback](@ref glfwSetWindowFocusCallback) has been called.
*
* The scancode of a key is specific to that platform or sometimes even to that
@@ -4682,8 +5294,6 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun ca
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
- * @remark @wayland File drop is currently unimplemented.
- *
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref path_drop
@@ -4705,6 +5315,11 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
+ * @remark @win32 The clipboard on Windows has a single global lock for reading and
+ * writing. GLFW tries to acquire it a few times, which is almost always enough. If it
+ * cannot acquire the lock then this function emits @ref GLFW_PLATFORM_ERROR and returns.
+ * It is safe to try this multiple times.
+ *
* @pointer_lifetime The specified string is copied before this function
* returns.
*
@@ -4733,6 +5348,11 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
*
+ * @remark @win32 The clipboard on Windows has a single global lock for reading and
+ * writing. GLFW tries to acquire it a few times, which is almost always enough. If it
+ * cannot acquire the lock then this function emits @ref GLFW_PLATFORM_ERROR and returns.
+ * It is safe to try this multiple times.
+ *
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetClipboardString or @ref glfwSetClipboardString, or until the library
@@ -4760,7 +5380,7 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window);
*
* The resolution of the timer is system dependent, but is usually on the order
* of a few micro- or nanoseconds. It uses the highest-resolution monotonic
- * time source on each supported platform.
+ * time source on each operating system.
*
* @return The current time, in seconds, or zero if an
* [error](@ref error_handling) occurred.
@@ -4855,12 +5475,15 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void);
* thread.
*
* This function makes the OpenGL or OpenGL ES context of the specified window
- * current on the calling thread. A context must only be made current on
- * a single thread at a time and each thread can have only a single current
- * context at a time.
+ * current on the calling thread. It can also detach the current context from
+ * the calling thread without making a new one current by passing in `NULL`.
*
- * When moving a context between threads, you must make it non-current on the
- * old thread before making it current on the new one.
+ * A context must only be made current on a single thread at a time and each
+ * thread can have only a single current context at a time. Making a context
+ * current detaches any previously current context on the calling thread.
+ *
+ * When moving a context between threads, you must detach it (make it
+ * non-current) on the old thread before making it current on the new one.
*
* By default, making a context non-current implicitly forces a pipeline flush.
* On machines that support `GL_KHR_context_flush_control`, you can control
@@ -4875,6 +5498,10 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void);
* @param[in] window The window whose context to make current, or `NULL` to
* detach the current context.
*
+ * @remarks If the previously current context was created via a different
+ * context creation API than the one passed to this function, GLFW will still
+ * detach the previous one from its API before making the new one current.
+ *
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.
*
@@ -4971,7 +5598,7 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window);
* GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR.
*
* @remark This function is not called during context creation, leaving the
- * swap interval set to whatever is the default on that platform. This is done
+ * swap interval set to whatever is the default for that API. This is done
* because some swap interval extensions used by GLFW do not allow the swap
* interval to be reset to zero once it has been set to a non-zero value.
*
@@ -5271,6 +5898,13 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys
* @remark @macos This function creates and sets a `CAMetalLayer` instance for
* the window content view, which is required for MoltenVK to function.
*
+ * @remark @x11 By default GLFW prefers the `VK_KHR_xcb_surface` extension,
+ * with the `VK_KHR_xlib_surface` extension as a fallback. You can make
+ * `VK_KHR_xlib_surface` the preferred extension by setting the
+ * [GLFW_X11_XCB_VULKAN_SURFACE](@ref GLFW_X11_XCB_VULKAN_SURFACE_hint) init
+ * hint. The name of the selected extension, if any, is included in the array
+ * returned by @ref glfwGetRequiredInstanceExtensions.
+ *
* @thread_safety This function may be called from any thread. For
* synchronization details of Vulkan objects, see the Vulkan specification.
*
@@ -5307,6 +5941,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
*/
#ifndef GLAPIENTRY
#define GLAPIENTRY APIENTRY
+ #define GLFW_GLAPIENTRY_DEFINED
#endif
/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */
diff --git a/extern/src/GLFW/glfw3native.h b/extern/src/GLFW/glfw3native.h
index fe74c733..92f0d324 100644
--- a/extern/src/GLFW/glfw3native.h
+++ b/extern/src/GLFW/glfw3native.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * GLFW 3.3 - www.glfw.org
+ * GLFW 3.4 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
@@ -74,6 +74,16 @@ extern "C" {
* and which platform-specific headers to include. It is then up your (by
* definition platform-specific) code to handle which of these should be
* defined.
+ *
+ * If you do not want the platform-specific headers to be included, define
+ * `GLFW_NATIVE_INCLUDE_NONE` before including the @ref glfw3native.h header.
+ *
+ * @code
+ * #define GLFW_EXPOSE_NATIVE_WIN32
+ * #define GLFW_EXPOSE_NATIVE_WGL
+ * #define GLFW_NATIVE_INCLUDE_NONE
+ * #include
+ * @endcode
*/
@@ -81,44 +91,71 @@ extern "C" {
* System headers and types
*************************************************************************/
-#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
- // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
- // example to allow applications to correctly declare a GL_KHR_debug callback)
- // but windows.h assumes no one will define APIENTRY before it does
- #if defined(GLFW_APIENTRY_DEFINED)
- #undef APIENTRY
- #undef GLFW_APIENTRY_DEFINED
- #endif
- #include
-#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
- #if defined(__OBJC__)
- #import
- #else
- #include
- typedef void* id;
- #endif
-#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
- #include
- #include
-#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
- #include
-#endif
+#if !defined(GLFW_NATIVE_INCLUDE_NONE)
-#if defined(GLFW_EXPOSE_NATIVE_WGL)
- /* WGL is declared by windows.h */
-#endif
-#if defined(GLFW_EXPOSE_NATIVE_NSGL)
- /* NSGL is declared by Cocoa.h */
-#endif
-#if defined(GLFW_EXPOSE_NATIVE_GLX)
- #include
-#endif
-#if defined(GLFW_EXPOSE_NATIVE_EGL)
- #include
-#endif
-#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
- #include
-#endif
+ #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
+ /* This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
+ * example to allow applications to correctly declare a GL_KHR_debug callback)
+ * but windows.h assumes no one will define APIENTRY before it does
+ */
+ #if defined(GLFW_APIENTRY_DEFINED)
+ #undef APIENTRY
+ #undef GLFW_APIENTRY_DEFINED
+ #endif
+ #include
+ #endif
+
+ #if defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
+ #if defined(__OBJC__)
+ #import
+ #else
+ #include
+ #include
+ #endif
+ #endif
+
+ #if defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
+ #include
+ #include
+ #endif
+
+ #if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
+ #include
+ #endif
+
+ #if defined(GLFW_EXPOSE_NATIVE_WGL)
+ /* WGL is declared by windows.h */
+ #endif
+ #if defined(GLFW_EXPOSE_NATIVE_NSGL)
+ /* NSGL is declared by Cocoa.h */
+ #endif
+ #if defined(GLFW_EXPOSE_NATIVE_GLX)
+ /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by
+ * default it also acts as an OpenGL header
+ * However, glx.h will include gl.h, which will define it unconditionally
+ */
+ #if defined(GLFW_GLAPIENTRY_DEFINED)
+ #undef GLAPIENTRY
+ #undef GLFW_GLAPIENTRY_DEFINED
+ #endif
+ #include
+ #endif
+ #if defined(GLFW_EXPOSE_NATIVE_EGL)
+ #include
+ #endif
+ #if defined(GLFW_EXPOSE_NATIVE_OSMESA)
+ /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by
+ * default it also acts as an OpenGL header
+ * However, osmesa.h will include gl.h, which will define it unconditionally
+ */
+ #if defined(GLFW_GLAPIENTRY_DEFINED)
+ #undef GLAPIENTRY
+ #undef GLFW_GLAPIENTRY_DEFINED
+ #endif
+ #include
+ #endif
+
+#endif /*GLFW_NATIVE_INCLUDE_NONE*/
/*************************************************************************
@@ -132,7 +169,8 @@ extern "C" {
* of the specified monitor, or `NULL` if an [error](@ref error_handling)
* occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -149,7 +187,8 @@ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
* `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -165,7 +204,8 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
* @return The `HWND` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @remark The `HDC` associated with the window can be queried with the
* [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
@@ -191,8 +231,8 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
* @return The `HGLRC` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_UNAVAILABLE and @ref GLFW_NO_WINDOW_CONTEXT.
*
* @remark The `HDC` associated with the window can be queried with the
* [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
@@ -218,7 +258,8 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
* @return The `CGDirectDisplayID` of the specified monitor, or
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -234,7 +275,8 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
* @return The `NSWindow` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -244,6 +286,23 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
* @ingroup native
*/
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
+
+/*! @brief Returns the `NSView` of the specified window.
+ *
+ * @return The `NSView` of the specified window, or `nil` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.4.
+ *
+ * @ingroup native
+ */
+GLFWAPI id glfwGetCocoaView(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
@@ -252,8 +311,8 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_UNAVAILABLE and @ref GLFW_NO_WINDOW_CONTEXT.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -271,7 +330,8 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
* @return The `Display` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -287,7 +347,8 @@ GLFWAPI Display* glfwGetX11Display(void);
* @return The `RRCrtc` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -303,7 +364,8 @@ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
* @return The `RROutput` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -319,7 +381,8 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
* @return The `Window` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -334,8 +397,8 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
*
* @param[in] string A UTF-8 encoded string.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
@@ -360,8 +423,8 @@ GLFWAPI void glfwSetX11SelectionString(const char* string);
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
- * GLFW_PLATFORM_ERROR.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_PLATFORM_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
@@ -387,8 +450,8 @@ GLFWAPI const char* glfwGetX11SelectionString(void);
* @return The `GLXContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -404,8 +467,8 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
* @return The `GLXWindow` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -423,7 +486,8 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
* @return The `struct wl_display*` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -439,7 +503,8 @@ GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
* @return The `struct wl_output*` of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -455,7 +520,8 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
* @return The main `struct wl_surface*` of the specified window, or `NULL` if
* an [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_UNAVAILABLE.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -475,6 +541,9 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
+ * @remark Because EGL is initialized on demand, this function will return
+ * `EGL_NO_DISPLAY` until the first context has been created via EGL.
+ *
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
@@ -489,8 +558,8 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_NO_WINDOW_CONTEXT.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -506,8 +575,8 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_NO_WINDOW_CONTEXT.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -532,8 +601,8 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_NO_WINDOW_CONTEXT.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -556,8 +625,8 @@ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_NO_WINDOW_CONTEXT.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
@@ -573,8 +642,8 @@ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height
* @return The `OSMesaContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
- * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
- * GLFW_NOT_INITIALIZED.
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_NO_WINDOW_CONTEXT.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
diff --git a/extern/src/imgui/imconfig.h b/extern/src/imgui/imconfig.h
index 876cf32f..d556cbaf 100644
--- a/extern/src/imgui/imconfig.h
+++ b/extern/src/imgui/imconfig.h
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------------
-// COMPILE-TIME OPTIONS FOR DEAR IMGUI
+// DEAR IMGUI COMPILE-TIME OPTIONS
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
@@ -9,7 +9,7 @@
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
-// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
+// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
@@ -26,21 +26,21 @@
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
-//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
+//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
+//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
-//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
+//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
-//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
+//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
@@ -50,21 +50,24 @@
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Include imgui_user.h at the end of imgui.h as a convenience
+// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
//#define IMGUI_INCLUDE_IMGUI_USER_H
+//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
-//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
+//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
-//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
+//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
+//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
@@ -75,6 +78,12 @@
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
+//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
+// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
+// Only works in combination with IMGUI_ENABLE_FREETYPE.
+// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
+//#define IMGUI_ENABLE_FREETYPE_LUNASVG
+
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
@@ -105,7 +114,7 @@
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
-//---- Debug Tools: Macro to break in Debugger
+//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
@@ -113,10 +122,10 @@
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
-//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
+//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
/*
namespace ImGui
{
- void MyFunction(const char* name, const MyMatrix44& v);
+ void MyFunction(const char* name, MyMatrix44* mtx);
}
*/
diff --git a/extern/src/imgui/imgui.cpp b/extern/src/imgui/imgui.cpp
index 400dcb22..d8ca1da2 100644
--- a/extern/src/imgui/imgui.cpp
+++ b/extern/src/imgui/imgui.cpp
@@ -1,30 +1,37 @@
-// dear imgui, v1.89.6
+// dear imgui, v1.90.7
// (main code and documentation)
// Help:
-// - Read FAQ at http://dearimgui.com/faq
-// - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
+// - See links below.
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
-// Read imgui.cpp for details, links and comments.
+// - Read top of imgui.cpp for more details, links and comments.
// Resources:
-// - FAQ http://dearimgui.com/faq
-// - Homepage & latest https://github.com/ocornut/imgui
-// - Releases & changelog https://github.com/ocornut/imgui/releases
-// - Gallery https://github.com/ocornut/imgui/issues/6478 (please post your screenshots/video there!)
-// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
-// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
-// - Issues & support https://github.com/ocornut/imgui/issues
+// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
+// - Homepage ................... https://github.com/ocornut/imgui
+// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
+// - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!)
+// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
+// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
+// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
+// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
+// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
+// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools
+// - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
+// - Issues & support ........... https://github.com/ocornut/imgui/issues
+// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
-// Getting Started?
-// - For first-time users having issues compiling/linking/running or issues loading fonts:
-// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
+// For first-time users having issues compiling/linking/running/loading fonts:
+// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
+// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
+// Copyright (c) 2014-2024 Omar Cornut
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// See LICENSE.txt for copyright and licensing details (standard MIT License).
// This library is free but needs your support to sustain development and maintenance.
-// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com".
-// Individuals: you can support continued development via donations. See docs/README or web page.
+// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
+// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding
+// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
@@ -70,8 +77,10 @@ CODE
// [SECTION] RENDER HELPERS
// [SECTION] INITIALIZATION, SHUTDOWN
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
+// [SECTION] ID STACK
// [SECTION] INPUTS
// [SECTION] ERROR CHECKING
+// [SECTION] ITEM SUBMISSION
// [SECTION] LAYOUT
// [SECTION] SCROLLING
// [SECTION] TOOLTIPS
@@ -85,7 +94,7 @@ CODE
// [SECTION] PLATFORM DEPENDENT HELPERS
// [SECTION] METRICS/DEBUGGER WINDOW
// [SECTION] DEBUG LOG WINDOW
-// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL)
+// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
*/
@@ -107,9 +116,10 @@ CODE
- Portable, minimize dependencies, run on target (consoles, phones, etc.).
- Efficient runtime and memory consumption.
- Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
+ Designed primarily for developers and content-creators, not the typical end-user!
+ Some of the current weaknesses (which we aim to address in the future) includes:
- - Doesn't look fancy, doesn't animate.
+ - Doesn't look fancy.
- Limited layout features, intricate layouts are typically crafted in code.
@@ -133,7 +143,7 @@ CODE
- CTRL+X, CTRL+C, CTRL+V: Use OS clipboard.
- CTRL+Z, CTRL+Y: Undo, Redo.
- ESCAPE: Revert text to its original value.
- - On OSX, controls are automatically adjusted to match standard OSX text editing shortcuts and behaviors.
+ - On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors.
- KEYBOARD CONTROLS
- Basic:
@@ -188,9 +198,11 @@ CODE
READ FIRST
----------
- Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
- - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or
- destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs.
+ - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
+ The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
+ data retention on your side, less state duplication, less state synchronization, fewer bugs.
- Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
+ Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
- The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
- Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
@@ -198,18 +210,38 @@ CODE
For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
- Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
- - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
- Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
If you get an assert, read the messages and comments around the assert.
- - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace.
- - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
- See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
- However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
- - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
+ - This codebase aims to be highly optimized:
+ - A typical idle frame should never call malloc/free.
+ - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
+ - We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
+ Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
+ - This codebase aims to be both highly opinionated and highly flexible:
+ - This code works because of the things it choose to solve or not solve.
+ - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
+ and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
+ This is to increase compatibility, increase maintainability and facilitate use from other languages.
+ - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
+ See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
+ We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
+ - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
+ (so don't use ImVector in your code or at our own risk!).
+ - Building: We don't use nor mandate a build system for the main library.
+ This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
+ This is also because providing a build system for the main library would be of little-value.
+ The build problems are almost never coming from the main library but from specific backends.
HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
----------------------------------------------
+ - Update submodule or copy/overwrite every file.
+ - About imconfig.h:
+ - You may modify your copy of imconfig.h, in this case don't overwrite it.
+ - or you may locally branch to modify imconfig.h and merge/rebase latest.
+ - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
+ specify a custom path for your imconfig.h file and instead not have to modify the default one.
+
- Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
- Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
- You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
@@ -218,11 +250,12 @@ CODE
from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
likely be a comment about it. Please report any issue to the GitHub page!
- To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
- - Try to keep your copy of Dear ImGui reasonably up to date.
+ - Try to keep your copy of Dear ImGui reasonably up to date!
GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
---------------------------------------------------------------
+ - See https://github.com/ocornut/imgui/wiki/Getting-Started.
- Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
- In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
- Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
@@ -330,7 +363,7 @@ CODE
To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
- Please read the FAQ and example applications for details about this!
+ Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
@@ -397,6 +430,70 @@ CODE
When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
+ - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
+ - old: CaptureKeyboardFromApp(bool)
+ - new: SetNextFrameWantCaptureKeyboard(bool)
+ - old: CaptureMouseFromApp(bool)
+ - new: SetNextFrameWantCaptureMouse(bool)
+ - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs).
+ - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
+ - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
+ - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
+ - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
+ - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
+ - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
+ - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
+ - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
+ - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
+ for various reasons those changes makes sense. They are being made because making some of those API public.
+ only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL.
+ - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys.
+ - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
+ - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456)
+ - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
+ - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
+ - old: TreeNode("##Hidden"); SameLine(); Text("Hello"); // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise).
+ - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
+ with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
+ You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent.
+ (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
+ - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417)
+ - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921)
+ - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
+ - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
+ - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
+ - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
+ - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls.
+ - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80.
+ - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
+ - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete).
+ those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features.
+ - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior "bool-to-flags" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work.
+ - old: BeginChild("Name", size, true)
+ - new: BeginChild("Name", size, ImGuiChildFlags_Border)
+ - old: BeginChild("Name", size, false)
+ - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
+ - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow.
+ - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
+ - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
+ - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user).
+ - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIDStackToolWindow() ("ID Stack Tool"), as earlier name was misleading. Kept inline redirection function. (#4631)
+ - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete).
+ - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
+ - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
+ - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
+ - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
+ - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
+ - GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
+ - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
+ - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details
+ - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
+ - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878)
+ - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15).
+ - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
+ - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior.
+ - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610)
+ - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.
- 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3.
- 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
- ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
@@ -475,7 +572,7 @@ CODE
- Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
read https://github.com/ocornut/imgui/issues/4921 for details.
- - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.
+ - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.
- IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX)
- IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
- Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes).
@@ -799,11 +896,12 @@ CODE
Q: Where is the documentation?
A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
- - Run the examples/ and explore them.
+ - Run the examples/ applications and explore them.
+ - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
- See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
- The demo covers most features of Dear ImGui, so you can read the code and see its output.
- See documentation and comments at the top of imgui.cpp + effectively imgui.h.
- - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
+ - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
- The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
- The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
@@ -819,14 +917,14 @@ CODE
================
Q: How to get started?
- A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
+ A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
>> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
- Q. How can I enable keyboard controls?
- Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
+ Q. How can I enable keyboard or gamepad controls?
+ Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
@@ -841,7 +939,7 @@ CODE
- How can I have multiple widgets with the same label?
- How can I have multiple windows with the same label?
Q: How can I display an image? What is ImTextureID, how does it work?
- Q: How can I use my own math types instead of ImVec2/ImVec4?
+ Q: How can I use my own math types instead of ImVec2?
Q: How can I interact with standard C++ types (such as std::string and std::vector)?
Q: How can I display custom shapes? (using low-level ImDrawList API)
>> See https://www.dearimgui.com/faq
@@ -854,7 +952,7 @@ CODE
Q: How can I easily use icons in my application?
Q: How can I load multiple fonts?
Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
- >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
+ >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
Q&A: Concerns
=============
@@ -869,12 +967,12 @@ CODE
==============
Q: How can I help?
- A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui!
+ A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
- This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people working on this project.
- - Individuals: you can support continued development via PayPal donations. See README.
- - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt
- and see how you want to help and can help!
+ This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
+ >>> See https://github.com/ocornut/imgui/wiki/Funding
+ - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
+ - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help!
- Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
@@ -900,11 +998,7 @@ CODE
// System includes
#include // vsnprintf, sscanf, printf
-#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
-#include // intptr_t
-#else
#include // intptr_t
-#endif
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
@@ -927,7 +1021,8 @@ CODE
#else
#include
#endif
-#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
+// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
#endif
@@ -967,6 +1062,7 @@ CODE
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__)
// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
@@ -983,27 +1079,29 @@ CODE
// Debug options
#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
-#define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.
static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
+static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut.
+
// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
+// Tooltip offset
+static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale
+
//-------------------------------------------------------------------------
// [SECTION] FORWARD DECLARATIONS
//-------------------------------------------------------------------------
static void SetCurrentWindow(ImGuiWindow* window);
-static void FindHoveredWindow();
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
-static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list);
static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window);
// Settings
@@ -1020,6 +1118,9 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport
namespace ImGui
{
+// Item
+static void ItemHandleShortcut(ImGuiID id);
+
// Navigation
static void NavUpdate();
static void NavUpdateWindowing();
@@ -1039,7 +1140,6 @@ static ImVec2 NavCalcPreferredRefPos();
static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
static void NavRestoreLayer(ImGuiNavLayer layer);
-static void NavRestoreHighlightAfterMove();
static int FindWindowFocusIndex(ImGuiWindow* window);
// Error Checking and Debug Tools
@@ -1047,6 +1147,7 @@ static void ErrorCheckNewFrameSanityChecks();
static void ErrorCheckEndFrameSanityChecks();
static void UpdateDebugToolItemPicker();
static void UpdateDebugToolStackQueries();
+static void UpdateDebugToolFlashStyleColor();
// Inputs
static void UpdateKeyboardInputs();
@@ -1056,14 +1157,16 @@ static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
// Misc
static void UpdateSettings();
-static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
+static int UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
static void RenderWindowOuterBorders(ImGuiWindow* window);
static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
static void RenderDimmedBackgrounds();
+static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
// Viewports
+const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
static void UpdateViewportsNewFrame();
}
@@ -1116,49 +1219,59 @@ static void* GImAllocatorUserData = NULL;
ImGuiStyle::ImGuiStyle()
{
- Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
- DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
- WindowPadding = ImVec2(8,8); // Padding within a window
- WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
- WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
- WindowMinSize = ImVec2(32,32); // Minimum window size
- WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
- WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
- ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
- ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
- PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
- PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
- FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
- FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
- FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
- ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
- ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
- CellPadding = ImVec2(4,2); // Padding within a table cell
- TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
- IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
- ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
- ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
- ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
- GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
- GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
- LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
- TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
- TabBorderSize = 0.0f; // Thickness of border around tabs.
- TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
- ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
- ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
- SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
- SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText()
- SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
- SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
- DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
- DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
- MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
- AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
- AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
- AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
- CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
- CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
+ Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
+ DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
+ WindowPadding = ImVec2(8,8); // Padding within a window
+ WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
+ WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
+ WindowMinSize = ImVec2(32,32); // Minimum window size
+ WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
+ WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
+ ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
+ ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
+ PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
+ PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
+ FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
+ FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
+ FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
+ ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
+ ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
+ CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
+ TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
+ IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
+ ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
+ ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
+ ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
+ GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
+ GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
+ LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
+ TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
+ TabBorderSize = 0.0f; // Thickness of border around tabs.
+ TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
+ TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
+ TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
+ TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
+ ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
+ ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
+ SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
+ SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText()
+ SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
+ SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
+ DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
+ DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
+ MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
+ AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
+ AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
+ AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
+ CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
+ CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
+
+ // Behaviors
+ HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
+ HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
+ HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
+ HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
+ HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
// Default theme
ImGui::StyleColorsDark(this);
@@ -1168,30 +1281,30 @@ ImGuiStyle::ImGuiStyle()
// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
void ImGuiStyle::ScaleAllSizes(float scale_factor)
{
- WindowPadding = ImFloor(WindowPadding * scale_factor);
- WindowRounding = ImFloor(WindowRounding * scale_factor);
- WindowMinSize = ImFloor(WindowMinSize * scale_factor);
- ChildRounding = ImFloor(ChildRounding * scale_factor);
- PopupRounding = ImFloor(PopupRounding * scale_factor);
- FramePadding = ImFloor(FramePadding * scale_factor);
- FrameRounding = ImFloor(FrameRounding * scale_factor);
- ItemSpacing = ImFloor(ItemSpacing * scale_factor);
- ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
- CellPadding = ImFloor(CellPadding * scale_factor);
- TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
- IndentSpacing = ImFloor(IndentSpacing * scale_factor);
- ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
- ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
- ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
- GrabMinSize = ImFloor(GrabMinSize * scale_factor);
- GrabRounding = ImFloor(GrabRounding * scale_factor);
- LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
- TabRounding = ImFloor(TabRounding * scale_factor);
- TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
- SeparatorTextPadding = ImFloor(SeparatorTextPadding * scale_factor);
- DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
- DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
- MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
+ WindowPadding = ImTrunc(WindowPadding * scale_factor);
+ WindowRounding = ImTrunc(WindowRounding * scale_factor);
+ WindowMinSize = ImTrunc(WindowMinSize * scale_factor);
+ ChildRounding = ImTrunc(ChildRounding * scale_factor);
+ PopupRounding = ImTrunc(PopupRounding * scale_factor);
+ FramePadding = ImTrunc(FramePadding * scale_factor);
+ FrameRounding = ImTrunc(FrameRounding * scale_factor);
+ ItemSpacing = ImTrunc(ItemSpacing * scale_factor);
+ ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor);
+ CellPadding = ImTrunc(CellPadding * scale_factor);
+ TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor);
+ IndentSpacing = ImTrunc(IndentSpacing * scale_factor);
+ ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);
+ ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);
+ ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);
+ GrabMinSize = ImTrunc(GrabMinSize * scale_factor);
+ GrabRounding = ImTrunc(GrabRounding * scale_factor);
+ LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);
+ TabRounding = ImTrunc(TabRounding * scale_factor);
+ TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
+ SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);
+ DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor);
+ DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor);
+ MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor);
}
ImGuiIO::ImGuiIO()
@@ -1208,16 +1321,10 @@ ImGuiIO::ImGuiIO()
IniSavingRate = 5.0f;
IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
LogFilename = "imgui_log.txt";
- MouseDoubleClickTime = 0.30f;
- MouseDoubleClickMaxDist = 6.0f;
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
for (int i = 0; i < ImGuiKey_COUNT; i++)
KeyMap[i] = -1;
#endif
- KeyRepeatDelay = 0.275f;
- KeyRepeatRate = 0.050f;
- HoverDelayNormal = 0.30f;
- HoverDelayShort = 0.10f;
UserData = NULL;
Fonts = NULL;
@@ -1226,6 +1333,12 @@ ImGuiIO::ImGuiIO()
FontAllowUserScaling = false;
DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
+ MouseDoubleClickTime = 0.30f;
+ MouseDoubleClickMaxDist = 6.0f;
+ MouseDragThreshold = 6.0f;
+ KeyRepeatDelay = 0.275f;
+ KeyRepeatRate = 0.050f;
+
// Miscellaneous options
MouseDrawCursor = false;
#ifdef __APPLE__
@@ -1247,12 +1360,12 @@ ImGuiIO::ImGuiIO()
// Note: Initialize() will setup default clipboard/ime handlers.
BackendPlatformName = BackendRendererName = NULL;
BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
+ PlatformLocaleDecimalPoint = '.';
// Input (NB: we already have memset zero the entire structure!)
MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
MouseSource = ImGuiMouseSource_Mouse;
- MouseDragThreshold = 6.0f;
for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
AppAcceptingEvents = true;
@@ -1327,13 +1440,15 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
}
}
-// FIXME: Perhaps we could clear queued events as well?
-void ImGuiIO::ClearInputCharacters()
+// Clear all incoming events.
+void ImGuiIO::ClearEventsQueue()
{
- InputQueueCharacters.resize(0);
+ IM_ASSERT(Ctx != NULL);
+ ImGuiContext& g = *Ctx;
+ g.InputEventsQueue.clear();
}
-// FIXME: Perhaps we could clear queued events as well?
+// Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
void ImGuiIO::ClearInputKeys()
{
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
@@ -1354,8 +1469,18 @@ void ImGuiIO::ClearInputKeys()
MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
}
MouseWheel = MouseWheelH = 0.0f;
+ InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
}
+// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
+// Current frame character buffer is now also cleared by ClearInputKeys().
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+void ImGuiIO::ClearInputCharacters()
+{
+ InputQueueCharacters.resize(0);
+}
+#endif
+
static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
{
ImGuiContext& g = *ctx;
@@ -1378,7 +1503,7 @@ static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventT
// - bool down: Is the key down? use false to signify a key release.
// - float analog_value: 0.0f..1.0f
// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
-// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULLFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
+// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
{
//if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }
@@ -1386,9 +1511,19 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
if (key == ImGuiKey_None || !AppAcceptingEvents)
return;
ImGuiContext& g = *Ctx;
- IM_ASSERT(ImGui::IsNamedKeyOrModKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
+ IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
- IM_ASSERT(key != ImGuiMod_Shortcut); // We could easily support the translation here but it seems saner to not accept it (TestEngine perform a translation itself)
+
+ // MacOS: swap Cmd(Super) and Ctrl
+ if (g.IO.ConfigMacOSXBehaviors)
+ {
+ if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; }
+ else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; }
+ else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
+ else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
+ else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; }
+ else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
+ }
// Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
@@ -1467,7 +1602,7 @@ void ImGuiIO::AddMousePosEvent(float x, float y)
return;
// Apply same flooring as UpdateMouseInputs()
- ImVec2 pos((x > -FLT_MAX) ? ImFloorSigned(x) : x, (y > -FLT_MAX) ? ImFloorSigned(y) : y);
+ ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
// Filter duplicate
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
@@ -1481,7 +1616,7 @@ void ImGuiIO::AddMousePosEvent(float x, float y)
e.EventId = g.InputEventsNextEventId++;
e.MousePos.PosX = pos.x;
e.MousePos.PosY = pos.y;
- e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
+ e.MousePos.MouseSource = g.InputEventsNextMouseSource;
g.InputEventsQueue.push_back(e);
}
@@ -1493,19 +1628,43 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
if (!AppAcceptingEvents)
return;
+ // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
+ if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
+ {
+ // Order of both statements matterns: this event will still release mouse button 1
+ mouse_button = 1;
+ if (!down)
+ MouseCtrlLeftAsRightClick = false;
+ }
+
// Filter duplicate
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
if (latest_button_down == down)
return;
+ // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
+ // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
+ // - At this point we want from !down to down, so this is handling the initial press.
+ if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
+ {
+ const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
+ if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
+ {
+ IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
+ MouseCtrlLeftAsRightClick = true;
+ AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
+ return;
+ }
+ }
+
ImGuiInputEvent e;
e.Type = ImGuiInputEventType_MouseButton;
e.Source = ImGuiInputSource_Mouse;
e.EventId = g.InputEventsNextEventId++;
e.MouseButton.Button = mouse_button;
e.MouseButton.Down = down;
- e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
+ e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
g.InputEventsQueue.push_back(e);
}
@@ -1808,13 +1967,15 @@ const char* ImStrSkipBlank(const char* str)
// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
#ifdef IMGUI_USE_STB_SPRINTF
+#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
#define STB_SPRINTF_IMPLEMENTATION
+#endif
#ifdef IMGUI_STB_SPRINTF_FILENAME
#include IMGUI_STB_SPRINTF_FILENAME
#else
#include "stb_sprintf.h"
#endif
-#endif
+#endif // #ifdef IMGUI_USE_STB_SPRINTF
#if defined(_MSC_VER) && !defined(vsnprintf)
#define vsnprintf _vsnprintf
@@ -1856,21 +2017,9 @@ int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
{
- ImGuiContext& g = *GImGui;
va_list args;
va_start(args, fmt);
- if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
- {
- const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
- *out_buf = buf;
- if (out_buf_end) { *out_buf_end = buf + strlen(buf); }
- }
- else
- {
- int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
- *out_buf = g.TempBuffer.Data;
- if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
- }
+ ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
va_end(args);
}
@@ -1880,9 +2029,23 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end,
if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
{
const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
+ if (buf == NULL)
+ buf = "(null)";
*out_buf = buf;
if (out_buf_end) { *out_buf_end = buf + strlen(buf); }
}
+ else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
+ {
+ int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
+ const char* buf = va_arg(args, const char*);
+ if (buf == NULL)
+ {
+ buf = "(null)";
+ buf_len = ImMin(buf_len, 6);
+ }
+ *out_buf = buf;
+ *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
+ }
else
{
int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
@@ -1975,11 +2138,18 @@ ImFileHandle ImFileOpen(const char* filename, const char* mode)
// Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
- ImVector buf;
- buf.resize(filename_wsize + mode_wsize);
- ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
- ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
- return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
+
+ // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
+ // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
+ wchar_t local_temp_stack[FILENAME_MAX];
+ ImVector local_temp_heap;
+ if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
+ local_temp_heap.resize(filename_wsize + mode_wsize);
+ wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
+ wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
+ ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
+ ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
+ return ::_wfopen(filename_wbuf, mode_wbuf);
#else
return fopen(filename, mode);
#endif
@@ -2211,6 +2381,32 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e
}
return bytes_count;
}
+
+const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)
+{
+ while (in_text_curr > in_text_start)
+ {
+ in_text_curr--;
+ if ((*in_text_curr & 0xC0) != 0x80)
+ return in_text_curr;
+ }
+ return in_text_start;
+}
+
+int ImTextCountLines(const char* in_text, const char* in_text_end)
+{
+ if (in_text_end == NULL)
+ in_text_end = in_text + strlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
+ int count = 0;
+ while (in_text < in_text_end)
+ {
+ const char* line_end = (const char*)memchr(in_text, '\n', in_text_end - in_text);
+ in_text = line_end ? line_end + 1 : in_text_end;
+ count++;
+ }
+ return count;
+}
+
IM_MSVC_RUNTIME_CHECKS_RESTORE
//-----------------------------------------------------------------------------
@@ -2406,11 +2602,9 @@ void ImGuiStorage::SetInt(ImGuiID key, int val)
{
ImGuiStoragePair* it = LowerBound(Data, key);
if (it == Data.end() || it->key != key)
- {
Data.insert(it, ImGuiStoragePair(key, val));
- return;
- }
- it->val_i = val;
+ else
+ it->val_i = val;
}
void ImGuiStorage::SetBool(ImGuiID key, bool val)
@@ -2422,22 +2616,18 @@ void ImGuiStorage::SetFloat(ImGuiID key, float val)
{
ImGuiStoragePair* it = LowerBound(Data, key);
if (it == Data.end() || it->key != key)
- {
Data.insert(it, ImGuiStoragePair(key, val));
- return;
- }
- it->val_f = val;
+ else
+ it->val_f = val;
}
void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
{
ImGuiStoragePair* it = LowerBound(Data, key);
if (it == Data.end() || it->key != key)
- {
Data.insert(it, ImGuiStoragePair(key, val));
- return;
- }
- it->val_p = val;
+ else
+ it->val_p = val;
}
void ImGuiStorage::SetAllInt(int v)
@@ -2497,16 +2687,15 @@ void ImGuiTextFilter::Build()
input_range.split(',', &Filters);
CountGrep = 0;
- for (int i = 0; i != Filters.Size; i++)
+ for (ImGuiTextRange& f : Filters)
{
- ImGuiTextRange& f = Filters[i];
while (f.b < f.e && ImCharIsBlankA(f.b[0]))
f.b++;
while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
f.e--;
if (f.empty())
continue;
- if (Filters[i].b[0] != '-')
+ if (f.b[0] != '-')
CountGrep += 1;
}
}
@@ -2519,9 +2708,8 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
if (text == NULL)
text = "";
- for (int i = 0; i != Filters.Size; i++)
+ for (const ImGuiTextRange& f : Filters)
{
- const ImGuiTextRange& f = Filters[i];
if (f.empty())
continue;
if (f.b[0] == '-')
@@ -2630,8 +2818,6 @@ void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
//-----------------------------------------------------------------------------
// [SECTION] ImGuiListClipper
-// This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
-// the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
//-----------------------------------------------------------------------------
// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
@@ -2642,54 +2828,6 @@ static bool GetSkipItemForListClipping()
return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
}
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-// Legacy helper to calculate coarse clipping of large list of evenly sized items.
-// This legacy API is not ideal because it assumes we will return a single contiguous rectangle.
-// Prefer using ImGuiListClipper which can returns non-contiguous ranges.
-void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
-{
- ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
- if (g.LogEnabled)
- {
- // If logging is active, do not perform any clipping
- *out_items_display_start = 0;
- *out_items_display_end = items_count;
- return;
- }
- if (GetSkipItemForListClipping())
- {
- *out_items_display_start = *out_items_display_end = 0;
- return;
- }
-
- // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect
- // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly.
- ImRect rect = window->ClipRect;
- if (g.NavMoveScoringItems)
- rect.Add(g.NavScoringNoClipRect);
- if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
- rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel
-
- const ImVec2 pos = window->DC.CursorPos;
- int start = (int)((rect.Min.y - pos.y) / items_height);
- int end = (int)((rect.Max.y - pos.y) / items_height);
-
- // When performing a navigation request, ensure we have one item extra in the direction we are moving to
- // FIXME: Verify this works with tabbing
- const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
- if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up)
- start--;
- if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down)
- end++;
-
- start = ImClamp(start, 0, items_count);
- end = ImClamp(end + 1, start, items_count);
- *out_items_display_start = start;
- *out_items_display_end = end;
-}
-#endif
-
static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0)
{
if (ranges.Size - offset <= 1)
@@ -2751,9 +2889,6 @@ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int it
ImGuiListClipper::ImGuiListClipper()
{
memset(this, 0, sizeof(*this));
- Ctx = ImGui::GetCurrentContext();
- IM_ASSERT(Ctx != NULL);
- ItemsCount = -1;
}
ImGuiListClipper::~ImGuiListClipper()
@@ -2763,6 +2898,9 @@ ImGuiListClipper::~ImGuiListClipper()
void ImGuiListClipper::Begin(int items_count, float items_height)
{
+ if (Ctx == NULL)
+ Ctx = ImGui::GetCurrentContext();
+
ImGuiContext& g = *Ctx;
ImGuiWindow* window = g.CurrentWindow;
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
@@ -2788,10 +2926,10 @@ void ImGuiListClipper::Begin(int items_count, float items_height)
void ImGuiListClipper::End()
{
- ImGuiContext& g = *Ctx;
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
{
// In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
+ ImGuiContext& g = *Ctx;
IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
@@ -2809,7 +2947,7 @@ void ImGuiListClipper::End()
ItemsCount = -1;
}
-void ImGuiListClipper::IncludeRangeByIndices(int item_begin, int item_end)
+void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
{
ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
@@ -2892,7 +3030,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
if (is_nav_request)
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
- if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1)
+ if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
// Add focused/active item
@@ -2910,26 +3048,28 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
// - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.
// - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
// which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
- for (int i = 0; i < data->Ranges.Size; i++)
- if (data->Ranges[i].PosToIndexConvert)
+ for (ImGuiListClipperRange& range : data->Ranges)
+ if (range.PosToIndexConvert)
{
- int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
- int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
- data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
- data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, clipper->ItemsCount);
- data->Ranges[i].PosToIndexConvert = false;
+ int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
+ int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
+ range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
+ range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
+ range.PosToIndexConvert = false;
}
ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
}
// Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
- if (data->StepNo < data->Ranges.Size)
+ while (data->StepNo < data->Ranges.Size)
{
clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
if (clipper->DisplayStart > already_submitted) //-V1051
ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart);
data->StepNo++;
+ if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)
+ continue;
return true;
}
@@ -2996,13 +3136,14 @@ const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
return style.Colors[idx];
}
-ImU32 ImGui::GetColorU32(ImU32 col)
+ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
{
ImGuiStyle& style = GImGui->Style;
- if (style.Alpha >= 1.0f)
+ alpha_mul *= style.Alpha;
+ if (alpha_mul >= 1.0f)
return col;
ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
- a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
+ a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
}
@@ -3014,7 +3155,8 @@ void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
backup.Col = idx;
backup.BackupValue = g.Style.Colors[idx];
g.ColorStack.push_back(backup);
- g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
+ if (g.DebugFlashStyleColorIdx != idx)
+ g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
}
void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
@@ -3024,7 +3166,8 @@ void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
backup.Col = idx;
backup.BackupValue = g.Style.Colors[idx];
g.ColorStack.push_back(backup);
- g.Style.Colors[idx] = col;
+ if (g.DebugFlashStyleColorIdx != idx)
+ g.Style.Colors[idx] = col;
}
void ImGui::PopStyleColor(int count)
@@ -3032,7 +3175,7 @@ void ImGui::PopStyleColor(int count)
ImGuiContext& g = *GImGui;
if (g.ColorStack.Size < count)
{
- IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times: stack underflow.");
+ IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!");
count = g.ColorStack.Size;
}
while (count > 0)
@@ -3046,34 +3189,38 @@ void ImGui::PopStyleColor(int count)
static const ImGuiDataVarInfo GStyleVarInfo[] =
{
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
- { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextBorderSize) },// ImGuiStyleVar_SeparatorTextBorderSize
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
- { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
+ { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
+ { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
};
const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
@@ -3094,7 +3241,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
*pvar = val;
return;
}
- IM_ASSERT_USER_ERROR(0, "Called PushStyleVar() variant with wrong type!");
+ IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
}
void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
@@ -3108,7 +3255,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
*pvar = val;
return;
}
- IM_ASSERT_USER_ERROR(0, "Called PushStyleVar() variant with wrong type!");
+ IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
}
void ImGui::PopStyleVar(int count)
@@ -3116,7 +3263,7 @@ void ImGui::PopStyleVar(int count)
ImGuiContext& g = *GImGui;
if (g.StyleVarStack.Size < count)
{
- IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times: stack underflow.");
+ IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!");
count = g.StyleVarStack.Size;
}
while (count > 0)
@@ -3351,7 +3498,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
// Render text, render ellipsis
RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
- ImVec2 ellipsis_pos = ImFloor(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
+ ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
@@ -3405,22 +3552,22 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl
float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
ImRect display_rect = bb;
display_rect.ClipWith(window->ClipRect);
- if (flags & ImGuiNavHighlightFlags_TypeDefault)
+ const float thickness = 2.0f;
+ if (flags & ImGuiNavHighlightFlags_Compact)
{
- const float THICKNESS = 2.0f;
- const float DISTANCE = 3.0f + THICKNESS * 0.5f;
- display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
+ window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
+ }
+ else
+ {
+ const float distance = 3.0f + thickness * 0.5f;
+ display_rect.Expand(ImVec2(distance, distance));
bool fully_visible = window->ClipRect.Contains(display_rect);
if (!fully_visible)
window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
- window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, 0, THICKNESS);
+ window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
if (!fully_visible)
window->DrawList->PopClipRect();
}
- if (flags & ImGuiNavHighlightFlags_TypeThin)
- {
- window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f);
- }
}
void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
@@ -3428,13 +3575,12 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso
ImGuiContext& g = *GImGui;
IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT);
ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;
- for (int n = 0; n < g.Viewports.Size; n++)
+ for (ImGuiViewportP* viewport : g.Viewports)
{
// We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
ImVec2 offset, size, uv[4];
if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
continue;
- ImGuiViewportP* viewport = g.Viewports[n];
const ImVec2 pos = base_pos - offset;
const float scale = base_scale;
if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
@@ -3510,6 +3656,7 @@ void ImGui::DestroyContext(ImGuiContext* ctx)
// IMPORTANT: ###xxx suffixes must be same in ALL languages
static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
{
+ { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
{ ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" },
{ ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" },
{ ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" },
@@ -3549,9 +3696,18 @@ void ImGui::Initialize()
// Create default viewport
ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
+ viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
g.Viewports.push_back(viewport);
g.TempBuffer.resize(1024 * 3 + 1, 0);
+ // Build KeysMayBeCharInput[] lookup table (1 bool per named key)
+ for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
+ if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
+ || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
+ || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
+ || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
+ g.KeysMayBeCharInput.SetBit(key);
+
#ifdef IMGUI_HAS_DOCK
#endif
@@ -3561,8 +3717,11 @@ void ImGui::Initialize()
// This function is merely here to free heap allocations.
void ImGui::Shutdown()
{
- // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
ImGuiContext& g = *GImGui;
+ IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
+ IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
+
+ // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
if (g.IO.Fonts && g.FontAtlasOwnedByContext)
{
g.IO.Fonts->Locked = false;
@@ -3600,6 +3759,7 @@ void ImGui::Shutdown()
g.FontStack.clear();
g.OpenPopupStack.clear();
g.BeginPopupStack.clear();
+ g.NavTreeNodeStack.clear();
g.Viewports.clear_delete();
@@ -3651,9 +3811,9 @@ void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
{
ImGuiContext& g = *ctx;
IM_ASSERT(hook_id != 0);
- for (int n = 0; n < g.Hooks.Size; n++)
- if (g.Hooks[n].HookId == hook_id)
- g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_;
+ for (ImGuiContextHook& hook : g.Hooks)
+ if (hook.HookId == hook_id)
+ hook.Type = ImGuiContextHookType_PendingRemoval_;
}
// Call context hooks (used by e.g. test engine)
@@ -3661,9 +3821,9 @@ void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
{
ImGuiContext& g = *ctx;
- for (int n = 0; n < g.Hooks.Size; n++)
- if (g.Hooks[n].Type == hook_type)
- g.Hooks[n].Callback(&g, &g.Hooks[n]);
+ for (ImGuiContextHook& hook : g.Hooks)
+ if (hook.Type == hook_type)
+ hook.Callback(&g, &hook);
}
@@ -3704,50 +3864,12 @@ ImGuiWindow::~ImGuiWindow()
ColumnsStorage.clear_destruct();
}
-ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
-{
- ImGuiID seed = IDStack.back();
- ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
- ImGuiContext& g = *Ctx;
- if (g.DebugHookIdInfo == id)
- ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
- return id;
-}
-
-ImGuiID ImGuiWindow::GetID(const void* ptr)
-{
- ImGuiID seed = IDStack.back();
- ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
- ImGuiContext& g = *Ctx;
- if (g.DebugHookIdInfo == id)
- ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
- return id;
-}
-
-ImGuiID ImGuiWindow::GetID(int n)
-{
- ImGuiID seed = IDStack.back();
- ImGuiID id = ImHashData(&n, sizeof(n), seed);
- ImGuiContext& g = *Ctx;
- if (g.DebugHookIdInfo == id)
- ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
- return id;
-}
-
-// This is only used in rare/specific situations to manufacture an ID out of nowhere.
-ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
-{
- ImGuiID seed = IDStack.back();
- ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
- ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
- return id;
-}
-
static void SetCurrentWindow(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
g.CurrentWindow = window;
g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
+ g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking
if (window)
{
g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
@@ -3832,6 +3954,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
g.ActiveIdNoClearOnFocusLoss = false;
g.ActiveIdWindow = window;
g.ActiveIdHasBeenEditedThisFrame = false;
+ g.ActiveIdFromShortcut = false;
if (id)
{
g.ActiveIdIsAlive = id;
@@ -3868,22 +3991,13 @@ ImGuiID ImGui::GetHoveredID()
return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
}
-// This is called by ItemAdd().
-// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
-void ImGui::KeepAliveID(ImGuiID id)
-{
- ImGuiContext& g = *GImGui;
- if (g.ActiveId == id)
- g.ActiveIdIsAlive = id;
- if (g.ActiveIdPreviousFrame == id)
- g.ActiveIdPreviousFrameIsAlive = true;
-}
-
void ImGui::MarkItemEdited(ImGuiID id)
{
// This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
// ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.
ImGuiContext& g = *GImGui;
+ if (g.LockMarkEdited > 0)
+ return;
if (g.ActiveId == id || g.ActiveId == 0)
{
g.ActiveIdHasBeenEditedThisFrame = true;
@@ -3923,6 +4037,24 @@ bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flag
return true;
}
+static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ if (flags & ImGuiHoveredFlags_DelayNormal)
+ return g.Style.HoverDelayNormal;
+ if (flags & ImGuiHoveredFlags_DelayShort)
+ return g.Style.HoverDelayShort;
+ return 0.0f;
+}
+
+static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
+{
+ // Allow instance flags to override shared flags
+ if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
+ shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
+ return user_flags | shared_flags;
+}
+
// This is roughly matching the behavior of internal-facing ItemHoverable()
// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
@@ -3930,12 +4062,17 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
+ IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!");
+
if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
{
if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
return false;
if (!IsItemFocused())
return false;
+
+ if (flags & ImGuiHoveredFlags_ForTooltip)
+ flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
}
else
{
@@ -3943,6 +4080,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
return false;
+
+ if (flags & ImGuiHoveredFlags_ForTooltip)
+ flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
+
IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function
// Done with rectangle culling so we can perform heavier checks now
@@ -3952,12 +4093,13 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
// 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)
+ if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
return false;
// Test if another item is active (e.g. being dragged)
+ const ImGuiID id = g.LastItemData.ID;
if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
- if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
+ if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
return false;
// Test if interactions on this window are blocked by an active popup or modal.
@@ -3971,48 +4113,61 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
// Special handling for calling after Begin() which represent the title bar or tab.
// When the window is skipped/collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
- if (g.LastItemData.ID == window->MoveId && window->WriteAccessed)
+ if (id == window->MoveId && window->WriteAccessed)
return false;
+
+ // Test if using AllowOverlap and overlapped
+ if ((g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
+ if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
+ if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
+ return false;
}
// Handle hover delay
// (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
- float delay;
- if (flags & ImGuiHoveredFlags_DelayNormal)
- delay = g.IO.HoverDelayNormal;
- else if (flags & ImGuiHoveredFlags_DelayShort)
- delay = g.IO.HoverDelayShort;
- else
- delay = 0.0f;
- if (delay > 0.0f)
+ const float delay = CalcDelayFromHoveredFlags(flags);
+ if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
{
ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect);
- if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverDelayIdPreviousFrame != hover_delay_id))
- g.HoverDelayTimer = 0.0f;
- g.HoverDelayId = hover_delay_id;
- return g.HoverDelayTimer >= delay;
+ if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
+ g.HoverItemDelayTimer = 0.0f;
+ g.HoverItemDelayId = hover_delay_id;
+
+ // When changing hovered item we requires a bit of stationary delay before activating hover timer,
+ // but once unlocked on a given item we also moving.
+ //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }
+ if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
+ return false;
+
+ if (g.HoverItemDelayTimer < delay)
+ return false;
}
return true;
}
// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
-bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
+// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
+// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
+// If you used this in your legacy/custom widgets code:
+// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.InFlags'.
+// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
+bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
{
ImGuiContext& g = *GImGui;
- if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
- return false;
-
ImGuiWindow* window = g.CurrentWindow;
if (g.HoveredWindow != window)
return false;
- if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
- return false;
if (!IsMouseHoveringRect(bb.Min, bb.Max))
return false;
+ if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
+ return false;
+ if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
+ if (!g.ActiveIdFromShortcut)
+ return false;
+
// Done with rectangle culling so we can perform heavier checks now.
- ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
{
g.HoveredIdDisabled = true;
@@ -4022,29 +4177,51 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
// We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
// hover test in widgets code. We could also decide to split this function is two.
if (id != 0)
+ {
+ // Drag source doesn't report as hovered
+ if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
+ return false;
+
SetHoveredID(id);
+ // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
+ // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
+ if (item_flags & ImGuiItemFlags_AllowOverlap)
+ {
+ g.HoveredIdAllowOverlap = true;
+ if (g.HoveredIdPreviousFrame != id)
+ return false;
+ }
+
+ // Display shortcut (only works with mouse)
+ if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut))
+ if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
+ SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
+ }
+
// When disabled we'll return false but still set HoveredId
if (item_flags & ImGuiItemFlags_Disabled)
{
// Release active id if turning disabled
- if (g.ActiveId == id)
+ if (g.ActiveId == id && id != 0)
ClearActiveID();
g.HoveredIdDisabled = true;
return false;
}
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (id != 0)
{
// [DEBUG] Item Picker tool!
- // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
- // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
- // items if we performed the test in ItemAdd(), but that would incur a small runtime cost.
+ // We perform the check here because reaching is path is rare (1~ time a frame),
+ // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
+ // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
if (g.DebugItemPickerBreakId == id)
IM_DEBUG_BREAK();
}
+#endif
if (g.NavDisableMouseHover)
return false;
@@ -4053,12 +4230,13 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
}
// FIXME: This is inlined/duplicated in ItemAdd()
+// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (!bb.Overlaps(window->ClipRect))
- if (id == 0 || (id != g.ActiveId && id != g.NavId))
+ if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
if (!g.LogEnabled)
return true;
return false;
@@ -4102,20 +4280,51 @@ float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
// IM_ALLOC() == ImGui::MemAlloc()
void* ImGui::MemAlloc(size_t size)
{
+ void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (ImGuiContext* ctx = GImGui)
- ctx->IO.MetricsActiveAllocations++;
- return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
+ DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
+#endif
+ return ptr;
}
// IM_FREE() == ImGui::MemFree()
void ImGui::MemFree(void* ptr)
{
- if (ptr)
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+ if (ptr != NULL)
if (ImGuiContext* ctx = GImGui)
- ctx->IO.MetricsActiveAllocations--;
+ DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
+#endif
return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
}
+// We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames"
+void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
+{
+ ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
+ IM_UNUSED(ptr);
+ if (entry->FrameCount != frame_count)
+ {
+ info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
+ entry = &info->LastEntriesBuf[info->LastEntriesIdx];
+ entry->FrameCount = frame_count;
+ entry->AllocCount = entry->FreeCount = 0;
+ }
+ if (size != (size_t)-1)
+ {
+ entry->AllocCount++;
+ info->TotalAllocCount++;
+ //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, size, ptr);
+ }
+ else
+ {
+ entry->FreeCount++;
+ info->TotalFreeCount++;
+ //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
+ }
+}
+
const char* ImGui::GetClipboardText()
{
ImGuiContext& g = *GImGui;
@@ -4158,33 +4367,33 @@ int ImGui::GetFrameCount()
return GImGui->FrameCount;
}
-static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
+static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
{
// Create the draw list on demand, because they are not frequently used for all viewports
ImGuiContext& g = *GImGui;
- IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
- ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
+ IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
+ ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
if (draw_list == NULL)
{
draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
draw_list->_OwnerName = drawlist_name;
- viewport->DrawLists[drawlist_no] = draw_list;
+ viewport->BgFgDrawLists[drawlist_no] = draw_list;
}
// Our ImDrawList system requires that there is always a command
- if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount)
+ if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
{
draw_list->_ResetForNewFrame();
draw_list->PushTextureID(g.IO.Fonts->TexID);
draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
- viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount;
+ viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
}
return draw_list;
}
ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
{
- return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
+ return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
}
ImDrawList* ImGui::GetBackgroundDrawList()
@@ -4195,7 +4404,7 @@ ImDrawList* ImGui::GetBackgroundDrawList()
ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
{
- return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
+ return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
}
ImDrawList* ImGui::GetForegroundDrawList()
@@ -4332,6 +4541,9 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
{
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
+
+ // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
+ // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
// Find the window hovered by mouse:
@@ -4339,7 +4551,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
// - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
// - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
bool clear_hovered_windows = false;
- FindHoveredWindow();
+ FindHoveredWindowEx(g.IO.MousePos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
// Modal windows prevents mouse from hovering behind them.
ImGuiWindow* modal_window = GetTopMostPopupModal();
@@ -4393,17 +4605,37 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
}
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
- if (g.WantCaptureKeyboardNextFrame != -1)
- io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
- else
- io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
+ io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
io.WantCaptureKeyboard = true;
+ if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
+ io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
}
+// Calling SetupDrawListSharedData() is followed by SetCurrentFont() which sets up the remaining data.
+static void SetupDrawListSharedData()
+{
+ ImGuiContext& g = *GImGui;
+ ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
+ for (ImGuiViewportP* viewport : g.Viewports)
+ virtual_space.Add(viewport->GetMainRect());
+ g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
+ g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
+ g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
+ g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
+ if (g.Style.AntiAliasedLines)
+ g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
+ if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
+ g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
+ if (g.Style.AntiAliasedFill)
+ g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
+ if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
+ g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
+}
+
void ImGui::NewFrame()
{
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
@@ -4446,30 +4678,13 @@ void ImGui::NewFrame()
// Setup current font and draw list shared data
g.IO.Fonts->Locked = true;
+ SetupDrawListSharedData();
SetCurrentFont(GetDefaultFont());
IM_ASSERT(g.Font->IsLoaded());
- ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
- for (int n = 0; n < g.Viewports.Size; n++)
- virtual_space.Add(g.Viewports[n]->GetMainRect());
- g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
- g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
- g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
- g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
- if (g.Style.AntiAliasedLines)
- g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
- if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
- g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
- if (g.Style.AntiAliasedFill)
- g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
- if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
- g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
// Mark rendering data as invalid to prevent user who may have a handle on it to use it.
- for (int n = 0; n < g.Viewports.Size; n++)
- {
- ImGuiViewportP* viewport = g.Viewports[n];
- viewport->DrawDataP.Clear();
- }
+ for (ImGuiViewportP* viewport : g.Viewports)
+ viewport->DrawDataP.Valid = false;
// Drag and drop keep the source ID alive so even if the source disappear our state is consistent
if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
@@ -4534,21 +4749,33 @@ void ImGui::NewFrame()
}
#endif
+ // Record when we have been stationary as this state is preserved while over same item.
+ // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
+ // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
+ if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
+ g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
+ else if (g.HoverItemDelayId == 0)
+ g.HoverItemUnlockedStationaryId = 0;
+ if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
+ g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
+ else if (g.HoveredWindow == NULL)
+ g.HoverWindowUnlockedStationaryId = 0;
+
// Update hover delay for IsItemHovered() with delays and tooltips
- g.HoverDelayIdPreviousFrame = g.HoverDelayId;
- if (g.HoverDelayId != 0)
+ g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
+ if (g.HoverItemDelayId != 0)
{
- //if (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f) // Need design/flags
- g.HoverDelayTimer += g.IO.DeltaTime;
- g.HoverDelayClearTimer = 0.0f;
- g.HoverDelayId = 0;
+ g.HoverItemDelayTimer += g.IO.DeltaTime;
+ g.HoverItemDelayClearTimer = 0.0f;
+ g.HoverItemDelayId = 0;
}
- else if (g.HoverDelayTimer > 0.0f)
+ else if (g.HoverItemDelayTimer > 0.0f)
{
// This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
- g.HoverDelayClearTimer += g.IO.DeltaTime;
- if (g.HoverDelayClearTimer >= ImMax(0.20f, g.IO.DeltaTime * 2.0f)) // ~6 frames at 30 Hz + allow for low framerate
- g.HoverDelayTimer = g.HoverDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
+ // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
+ g.HoverItemDelayClearTimer += g.IO.DeltaTime;
+ if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
+ g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
}
// Drag and drop
@@ -4603,9 +4830,8 @@ void ImGui::NewFrame()
// Mark all windows as not visible and compact unused memory.
IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
- for (int i = 0; i != g.Windows.Size; i++)
+ for (ImGuiWindow* window : g.Windows)
{
- ImGuiWindow* window = g.Windows[i];
window->WasActive = window->Active;
window->Active = false;
window->WriteAccessed = false;
@@ -4621,9 +4847,9 @@ void ImGui::NewFrame()
for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
- for (int i = 0; i < g.TablesTempData.Size; i++)
- if (g.TablesTempData[i].LastTimeActive >= 0.0f && g.TablesTempData[i].LastTimeActive < memory_compact_start_time)
- TableGcCompactTransientBuffers(&g.TablesTempData[i]);
+ for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
+ if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
+ TableGcCompactTransientBuffers(&table_temp_data);
if (g.GcCompactAll)
GcCompactTransientMiscBuffers();
g.GcCompactAll = false;
@@ -4641,15 +4867,22 @@ void ImGui::NewFrame()
g.GroupStack.resize(0);
// [DEBUG] Update debug features
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
UpdateDebugToolItemPicker();
UpdateDebugToolStackQueries();
+ UpdateDebugToolFlashStyleColor();
if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
- g.DebugLocateId = 0;
- if (g.DebugLogClipperAutoDisableFrames > 0 && --g.DebugLogClipperAutoDisableFrames == 0)
{
- DebugLog("(Auto-disabled ImGuiDebugLogFlags_EventClipper to avoid spamming)\n");
- g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
+ g.DebugLocateId = 0;
+ g.DebugBreakInLocateId = false;
}
+ if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
+ {
+ DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
+ g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
+ g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
+ }
+#endif
// Create implicit/fallback window - which we will only render it if the user has added something to it.
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
@@ -4661,10 +4894,12 @@ void ImGui::NewFrame()
// [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
// allowing to validate correct Begin/End behavior in user code.
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (g.IO.ConfigDebugBeginReturnValueLoop)
g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
else
g.DebugBeginReturnValueCullDepth = -1;
+#endif
CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
}
@@ -4697,53 +4932,17 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im
}
}
-static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list)
-{
- if (draw_list->CmdBuffer.Size == 0)
- return;
- if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)
- return;
-
- // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
- // May trigger for you if you are using PrimXXX functions incorrectly.
- IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
- IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
- if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
- IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
-
- // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
- // If this assert triggers because you are drawing lots of stuff manually:
- // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
- // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
- // - If you want large meshes with more than 64K vertices, you can either:
- // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
- // Most example backends already support this from 1.71. Pre-1.71 backends won't.
- // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
- // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
- // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
- // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
- // Your own engine or render API may use different parameters or function calls to specify index sizes.
- // 2 and 4 bytes indices are generally supported by most graphics API.
- // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
- // the 64K limit to split your draw commands in multiple draw lists.
- if (sizeof(ImDrawIdx) == 2)
- IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
-
- out_list->push_back(draw_list);
-}
-
static void AddWindowToDrawData(ImGuiWindow* window, int layer)
{
ImGuiContext& g = *GImGui;
ImGuiViewportP* viewport = g.Viewports[0];
g.IO.MetricsRenderWindows++;
- AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList);
- for (int i = 0; i < window->DC.ChildWindows.Size; i++)
- {
- ImGuiWindow* child = window->DC.ChildWindows[i];
+ if (window->DrawList->_Splitter._Count > 1)
+ window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
+ ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
+ for (ImGuiWindow* child : window->DC.ChildWindows)
if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
AddWindowToDrawData(child, layer);
- }
}
static inline int GetWindowDisplayLayer(ImGuiWindow* window)
@@ -4757,42 +4956,41 @@ static inline void AddRootWindowToDrawData(ImGuiWindow* window)
AddWindowToDrawData(window, GetWindowDisplayLayer(window));
}
-void ImDrawDataBuilder::FlattenIntoSingleLayer()
+static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
{
- int n = Layers[0].Size;
- int size = n;
- for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
- size += Layers[i].Size;
- Layers[0].resize(size);
- for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
+ int n = builder->Layers[0]->Size;
+ int full_size = n;
+ for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
+ full_size += builder->Layers[i]->Size;
+ builder->Layers[0]->resize(full_size);
+ for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
{
- ImVector& layer = Layers[layer_n];
- if (layer.empty())
+ ImVector* layer = builder->Layers[layer_n];
+ if (layer->empty())
continue;
- memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
- n += layer.Size;
- layer.resize(0);
+ memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
+ n += layer->Size;
+ layer->resize(0);
}
}
-static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists)
+static void InitViewportDrawData(ImGuiViewportP* viewport)
{
ImGuiIO& io = ImGui::GetIO();
ImDrawData* draw_data = &viewport->DrawDataP;
+
+ viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
+ viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
+ viewport->DrawDataBuilder.Layers[0]->resize(0);
+ viewport->DrawDataBuilder.Layers[1]->resize(0);
+
draw_data->Valid = true;
- draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
- draw_data->CmdListsCount = draw_lists->Size;
+ draw_data->CmdListsCount = 0;
draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
draw_data->DisplayPos = viewport->Pos;
draw_data->DisplaySize = viewport->Size;
draw_data->FramebufferScale = io.DisplayFramebufferScale;
- for (int n = 0; n < draw_lists->Size; n++)
- {
- ImDrawList* draw_list = draw_lists->Data[n];
- draw_list->_PopUnusedDrawCmd();
- draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;
- draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;
- }
+ draw_data->OwnerViewport = viewport;
}
// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
@@ -4831,14 +5029,14 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32
ImDrawList* draw_list = window->RootWindow->DrawList;
if (draw_list->CmdBuffer.Size == 0)
draw_list->AddDrawCmd();
- draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged
+ draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)
draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
ImDrawCmd cmd = draw_list->CmdBuffer.back();
IM_ASSERT(cmd.ElemCount == 6);
draw_list->CmdBuffer.pop_back();
draw_list->CmdBuffer.push_front(cmd);
- draw_list->PopClipRect();
draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
+ draw_list->PopClipRect();
}
}
@@ -4874,7 +5072,7 @@ static void ImGui::RenderDimmedBackgrounds()
{
// Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
- RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio));
+ RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
}
else if (dim_bg_for_window_list)
{
@@ -4918,18 +5116,7 @@ void ImGui::EndFrame()
{
IMGUI_DEBUG_LOG_IO("[io] Calling io.SetPlatformImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);
ImGuiViewport* viewport = GetMainViewport();
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- if (viewport->PlatformHandleRaw == NULL && g.IO.ImeWindowHandle != NULL)
- {
- viewport->PlatformHandleRaw = g.IO.ImeWindowHandle;
- g.IO.SetPlatformImeDataFn(viewport, ime_data);
- viewport->PlatformHandleRaw = NULL;
- }
- else
-#endif
- {
- g.IO.SetPlatformImeDataFn(viewport, ime_data);
- }
+ g.IO.SetPlatformImeDataFn(viewport, ime_data);
}
// Hide implicit/fallback "Debug" window if it hasn't been used
@@ -4969,9 +5156,8 @@ void ImGui::EndFrame()
// We cannot do that on FocusWindow() because children may not exist yet
g.WindowsTempSortBuffer.resize(0);
g.WindowsTempSortBuffer.reserve(g.Windows.Size);
- for (int i = 0; i != g.Windows.Size; i++)
+ for (ImGuiWindow* window : g.Windows)
{
- ImGuiWindow* window = g.Windows[i];
if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
continue;
AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
@@ -4986,6 +5172,7 @@ void ImGui::EndFrame()
g.IO.Fonts->Locked = false;
// Clear Input data for next frame
+ g.IO.MousePosPrev = g.IO.MousePos;
g.IO.AppFocusLost = false;
g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
g.IO.InputQueueCharacters.resize(0);
@@ -5003,32 +5190,30 @@ void ImGui::Render()
if (g.FrameCountEnded != g.FrameCount)
EndFrame();
- const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount);
+ if (g.FrameCountRendered == g.FrameCount)
+ return;
g.FrameCountRendered = g.FrameCount;
- g.IO.MetricsRenderWindows = 0;
+ g.IO.MetricsRenderWindows = 0;
CallContextHooks(&g, ImGuiContextHookType_RenderPre);
- // Add background ImDrawList (for each active viewport)
- for (int n = 0; n != g.Viewports.Size; n++)
- {
- ImGuiViewportP* viewport = g.Viewports[n];
- viewport->DrawDataBuilder.Clear();
- if (viewport->DrawLists[0] != NULL)
- AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
- }
-
// Draw modal/window whitening backgrounds
- if (first_render_of_frame)
- RenderDimmedBackgrounds();
+ RenderDimmedBackgrounds();
+
+ // Add background ImDrawList (for each active viewport)
+ for (ImGuiViewportP* viewport : g.Viewports)
+ {
+ InitViewportDrawData(viewport);
+ if (viewport->BgFgDrawLists[0] != NULL)
+ AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
+ }
// Add ImDrawList to render
ImGuiWindow* windows_to_render_top_most[2];
windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
- for (int n = 0; n != g.Windows.Size; n++)
+ for (ImGuiWindow* window : g.Windows)
{
- ImGuiWindow* window = g.Windows[n];
IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
AddRootWindowToDrawData(window);
@@ -5038,22 +5223,25 @@ void ImGui::Render()
AddRootWindowToDrawData(windows_to_render_top_most[n]);
// Draw software mouse cursor if requested by io.MouseDrawCursor flag
- if (g.IO.MouseDrawCursor && first_render_of_frame && g.MouseCursor != ImGuiMouseCursor_None)
+ if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
// Setup ImDrawData structures for end-user
g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
- for (int n = 0; n < g.Viewports.Size; n++)
+ for (ImGuiViewportP* viewport : g.Viewports)
{
- ImGuiViewportP* viewport = g.Viewports[n];
- viewport->DrawDataBuilder.FlattenIntoSingleLayer();
+ FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
// Add foreground ImDrawList (for each active viewport)
- if (viewport->DrawLists[1] != NULL)
- AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
+ if (viewport->BgFgDrawLists[1] != NULL)
+ AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
- SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
+ // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
ImDrawData* draw_data = &viewport->DrawDataP;
+ IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
+ for (ImDrawList* draw_list : draw_data->CmdLists)
+ draw_list->_PopUnusedDrawCmd();
+
g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
}
@@ -5084,22 +5272,24 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex
// FIXME: Investigate using ceilf or e.g.
// - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
// - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
- text_size.x = IM_FLOOR(text_size.x + 0.99999f);
+ text_size.x = IM_TRUNC(text_size.x + 0.99999f);
return text_size;
}
// Find window given position, search front-to-back
-// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
-// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
-// called, aka before the next Begin(). Moving window isn't affected.
-static void FindHoveredWindow()
+// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
+// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
+// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
+// called, aka before the next Begin(). Moving window isn't affected.
+// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
+void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
{
ImGuiContext& g = *GImGui;
-
ImGuiWindow* hovered_window = NULL;
- ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
- if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
+ ImGuiWindow* hovered_window_under_moving_window = NULL;
+
+ if (find_first_and_in_any_viewport == false && g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
hovered_window = g.MovingWindow;
ImVec2 padding_regular = g.Style.TouchExtraPadding;
@@ -5114,12 +5304,8 @@ static void FindHoveredWindow()
continue;
// Using the clipped AABB, a child window will typically be clipped by its parent (not always)
- ImRect bb(window->OuterRectClipped);
- if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
- bb.Expand(padding_regular);
- else
- bb.Expand(padding_for_resize);
- if (!bb.Contains(g.IO.MousePos))
+ ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
+ if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
continue;
// Support for one rectangular hole in any given window
@@ -5128,21 +5314,30 @@ static void FindHoveredWindow()
{
ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
- if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
+ if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
continue;
}
- if (hovered_window == NULL)
+ if (find_first_and_in_any_viewport)
+ {
hovered_window = window;
- IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
- if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
- hovered_window_ignoring_moving_window = window;
- if (hovered_window && hovered_window_ignoring_moving_window)
break;
+ }
+ else
+ {
+ if (hovered_window == NULL)
+ hovered_window = window;
+ IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
+ if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
+ hovered_window_under_moving_window = window;
+ if (hovered_window && hovered_window_under_moving_window)
+ break;
+ }
}
- g.HoveredWindow = hovered_window;
- g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
+ *out_hovered_window = hovered_window;
+ if (out_hovered_window_under_moving_window != NULL)
+ *out_hovered_window_under_moving_window = hovered_window_under_moving_window;
}
bool ImGui::IsItemActive()
@@ -5204,6 +5399,9 @@ bool ImGui::IsItemToggledSelection()
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
}
+// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
+// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
+// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
bool ImGui::IsAnyItemHovered()
{
ImGuiContext& g = *GImGui;
@@ -5234,17 +5432,28 @@ bool ImGui::IsItemEdited()
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
}
+// Allow next item to be overlapped by subsequent items.
+// This works by requiring HoveredId to match for two subsequent frames,
+// so if a following items overwrite it our interactions will naturally be disabled.
+void ImGui::SetNextItemAllowOverlap()
+{
+ ImGuiContext& g = *GImGui;
+ g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
+}
+
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
-// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
+// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
void ImGui::SetItemAllowOverlap()
{
ImGuiContext& g = *GImGui;
ImGuiID id = g.LastItemData.ID;
if (g.HoveredId == id)
g.HoveredIdAllowOverlap = true;
- if (g.ActiveId == id)
+ if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
g.ActiveIdAllowOverlap = true;
}
+#endif
// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function.
void ImGui::SetActiveIdUsingAllKeyboardKeys()
@@ -5280,40 +5489,105 @@ ImVec2 ImGui::GetItemRectSize()
return g.LastItemData.Rect.GetSize();
}
-bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
+// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
+// ImGuiChildFlags_Border is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
+bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
+{
+ ImGuiID id = GetCurrentWindow()->GetID(str_id);
+ return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
+}
+
+bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
+{
+ return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
+}
+
+bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* parent_window = g.CurrentWindow;
+ IM_ASSERT(id != 0);
- flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
- flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
+ // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
+ const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle;
+ IM_UNUSED(ImGuiChildFlags_SupportedMask_);
+ IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
+ IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
+ if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
+ {
+ IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
+ IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
+ }
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
+ child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
+#endif
+ if (child_flags & ImGuiChildFlags_AutoResizeX)
+ child_flags &= ~ImGuiChildFlags_ResizeX;
+ if (child_flags & ImGuiChildFlags_AutoResizeY)
+ child_flags &= ~ImGuiChildFlags_ResizeY;
- // Size
- const ImVec2 content_avail = GetContentRegionAvail();
- ImVec2 size = ImFloor(size_arg);
- const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
- if (size.x <= 0.0f)
- size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too many issues)
- if (size.y <= 0.0f)
- size.y = ImMax(content_avail.y + size.y, 4.0f);
+ // Set window flags
+ window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;
+ window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
+ if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
+ window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
+ if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
+ window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
+
+ // Special framed style
+ if (child_flags & ImGuiChildFlags_FrameStyle)
+ {
+ PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
+ PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
+ PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
+ PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
+ child_flags |= ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding;
+ window_flags |= ImGuiWindowFlags_NoMove;
+ }
+
+ // Forward child flags
+ g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
+ g.NextWindowData.ChildFlags = child_flags;
+
+ // Forward size
+ // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
+ // (the alternative would to store conditional flags per axis, which is possible but more code)
+ const ImVec2 size_avail = GetContentRegionAvail();
+ const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
+ const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
SetNextWindowSize(size);
// Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
+ // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
+ // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
const char* temp_window_name;
+ /*if (name && parent_window->IDStack.back() == parent_window->ID)
+ ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
+ else*/
if (name)
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
else
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
+ // Set style
const float backup_border_size = g.Style.ChildBorderSize;
- if (!border)
+ if ((child_flags & ImGuiChildFlags_Border) == 0)
g.Style.ChildBorderSize = 0.0f;
- bool ret = Begin(temp_window_name, NULL, flags);
+
+ // Begin into window
+ const bool ret = Begin(temp_window_name, NULL, window_flags);
+
+ // Restore style
g.Style.ChildBorderSize = backup_border_size;
+ if (child_flags & ImGuiChildFlags_FrameStyle)
+ {
+ PopStyleVar(3);
+ PopStyleColor();
+ }
ImGuiWindow* child_window = g.CurrentWindow;
child_window->ChildId = id;
- child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
// Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
// While this is not really documented/defined, it seems that the expected thing to do.
@@ -5321,11 +5595,11 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
parent_window->DC.CursorPos = child_window->Pos;
// Process navigation-in immediately so NavInit can run on first frame
- // Can enter a child if (A) it has navigatable items or (B) it can be scrolled.
- const ImGuiID temp_id_for_activation = (id + 1);
+ // Can enter a child if (A) it has navigable items or (B) it can be scrolled.
+ const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
if (g.ActiveId == temp_id_for_activation)
ClearActiveID();
- if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
+ if (g.NavActivateId == id && !(window_flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
{
FocusWindow(child_window);
NavInitWindow(child_window, false);
@@ -5335,88 +5609,49 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
return ret;
}
-bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
-{
- ImGuiWindow* window = GetCurrentWindow();
- return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
-}
-
-bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
-{
- IM_ASSERT(id != 0);
- return BeginChildEx(NULL, id, size_arg, border, extra_flags);
-}
-
void ImGui::EndChild()
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
+ ImGuiWindow* child_window = g.CurrentWindow;
IM_ASSERT(g.WithinEndChild == false);
- IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
+ IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
g.WithinEndChild = true;
- if (window->BeginCount > 1)
+ ImVec2 child_size = child_window->Size;
+ End();
+ if (child_window->BeginCount == 1)
{
- End();
- }
- else
- {
- ImVec2 sz = window->Size;
- if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
- sz.x = ImMax(4.0f, sz.x);
- if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
- sz.y = ImMax(4.0f, sz.y);
- End();
-
ImGuiWindow* parent_window = g.CurrentWindow;
- ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
- ItemSize(sz);
- if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavWindowHasScrollY) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
+ ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
+ ItemSize(child_size);
+ if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !(child_window->Flags & ImGuiWindowFlags_NavFlattened))
{
- ItemAdd(bb, window->ChildId);
- RenderNavHighlight(bb, window->ChildId);
+ ItemAdd(bb, child_window->ChildId);
+ RenderNavHighlight(bb, child_window->ChildId);
// When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
- if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow)
- RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
+ if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
+ RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_Compact);
}
else
{
// Not navigable into
- ItemAdd(bb, 0);
+ // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
+ // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
+ ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
// But when flattened we directly reach items, adjust active layer mask accordingly
- if (window->Flags & ImGuiWindowFlags_NavFlattened)
- parent_window->DC.NavLayersActiveMaskNext |= window->DC.NavLayersActiveMaskNext;
+ if (child_window->Flags & ImGuiWindowFlags_NavFlattened)
+ parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
}
- if (g.HoveredWindow == window)
+ if (g.HoveredWindow == child_window)
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
}
g.WithinEndChild = false;
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
}
-// Helper to create a child window / scrolling region that looks like a normal widget frame.
-bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
-{
- ImGuiContext& g = *GImGui;
- const ImGuiStyle& style = g.Style;
- PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
- PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
- PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
- PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
- bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
- PopStyleVar(3);
- PopStyleColor();
- return ret;
-}
-
-void ImGui::EndChildFrame()
-{
- EndChild();
-}
-
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
{
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
@@ -5438,9 +5673,9 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name)
static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
{
- window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
+ window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y));
if (settings->Size.x > 0 && settings->Size.y > 0)
- window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
+ window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
window->Collapsed = settings->Collapsed;
}
@@ -5473,6 +5708,7 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s
// Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
window->Pos = main_viewport->Pos + ImVec2(60, 60);
+ window->Size = window->SizeFull = ImVec2(0, 0);
window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
if (settings != NULL)
@@ -5521,13 +5757,37 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
return window;
}
+static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
+{
+ // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
+ // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
+ // Perhaps should tend further a neater test for this.
+ ImGuiContext& g = *GImGui;
+ ImVec2 size_min;
+ if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
+ {
+ size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
+ size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
+ }
+ else
+ {
+ size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
+ size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
+ }
+
+ // Reduce artifacts with very small windows
+ ImGuiWindow* window_for_height = window;
+ size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
+ return size_min;
+}
+
static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
{
ImGuiContext& g = *GImGui;
ImVec2 new_size = size_desired;
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
{
- // Using -1,-1 on either X/Y axis to preserve the current size.
+ // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
ImRect cr = g.NextWindowData.SizeConstraintRect;
new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
@@ -5541,19 +5801,13 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& s
g.NextWindowData.SizeCallback(&data);
new_size = data.DesiredSize;
}
- new_size.x = IM_FLOOR(new_size.x);
- new_size.y = IM_FLOOR(new_size.y);
+ new_size.x = IM_TRUNC(new_size.x);
+ new_size.y = IM_TRUNC(new_size.y);
}
// Minimum size
- if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
- {
- ImGuiWindow* window_for_height = window;
- new_size = ImMax(new_size, g.Style.WindowMinSize);
- const float minimum_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f);
- new_size.y = ImMax(new_size.y, minimum_height); // Reduce artifacts with very small windows
- }
- return new_size;
+ ImVec2 size_min = CalcWindowMinSize(window);
+ return ImMax(new_size, size_min);
}
static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
@@ -5570,10 +5824,10 @@ static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_cur
return;
}
- content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
- content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
- content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
- content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
+ content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
+ content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
+ content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
+ content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
}
static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
@@ -5592,19 +5846,22 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont
else
{
// Maximum window size is determined by the viewport size or monitor size
- const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
- const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
- ImVec2 size_min = style.WindowMinSize;
- if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
- size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
+ ImVec2 size_min = CalcWindowMinSize(window);
+ ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
+ ImVec2 size_auto_fit = ImClamp(size_desired, size_min, size_max);
- ImVec2 avail_size = ImGui::GetMainViewport()->WorkSize;
- ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f));
+ // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
+ // we may need to compute/store three variants of size_auto_fit, for x/y/xy.
+ // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
+ if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & ImGuiChildFlags_ResizeY))
+ size_auto_fit.y = window->SizeFull.y;
+ else if (!(window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY))
+ size_auto_fit.x = window->SizeFull.x;
// When the window cannot fit all contents (either because of constraints, either because screen is too small),
// we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
- bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
+ bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
if (will_have_scrollbar_x)
size_auto_fit.y += style.ScrollbarSize;
@@ -5647,7 +5904,7 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co
*out_size = size_constrained;
}
-// Data for resizing from corner
+// Data for resizing from resize grip / corner
struct ImGuiResizeGripDef
{
ImVec2 CornerPosN;
@@ -5665,9 +5922,9 @@ static const ImGuiResizeGripDef resize_grip_def[4] =
// Data for resizing from borders
struct ImGuiResizeBorderDef
{
- ImVec2 InnerDir;
- ImVec2 SegmentN1, SegmentN2;
- float OuterAngle;
+ ImVec2 InnerDir; // Normal toward inside
+ ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left)
+ float OuterAngle; // Angle toward outside
};
static const ImGuiResizeBorderDef resize_border_def[4] =
{
@@ -5713,7 +5970,7 @@ ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
// Handle resize for: Resize Grips, Borders, Gamepad
// Return true when using auto-fit (double-click on resize grip)
-static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
+static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
{
ImGuiContext& g = *GImGui;
ImGuiWindowFlags flags = window->Flags;
@@ -5723,10 +5980,9 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
return false;
- bool ret_auto_fit = false;
- const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
- const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
- const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
+ int ret_auto_fit_mask = 0x00;
+ const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
+ const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
ImRect clamp_rect = visibility_rect;
@@ -5759,11 +6015,11 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
if (hovered || held)
g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
- if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0)
+ if (held && g.IO.MouseDoubleClicked[0])
{
- // Manual auto-fit when double-clicking
+ // Auto-fit when double-clicking
size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
- ret_auto_fit = true;
+ ret_auto_fit_mask = 0x03; // Both axises
ClearActiveID();
}
else if (held)
@@ -5781,8 +6037,16 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
if (resize_grip_n == 0 || held || hovered)
resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
}
- for (int border_n = 0; border_n < resize_border_count; border_n++)
+
+ int resize_border_mask = 0x00;
+ if (window->Flags & ImGuiWindowFlags_ChildWindow)
+ resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
+ else
+ resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
+ for (int border_n = 0; border_n < 4; border_n++)
{
+ if ((resize_border_mask & (1 << border_n)) == 0)
+ continue;
const ImGuiResizeBorderDef& def = resize_border_def[border_n];
const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
@@ -5791,22 +6055,77 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
- //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
- if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
- {
+ //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
+ if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
+ hovered = false;
+ if (hovered || held)
g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
- if (held)
- *border_held = border_n;
- }
- if (held)
+ if (held && g.IO.MouseDoubleClicked[0])
{
+ // Double-clicking bottom or right border auto-fit on this axis
+ // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
+ // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
+ if (border_n == 1 || border_n == 3) // Right and bottom border
+ {
+ size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
+ ret_auto_fit_mask |= (1 << axis);
+ hovered = held = false; // So border doesn't show highlighted at new position
+ }
+ ClearActiveID();
+ }
+ else if (held)
+ {
+ // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
+ // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.
+ // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
+ const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false));
+ if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
+ {
+ g.WindowResizeBorderExpectedRect = border_rect;
+ g.WindowResizeRelativeMode = false;
+ }
+ if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
+ g.WindowResizeRelativeMode = true;
+
+ const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
+ const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
+ const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above.
+
+ // Use absolute mode position
+ ImVec2 border_target = window->Pos;
+ border_target[axis] = border_target_abs_mode_for_axis;
+
+ // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
+ bool ignore_resize = false;
+ if (g.WindowResizeRelativeMode)
+ {
+ //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
+ border_target[axis] = border_target_rel_mode_for_axis;
+ if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis))
+ ignore_resize = true;
+ }
+
+ // Clamp, apply
ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX);
ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
- ImVec2 border_target = window->Pos;
- border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING;
border_target = ImClamp(border_target, clamp_min, clamp_max);
- CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
+ if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
+ {
+ ImGuiWindowFlags parent_flags = window->ParentWindow->Flags;
+ ImRect border_limit_rect = window->ParentWindow->InnerRect;
+ border_limit_rect.Expand(ImVec2(-ImMax(window->WindowPadding.x, window->WindowBorderSize), -ImMax(window->WindowPadding.y, window->WindowBorderSize)));
+ if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar))
+ border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x);
+ if (parent_flags & ImGuiWindowFlags_NoScrollbar)
+ border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y);
+ }
+ if (!ignore_resize)
+ CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
}
+ if (hovered)
+ *border_hovered = border_n;
+ if (held)
+ *border_held = border_n;
}
PopID();
@@ -5832,7 +6151,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
g.NavWindowingToggleLayer = false;
g.NavDisableMouseHover = true;
resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
- ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaSize);
+ ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
{
// FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
@@ -5843,19 +6162,24 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
}
// Apply back modified position/size to window
- if (size_target.x != FLT_MAX)
- {
- window->SizeFull = size_target;
+ const ImVec2 curr_pos = window->Pos;
+ const ImVec2 curr_size = window->SizeFull;
+ if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
+ window->Size.x = window->SizeFull.x = size_target.x;
+ if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
+ window->Size.y = window->SizeFull.y = size_target.y;
+ if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
+ window->Pos.x = ImTrunc(pos_target.x);
+ if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
+ window->Pos.y = ImTrunc(pos_target.y);
+ if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y)
MarkIniSettingsDirty(window);
- }
- if (pos_target.x != FLT_MAX)
- {
- window->Pos = ImFloor(pos_target);
- MarkIniSettingsDirty(window);
- }
- window->Size = window->SizeFull;
- return ret_auto_fit;
+ // Recalculate next expected border expected coordinates
+ if (*border_held != -1)
+ g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
+
+ return ret_auto_fit_mask;
}
static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
@@ -5867,27 +6191,40 @@ static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_
window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
}
+static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
+{
+ const ImGuiResizeBorderDef& def = resize_border_def[border_n];
+ const float rounding = window->WindowRounding;
+ const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
+ window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
+ window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
+ window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
+}
+
static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
- float rounding = window->WindowRounding;
- float border_size = window->WindowBorderSize;
- if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
- window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
-
- int border_held = window->ResizeBorderHeld;
- if (border_held != -1)
+ const float border_size = window->WindowBorderSize;
+ const ImU32 border_col = GetColorU32(ImGuiCol_Border);
+ if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
+ window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
+ else if (border_size > 0.0f)
{
- const ImGuiResizeBorderDef& def = resize_border_def[border_held];
- ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
- window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
- window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
- window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual
+ if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
+ RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
+ if (window->ChildFlags & ImGuiChildFlags_ResizeY)
+ RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
+ }
+ if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
+ {
+ const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
+ const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
+ RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
}
if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
{
float y = window->Pos.y + window->TitleBarHeight() - 1;
- window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize);
+ window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize);
}
}
@@ -6005,18 +6342,18 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
ImVec2 collapse_button_pos;
if (has_close_button)
{
- pad_r += button_sz;
- close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
+ close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
+ pad_r += button_sz + style.ItemInnerSpacing.x;
}
if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
{
- pad_r += button_sz;
- collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
+ collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
+ pad_r += button_sz + style.ItemInnerSpacing.x;
}
if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
{
- collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
- pad_l += button_sz;
+ collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
+ pad_l += button_sz + style.ItemInnerSpacing.x;
}
// Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
@@ -6086,18 +6423,43 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags
}
}
+// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
+// This is designed as a toy/test-bed for
+void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ window->SkipRefresh = false;
+ if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
+ return;
+ if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
+ {
+ // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
+ if (window->Appearing) // If currently appearing
+ return;
+ if (window->Hidden) // If was hidden (previous frame)
+ return;
+ if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow && window->RootWindow == g.HoveredWindow->RootWindow)
+ return;
+ if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow && window->RootWindow == g.NavWindow->RootWindow)
+ return;
+ window->DrawList = NULL;
+ window->SkipRefresh = true;
+ }
+}
+
// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
-// - Window // FindBlockingModal() returns Modal1
-// - Window // .. returns Modal1
+// - WindowA // FindBlockingModal() returns Modal1
+// - WindowB // .. returns Modal1
// - Modal1 // .. returns Modal2
-// - Window // .. returns Modal2
-// - Window // .. returns Modal2
+// - WindowC // .. returns Modal2
+// - WindowD // .. returns Modal2
// - Modal2 // .. returns Modal2
+// - WindowE // .. returns NULL
// Notes:
// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
-// Only difference is here we check for ->Active/WasActive but it may be unecessary.
+// Only difference is here we check for ->Active/WasActive but it may be unnecessary.
ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
@@ -6105,20 +6467,18 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
return NULL;
// Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
- for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--)
+ for (ImGuiPopupData& popup_data : g.OpenPopupStack)
{
- ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window;
+ ImGuiWindow* popup_window = popup_data.Window;
if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
continue;
if (!popup_window->Active && !popup_window->WasActive) // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.
continue;
if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
return popup_window;
- if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed.
- break;
- for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow)
- if (IsWindowWithinBeginStackOf(window, parent))
- return popup_window; // Place window above its begin stack parent.
+ if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal
+ continue;
+ return popup_window; // Place window right below first block modal
}
return NULL;
}
@@ -6144,6 +6504,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (window_just_created)
window = CreateNewWindow(name, flags);
+ // [DEBUG] Debug break requested by user
+ if (g.DebugBreakInWindow == window->ID)
+ IM_DEBUG_BREAK();
+
// Automatically disable manual moving/resizing when NoInputs is set
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
@@ -6172,6 +6536,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
UpdateWindowInFocusOrderList(window, window_just_created, flags);
window->Flags = (ImGuiWindowFlags)flags;
+ window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
window->LastFrameActive = current_frame;
window->LastTimeActive = (float)g.Time;
window->BeginOrderWithinParent = 0;
@@ -6192,7 +6557,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->IDStack.push_back(window->ID);
// Add to stack
- // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
g.CurrentWindow = window;
ImGuiWindowStackData window_stack_data;
window_stack_data.Window = window;
@@ -6200,21 +6564,24 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window_stack_data.StackSizesOnBegin.SetToContextState(&g);
g.CurrentWindowStack.push_back(window_stack_data);
if (flags & ImGuiWindowFlags_ChildMenu)
- g.BeginMenuCount++;
+ g.BeginMenuDepth++;
// Update ->RootWindow and others pointers (before any possible call to FocusWindow)
if (first_begin_of_the_frame)
{
UpdateWindowParentAndRootLinks(window, flags, parent_window);
window->ParentWindowInBeginStack = parent_window_in_stack;
+
+ // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
+ // e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
+ window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL;
}
// Add to focus scope stack
- PushFocusScope(window->ID);
+ PushFocusScope((flags & ImGuiWindowFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
window->NavRootFocusScopeId = g.CurrentFocusScopeId;
- g.CurrentWindow = NULL;
- // Add to popup stack
+ // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
if (flags & ImGuiWindowFlags_Popup)
{
ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
@@ -6248,6 +6615,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
+ if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
+ g.NextWindowData.SizeVal.x = window->SizeFull.x;
+ if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
+ g.NextWindowData.SizeVal.y = window->SizeFull.y;
SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
}
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
@@ -6274,8 +6645,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (window->Appearing)
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
+ // [EXPERIMENTAL] Skip Refresh mode
+ UpdateWindowSkipRefresh(window);
+
+ // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
+ g.CurrentWindow = NULL;
+
// When reusing window again multiple times a frame, just append content (don't need to setup again)
- if (first_begin_of_the_frame)
+ if (first_begin_of_the_frame && !window->SkipRefresh)
{
// Initialize
const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
@@ -6347,15 +6724,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
else
window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
window->WindowPadding = style.WindowPadding;
- if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
+ if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
// Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
+ // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
+ // Those flags will be altered further down in the function depending on more conditions.
bool use_current_size_for_scrollbar_x = window_just_created;
bool use_current_size_for_scrollbar_y = window_just_created;
+ if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
+ use_current_size_for_scrollbar_x = true;
+ if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
+ use_current_size_for_scrollbar_y = true;
// Collapse window by double-clicking on title bar
// At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
@@ -6363,8 +6746,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
// We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
ImRect title_bar_rect = window->TitleBarRect();
- if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2)
- window->WantCollapseToggle = true;
+ if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
+ if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
+ window->WantCollapseToggle = true;
if (window->WantCollapseToggle)
{
window->Collapsed = !window->Collapsed;
@@ -6382,7 +6766,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// SIZE
// Outer Decoration Sizes
- // (we need to clear ScrollbarSize immediatly as CalcWindowAutoFitSize() needs it and can be called from other locations).
+ // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
window->DecoOuterSizeX1 = 0.0f;
window->DecoOuterSizeX2 = 0.0f;
@@ -6470,7 +6854,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
ClampWindowPos(window, visibility_rect);
- window->Pos = ImFloor(window->Pos);
+ window->Pos = ImTrunc(window->Pos);
// Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
// Large values tend to lead to variety of artifacts and are not recommended.
@@ -6503,13 +6887,19 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
#endif
// Handle manual resize: Resize Grips, Borders, Gamepad
- int border_held = -1;
+ int border_hovered = -1, border_held = -1;
ImU32 resize_grip_col[4] = {};
- const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
- const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
+ const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
+ const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
if (!window->Collapsed)
- if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
- use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
+ if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
+ {
+ if (auto_fit_mask & (1 << ImGuiAxis_X))
+ use_current_size_for_scrollbar_x = true;
+ if (auto_fit_mask & (1 << ImGuiAxis_Y))
+ use_current_size_for_scrollbar_y = true;
+ }
+ window->ResizeBorderHovered = (signed char)border_hovered;
window->ResizeBorderHeld = (signed char)border_held;
// SCROLLBAR VISIBILITY
@@ -6564,24 +6954,26 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
// Inner clipping rectangle.
- // Will extend a little bit outside the normal work region.
- // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
- // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
+ // - Extend a outside of normal work region up to borders.
+ // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
+ // - It also makes clipped items be more noticeable.
+ // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
+ // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
// Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
// Affected by window/frame border size. Used by:
// - Begin() initial clip rect
float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
- window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
+ window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize);
window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
- window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
+ window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - window->WindowBorderSize);
window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
window->InnerClipRect.ClipWithFull(host_rect);
// Default item width. Make it proportional to window size if window manually resizes
if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
- window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
+ window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
else
- window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
+ window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
// SCROLLING
@@ -6642,14 +7034,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
- window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
- window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
+ window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
+ window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
window->ParentWorkRect = window->WorkRect;
// [LEGACY] Content Region
// FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
+ // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
// Used by:
// - Mouse wheel scrolling + many other things
window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
@@ -6698,6 +7091,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DC.TextWrapPos = -1.0f; // disabled
window->DC.ItemWidthStack.resize(0);
window->DC.TextWrapPosStack.resize(0);
+ if (flags & ImGuiWindowFlags_Modal)
+ window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
if (window->AutoFitFramesX > 0)
window->AutoFitFramesX--;
@@ -6732,7 +7127,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
// This is useful to allow creating context menus on title bar only, etc.
- SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
+ SetLastItemDataForWindow(window, title_bar_rect);
// [DEBUG]
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
@@ -6748,11 +7143,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
else
{
+ // Skip refresh always mark active
+ if (window->SkipRefresh)
+ window->Active = true;
+
// Append
SetCurrentWindow(window);
+ SetLastItemDataForWindow(window, window->TitleBarRect());
}
- PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
+ if (!window->SkipRefresh)
+ PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
// Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
window->WriteAccessed = false;
@@ -6760,20 +7161,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
g.NextWindowData.ClearFlags();
// Update visibility
- if (first_begin_of_the_frame)
+ if (first_begin_of_the_frame && !window->SkipRefresh)
{
- if (flags & ImGuiWindowFlags_ChildWindow)
+ if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
{
// Child window can be out of sight and have "negative" clip windows.
// Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
- if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
- {
- const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
- if (!g.LogEnabled && !nav_request)
- if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
+ const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
+ if (!g.LogEnabled && !nav_request)
+ if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
+ {
+ if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
+ window->HiddenFramesCannotSkipItems = 1;
+ else
window->HiddenFramesCanSkipItems = 1;
- }
+ }
// Hide along with parent or if parent is collapsed
if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
@@ -6804,19 +7207,33 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
skip_items = true;
window->SkipItems = skip_items;
}
+ else if (first_begin_of_the_frame)
+ {
+ // Skip refresh mode
+ window->SkipItems = true;
+ }
// [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
// (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
- if (!window->IsFallbackWindow && ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size)))
- {
- if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
- if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
- return false;
- }
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+ if (!window->IsFallbackWindow)
+ if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
+ {
+ if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
+ if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
+ return false;
+ }
+#endif
return !window->SkipItems;
}
+static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
+{
+ ImGuiContext& g = *GImGui;
+ SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect);
+}
+
void ImGui::End()
{
ImGuiContext& g = *GImGui;
@@ -6837,9 +7254,16 @@ void ImGui::End()
// Close anything that is open
if (window->DC.CurrentColumns)
EndColumns();
- PopClipRect(); // Inner window clip rectangle
+ if (!window->SkipRefresh)
+ PopClipRect(); // Inner window clip rectangle
PopFocusScope();
+ if (window->SkipRefresh)
+ {
+ IM_ASSERT(window->DrawList == NULL);
+ window->DrawList = &window->DrawListInst;
+ }
+
// Stop logging
if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
LogFinish();
@@ -6850,7 +7274,7 @@ void ImGui::End()
// Pop from window stack
g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
if (window->Flags & ImGuiWindowFlags_ChildMenu)
- g.BeginMenuCount--;
+ g.BeginMenuDepth--;
if (window->Flags & ImGuiWindowFlags_Popup)
g.BeginPopupStack.pop_back();
g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithContextState(&g);
@@ -6963,8 +7387,9 @@ void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
g.NavMousePosDirty = true;
g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
g.NavLayer = ImGuiNavLayer_Main;
- g.NavFocusScopeId = window ? window->NavRootFocusScopeId : 0;
+ SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
g.NavIdIsAlive = false;
+ g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
// Close popups if any
ClosePopupsOverWindow(window, false);
@@ -7012,7 +7437,6 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind
{
// We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
ImGuiWindow* window = g.WindowsFocusOrder[i];
- IM_ASSERT(window == window->RootWindow);
if (window == ignore_window || !window->WasActive)
continue;
if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
@@ -7210,9 +7634,14 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b
return false;
}
+// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
+// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
+// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
+// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
{
- IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function
+ IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!");
+
ImGuiContext& g = *GImGui;
ImGuiWindow* ref_window = g.HoveredWindow;
ImGuiWindow* cur_window = g.CurrentWindow;
@@ -7240,6 +7669,17 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
return false;
+
+ // When changing hovered window we requires a bit of stationary delay before activating hover timer.
+ // FIXME: We don't support delay other than stationary one for now, other delay would need a way
+ // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
+ // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
+ // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
+ if (flags & ImGuiHoveredFlags_ForTooltip)
+ flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
+ if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
+ return false;
+
return true;
}
@@ -7304,7 +7744,7 @@ void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
// Set
const ImVec2 old_pos = window->Pos;
- window->Pos = ImFloor(pos);
+ window->Pos = ImTrunc(pos);
ImVec2 offset = window->Pos - old_pos;
if (offset.x == 0.0f && offset.y == 0.0f)
return;
@@ -7342,18 +7782,22 @@ void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
+ // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
+ if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
+ window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
+ if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
+ window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
+
// Set
ImVec2 old_size = window->SizeFull;
- window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
- window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
if (size.x <= 0.0f)
window->AutoFitOnlyGrows = false;
else
- window->SizeFull.x = IM_FLOOR(size.x);
+ window->SizeFull.x = IM_TRUNC(size.x);
if (size.y <= 0.0f)
window->AutoFitOnlyGrows = false;
else
- window->SizeFull.y = IM_FLOOR(size.y);
+ window->SizeFull.y = IM_TRUNC(size.y);
if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
MarkIniSettingsDirty(window);
}
@@ -7387,7 +7831,7 @@ void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const I
window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
}
-void ImGui::SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window)
+void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
{
window->Hidden = window->SkipItems = true;
window->HiddenFramesCanSkipItems = 1;
@@ -7453,6 +7897,10 @@ void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
}
+// For each axis:
+// - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.
+// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
+// - See "Demo->Examples->Constrained-resizing window" for examples.
void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
{
ImGuiContext& g = *GImGui;
@@ -7468,7 +7916,7 @@ void ImGui::SetNextWindowContentSize(const ImVec2& size)
{
ImGuiContext& g = *GImGui;
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
- g.NextWindowData.ContentSizeVal = ImFloor(size);
+ g.NextWindowData.ContentSizeVal = ImTrunc(size);
}
void ImGui::SetNextWindowScroll(const ImVec2& scroll)
@@ -7500,6 +7948,14 @@ void ImGui::SetNextWindowBgAlpha(float alpha)
g.NextWindowData.BgAlphaVal = alpha;
}
+// This is experimental and meant to be a toy for exploring a future/wider range of features.
+void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
+ g.NextWindowData.RefreshFlagsVal = flags;
+}
+
ImDrawList* ImGui::GetWindowDrawList()
{
ImGuiWindow* window = GetCurrentWindow();
@@ -7530,35 +7986,89 @@ void ImGui::SetWindowFontScale(float scale)
g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
}
-void ImGui::ActivateItem(ImGuiID id)
-{
- ImGuiContext& g = *GImGui;
- g.NavNextActivateId = id;
- g.NavNextActivateFlags = ImGuiActivateFlags_None;
-}
-
void ImGui::PushFocusScope(ImGuiID id)
{
ImGuiContext& g = *GImGui;
- g.FocusScopeStack.push_back(id);
+ ImGuiFocusScopeData data;
+ data.ID = id;
+ data.WindowID = g.CurrentWindow->ID;
+ g.FocusScopeStack.push_back(data);
g.CurrentFocusScopeId = id;
}
void ImGui::PopFocusScope()
{
ImGuiContext& g = *GImGui;
- IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
+ if (g.FocusScopeStack.Size == 0)
+ {
+ IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!");
+ return;
+ }
g.FocusScopeStack.pop_back();
- g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back() : 0;
+ g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
+}
+
+void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
+{
+ ImGuiContext& g = *GImGui;
+ g.NavFocusScopeId = focus_scope_id;
+ g.NavFocusRoute.resize(0); // Invalidate
+ if (focus_scope_id == 0)
+ return;
+ IM_ASSERT(g.NavWindow != NULL);
+
+ // Store current path (in reverse order)
+ if (focus_scope_id == g.CurrentFocusScopeId)
+ {
+ // Top of focus stack contains local focus scopes inside current window
+ for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
+ g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
+ }
+ else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
+ g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
+ else
+ return;
+
+ // Then follow on manually set ParentWindowForFocusRoute field (#6798)
+ for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
+ g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
+ IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
+}
+
+// Focus = move navigation cursor, set scrolling, set focus window.
+void ImGui::FocusItem()
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
+ if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
+ {
+ IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
+ return;
+ }
+
+ ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight | ImGuiNavMoveFlags_NoSelect;
+ ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
+ SetNavWindow(window);
+ NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
+ NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
+}
+
+void ImGui::ActivateItemByID(ImGuiID id)
+{
+ ImGuiContext& g = *GImGui;
+ g.NavNextActivateId = id;
+ g.NavNextActivateFlags = ImGuiActivateFlags_None;
}
// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
+// But ActivateItem() should function without altering scroll/focus?
void ImGui::SetKeyboardFocusHere(int offset)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
IM_ASSERT(offset >= -1); // -1 is allowed but not below
- IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
+ IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
// It makes sense in the vast majority of cases to never interrupt a drag and drop.
// When we refactor this function into ActivateItem() we may want to make this an option.
@@ -7566,14 +8076,15 @@ void ImGui::SetKeyboardFocusHere(int offset)
// is also automatically dropped in the event g.ActiveId is stolen.
if (g.DragDropActive || g.MovingWindow != NULL)
{
- IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere() ignored while DragDropActive!\n");
+ IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
return;
}
SetNavWindow(window);
+ ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight;
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
- NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
+ NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
if (offset == -1)
{
NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
@@ -7615,6 +8126,69 @@ ImGuiStorage* ImGui::GetStateStorage()
return window->DC.StateStorage;
}
+bool ImGui::IsRectVisible(const ImVec2& size)
+{
+ ImGuiWindow* window = GImGui->CurrentWindow;
+ return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
+}
+
+bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
+{
+ ImGuiWindow* window = GImGui->CurrentWindow;
+ return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
+}
+
+//-----------------------------------------------------------------------------
+// [SECTION] ID STACK
+//-----------------------------------------------------------------------------
+
+// This is one of the very rare legacy case where we use ImGuiWindow methods,
+// it should ideally be flattened at some point but it's been used a lots by widgets.
+ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
+{
+ ImGuiID seed = IDStack.back();
+ ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+ ImGuiContext& g = *Ctx;
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
+#endif
+ return id;
+}
+
+ImGuiID ImGuiWindow::GetID(const void* ptr)
+{
+ ImGuiID seed = IDStack.back();
+ ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+ ImGuiContext& g = *Ctx;
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
+#endif
+ return id;
+}
+
+ImGuiID ImGuiWindow::GetID(int n)
+{
+ ImGuiID seed = IDStack.back();
+ ImGuiID id = ImHashData(&n, sizeof(n), seed);
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+ ImGuiContext& g = *Ctx;
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
+#endif
+ return id;
+}
+
+// This is only used in rare/specific situations to manufacture an ID out of nowhere.
+ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
+{
+ ImGuiID seed = IDStack.back();
+ ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
+ ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
+ return id;
+}
+
void ImGui::PushID(const char* str_id)
{
ImGuiContext& g = *GImGui;
@@ -7652,29 +8226,35 @@ void ImGui::PushOverrideID(ImGuiID id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
if (g.DebugHookIdInfo == id)
DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
+#endif
window->IDStack.push_back(id);
}
// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
-// (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
+// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
{
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *GImGui;
if (g.DebugHookIdInfo == id)
DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
+#endif
return id;
}
ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
{
ImGuiID id = ImHashData(&n, sizeof(n), seed);
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
ImGuiContext& g = *GImGui;
if (g.DebugHookIdInfo == id)
DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
+#endif
return id;
}
@@ -7703,22 +8283,11 @@ ImGuiID ImGui::GetID(const void* ptr_id)
return window->GetID(ptr_id);
}
-bool ImGui::IsRectVisible(const ImVec2& size)
-{
- ImGuiWindow* window = GImGui->CurrentWindow;
- return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
-}
-
-bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
-{
- ImGuiWindow* window = GImGui->CurrentWindow;
- return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
-}
-
-
//-----------------------------------------------------------------------------
// [SECTION] INPUTS
//-----------------------------------------------------------------------------
+// - GetModForModKey() [Internal]
+// - FixupKeyChord() [Internal]
// - GetKeyData() [Internal]
// - GetKeyIndex() [Internal]
// - GetKeyName()
@@ -7748,6 +8317,7 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
// - IsMouseDragPastThreshold() [Internal]
// - IsMouseDragging()
// - GetMousePos()
+// - SetMousePos() [Internal]
// - GetMousePosOnOpeningCurrentPopup()
// - IsMousePosValid()
// - IsAnyMouseDown()
@@ -7779,13 +8349,35 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
// - Shortcut() [Internal]
//-----------------------------------------------------------------------------
+static ImGuiKeyChord GetModForModKey(ImGuiKey key)
+{
+ if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
+ return ImGuiMod_Ctrl;
+ if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
+ return ImGuiMod_Shift;
+ if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
+ return ImGuiMod_Alt;
+ if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
+ return ImGuiMod_Super;
+ return ImGuiMod_None;
+}
+
+ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
+{
+ // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
+ ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
+ if (IsModKey(key))
+ key_chord |= GetModForModKey(key);
+ return key_chord;
+}
+
ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
{
ImGuiContext& g = *ctx;
// Special storage location for mods
if (key & ImGuiMod_Mask_)
- key = ConvertSingleModFlagToKey(ctx, key);
+ key = ConvertSingleModFlagToKey(key);
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);
@@ -7798,6 +8390,7 @@ ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
}
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+// Formally moved to obsolete section in 1.90.5 in spite of documented as obsolete since 1.87
ImGuiKey ImGui::GetKeyIndex(ImGuiKey key)
{
ImGuiContext& g = *GImGui;
@@ -7816,11 +8409,13 @@ static const char* const GKeyNames[] =
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
+ "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
"Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
"Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
"Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
"Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
"KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
+ "AppBack", "AppForward",
"GamepadStart", "GamepadBack",
"GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
"GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
@@ -7834,10 +8429,12 @@ IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
const char* ImGui::GetKeyName(ImGuiKey key)
{
- ImGuiContext& g = *GImGui;
+ if (key == ImGuiKey_None)
+ return "None";
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
- IM_ASSERT((IsNamedKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
+ IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
#else
+ ImGuiContext& g = *GImGui;
if (IsLegacyKey(key))
{
if (g.IO.KeyMap[key] == -1)
@@ -7846,28 +8443,34 @@ const char* ImGui::GetKeyName(ImGuiKey key)
key = (ImGuiKey)g.IO.KeyMap[key];
}
#endif
- if (key == ImGuiKey_None)
- return "None";
if (key & ImGuiMod_Mask_)
- key = ConvertSingleModFlagToKey(&g, key);
+ key = ConvertSingleModFlagToKey(key);
if (!IsNamedKey(key))
return "Unknown";
return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
}
-// ImGuiMod_Shortcut is translated to either Ctrl or Super.
-void ImGui::GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size)
+// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
+// Lifetime of return value: valid until next call to same function.
+const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
{
ImGuiContext& g = *GImGui;
- if (key_chord & ImGuiMod_Shortcut)
- key_chord = ConvertShortcutMod(key_chord);
- ImFormatString(out_buf, (size_t)out_buf_size, "%s%s%s%s%s",
+
+ const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
+ if (IsModKey(key))
+ key_chord &= ~GetModForModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
+ ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s",
(key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
(key_chord & ImGuiMod_Shift) ? "Shift+" : "",
(key_chord & ImGuiMod_Alt) ? "Alt+" : "",
- (key_chord & ImGuiMod_Super) ? (g.IO.ConfigMacOSXBehaviors ? "Cmd+" : "Super+") : "",
- GetKeyName((ImGuiKey)(key_chord & ~ImGuiMod_Mask_)));
+ (key_chord & ImGuiMod_Super) ? "Super+" : "",
+ (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
+ size_t len;
+ if (key == ImGuiKey_None && key_chord != 0)
+ if ((len = strlen(g.TempKeychordName)) != 0) // Remove trailing '+'
+ g.TempKeychordName[len - 1] = 0;
+ return g.TempKeychordName;
}
// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
@@ -7934,19 +8537,24 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
{
routing_entry = &rt->Entries[old_routing_idx];
+ routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
- routing_entry->RoutingNext = ImGuiKeyOwner_None;
+ routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
routing_entry->RoutingNextScore = 255;
- if (routing_entry->RoutingCurr == ImGuiKeyOwner_None)
+ if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
continue;
rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
// Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
+ // This is the result of previous frame's SetShortcutRouting() call.
if (routing_entry->Mods == g.IO.KeyMods)
{
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
- if (owner_data->OwnerCurr == ImGuiKeyOwner_None)
+ if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
+ {
owner_data->OwnerCurr = routing_entry->RoutingCurr;
+ //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
+ }
}
}
@@ -7962,7 +8570,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
{
ImGuiContext& g = *GImGui;
- return (owner_id != ImGuiKeyOwner_None && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
+ return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
}
ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
@@ -7976,12 +8584,10 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
ImGuiContext& g = *GImGui;
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
ImGuiKeyRoutingData* routing_data;
- if (key_chord & ImGuiMod_Shortcut)
- key_chord = ConvertShortcutMod(key_chord);
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
if (key == ImGuiKey_None)
- key = ConvertSingleModFlagToKey(&g, mods);
+ key = ConvertSingleModFlagToKey(mods);
IM_ASSERT(IsNamedKey(key));
// Get (in the majority of case, the linked list will have one element so this should be 2 reads.
@@ -8004,20 +8610,18 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
}
// Current score encoding (lower is highest priority):
-// - 0: ImGuiInputFlags_RouteGlobalHigh
-// - 1: ImGuiInputFlags_RouteFocused (if item active)
-// - 2: ImGuiInputFlags_RouteGlobal
+// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
+// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active)
+// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
-// - 254: ImGuiInputFlags_RouteGlobalLow
+// - 254: ImGuiInputFlags_RouteGlobal
// - 255: never route
// 'flags' should include an explicit routing policy
-static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputFlags flags)
+static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
{
+ ImGuiContext& g = *GImGui;
if (flags & ImGuiInputFlags_RouteFocused)
{
- ImGuiContext& g = *GImGui;
- ImGuiWindow* focused = g.NavWindow;
-
// ActiveID gets top priority
// (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
if (owner_id != 0 && g.ActiveId == owner_id)
@@ -8029,65 +8633,136 @@ static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputF
// - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best)
// Assuming only WindowA is submitting a routing request,
// - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
- if (focused != NULL && focused->RootWindow == location->RootWindow)
- for (int next_score = 3; focused != NULL; next_score++)
- {
- if (focused == location)
- {
- IM_ASSERT(next_score < 255);
- return next_score;
- }
- focused = (focused->RootWindow != focused) ? focused->ParentWindow : NULL; // FIXME: This could be later abstracted as a focus path
- }
+ // This essentially follow the window->ParentWindowForFocusRoute chain.
+ if (focus_scope_id == 0)
+ return 255;
+ for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
+ if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
+ return 3 + index_in_focus_path;
return 255;
}
-
- // ImGuiInputFlags_RouteGlobalHigh is default, so calls without flags are not conditional
- if (flags & ImGuiInputFlags_RouteGlobal)
- return 2;
- if (flags & ImGuiInputFlags_RouteGlobalLow)
+ else if (flags & ImGuiInputFlags_RouteActive)
+ {
+ if (owner_id != 0 && g.ActiveId == owner_id)
+ return 1;
+ return 255;
+ }
+ else if (flags & ImGuiInputFlags_RouteGlobal)
+ {
+ if (flags & ImGuiInputFlags_RouteOverActive)
+ return 0;
+ if (flags & ImGuiInputFlags_RouteOverFocused)
+ return 2;
return 254;
+ }
+ IM_ASSERT(0);
return 0;
}
+// We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
+// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
+static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
+{
+ // Mimic 'ignore_char_inputs' logic in InputText()
+ ImGuiContext& g = *GImGui;
+
+ // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
+ ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
+ const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
+ if (ignore_char_inputs)
+ return false;
+
+ // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
+ ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
+ return g.KeysMayBeCharInput.TestBit(key);
+}
+
// Request a desired route for an input chord (key + mods).
// Return true if the route is available this frame.
// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
// (Conceptually this does a "Submit for next frame" + "Test for current frame".
// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
-// - Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
-// - Using 'owner_id == ImGuiKeyOwner_None': allows disabling/locking a shortcut.
-bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
+bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
{
ImGuiContext& g = *GImGui;
- if ((flags & ImGuiInputFlags_RouteMask_) == 0)
- flags |= ImGuiInputFlags_RouteGlobalHigh; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
+ if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
+ flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
else
- IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used
+ IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
+ IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
+ if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused))
+ IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
+
+ // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
+ key_chord = FixupKeyChord(key_chord);
+
+ // [DEBUG] Debug break requested by user
+ if (g.DebugBreakInShortcutRouting == key_chord)
+ IM_DEBUG_BREAK();
if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
if (g.NavWindow == NULL)
return false;
- if (flags & ImGuiInputFlags_RouteAlways)
- return true;
- const int score = CalcRoutingScore(g.CurrentWindow, owner_id, flags);
+ // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
+ if (flags & ImGuiInputFlags_RouteAlways)
+ {
+ IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
+ return true;
+ }
+
+ // Specific culling when there's an active item.
+ if (g.ActiveId != 0 && g.ActiveId != owner_id)
+ {
+ if (flags & ImGuiInputFlags_RouteActive)
+ return false;
+
+ // Cull shortcuts with no modifiers when it could generate a character.
+ // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
+ // but Shortcut(Ctrl+G) should generally trigger when InputText() is active.
+ // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
+ // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
+ if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
+ {
+ IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
+ return false;
+ }
+
+ // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
+ if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
+ {
+ ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
+ if (key == ImGuiKey_None)
+ key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
+ if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
+ return false;
+ }
+ }
+
+ // Where do we evaluate route for?
+ ImGuiID focus_scope_id = g.CurrentFocusScopeId;
+ if (flags & ImGuiInputFlags_RouteFromRootWindow)
+ focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
+
+ const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
+ IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
if (score == 255)
return false;
// Submit routing for NEXT frame (assuming score is sufficient)
// FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <).
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
- const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
//const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
if (score < routing_data->RoutingNextScore)
{
- routing_data->RoutingNext = routing_id;
+ routing_data->RoutingNext = owner_id;
routing_data->RoutingNextScore = (ImU8)score;
}
// Return routing state for CURRENT frame
- return routing_data->RoutingCurr == routing_id;
+ if (routing_data->RoutingCurr == owner_id)
+ IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
+ return routing_data->RoutingCurr == owner_id;
}
// Currently unused by core (but used by tests)
@@ -8095,6 +8770,7 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI
bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
{
const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
+ key_chord = FixupKeyChord(key_chord);
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
return routing_data->RoutingCurr == routing_id;
}
@@ -8118,11 +8794,11 @@ bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
{
- return IsKeyPressed(key, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);
+ return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
}
// Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
-bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
+bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
{
const ImGuiKeyData* key_data = GetKeyData(key);
if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
@@ -8131,13 +8807,28 @@ bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
if (t < 0.0f)
return false;
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
+ if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
+ flags |= ImGuiInputFlags_Repeat;
bool pressed = (t == 0.0f);
- if (!pressed && ((flags & ImGuiInputFlags_Repeat) != 0))
+ if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
{
float repeat_delay, repeat_rate;
GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
+ if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
+ {
+ // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
+ // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
+ ImGuiContext& g = *GImGui;
+ double key_pressed_time = g.Time - t + 0.00001f;
+ if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
+ pressed = false;
+ if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
+ pressed = false;
+ if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
+ pressed = false;
+ }
}
if (!pressed)
return false;
@@ -8177,10 +8868,10 @@ bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
{
- return IsMouseClicked(button, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);
+ return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
}
-bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags)
+bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
@@ -8189,7 +8880,7 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInput
const float t = g.IO.MouseDownDuration[button];
if (t < 0.0f)
return false;
- IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
+ IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
@@ -8223,6 +8914,13 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
}
+bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
+ return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
+}
+
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
@@ -8242,9 +8940,8 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c
if (clip)
rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
- // Expand for touch input
- const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
- if (!rect_for_touch.Contains(g.IO.MousePos))
+ // Hit testing, expanded for touch input
+ if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
return false;
return true;
}
@@ -8275,6 +8972,17 @@ ImVec2 ImGui::GetMousePos()
return g.IO.MousePos;
}
+// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
+// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
+void ImGui::TeleportMousePos(const ImVec2& pos)
+{
+ ImGuiContext& g = *GImGui;
+ g.IO.MousePos = g.IO.MousePosPrev = pos;
+ g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
+ g.IO.WantSetMousePos = true;
+ //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
+}
+
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
{
@@ -8410,7 +9118,9 @@ static void ImGui::UpdateKeyboardInputs()
GetKeyData(ImGuiMod_Super)->Down = io.KeySuper;
}
}
+#endif
+ // Import legacy ImGuiNavInput_ io inputs and convert to gamepad keys
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)
@@ -8433,7 +9143,6 @@ static void ImGui::UpdateKeyboardInputs()
MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);
#undef NAV_MAP_KEY
}
-#endif
#endif
// Update aliases
@@ -8442,15 +9151,20 @@ static void ImGui::UpdateKeyboardInputs()
UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
- // Synchronize io.KeyMods and io.KeyXXX values.
+ // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
// - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
// - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
// So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
+ const ImGuiKeyChord prev_key_mods = io.KeyMods;
io.KeyMods = GetMergedModsFromKeys();
io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
+ if (prev_key_mods != io.KeyMods)
+ g.LastKeyModsChangeTime = g.Time;
+ if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
+ g.LastKeyModsChangeFromNoneTime = g.Time;
// Clear gamepad data if disabled
if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
@@ -8466,6 +9180,14 @@ static void ImGui::UpdateKeyboardInputs()
ImGuiKeyData* key_data = &io.KeysData[i];
key_data->DownDurationPrev = key_data->DownDuration;
key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
+ if (key_data->DownDuration == 0.0f)
+ {
+ ImGuiKey key = (ImGuiKey)(ImGuiKey_KeysData_OFFSET + i);
+ if (IsKeyboardKey(key))
+ g.LastKeyboardKeyPressTime = g.Time;
+ else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
+ g.LastKeyboardKeyPressTime = g.Time;
+ }
}
// Update keys/input owner (named keys only): one entry per key
@@ -8475,10 +9197,11 @@ static void ImGui::UpdateKeyboardInputs()
ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
owner_data->OwnerCurr = owner_data->OwnerNext;
if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
- owner_data->OwnerNext = ImGuiKeyOwner_None;
+ owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore
}
+ // Update key routing (for e.g. shortcuts)
UpdateKeyRoutingTable(&g.KeysRoutingTable);
}
@@ -8496,7 +9219,7 @@ static void ImGui::UpdateMouseInputs()
// Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
if (IsMousePosValid(&io.MousePos))
- io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos);
+ io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
// If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
@@ -8504,11 +9227,17 @@ static void ImGui::UpdateMouseInputs()
else
io.MouseDelta = ImVec2(0.0f, 0.0f);
+ // Update stationary timer.
+ // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
+ const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
+ const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
+ g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
+ //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
+
// If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
g.NavDisableMouseHover = false;
- io.MousePosPrev = io.MousePos;
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
{
io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
@@ -8624,8 +9353,8 @@ void ImGui::UpdateMouseWheel()
}
ImVec2 wheel;
- wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_None) ? g.IO.MouseWheelH : 0.0f;
- wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_None) ? g.IO.MouseWheel : 0.0f;
+ wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
+ wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
@@ -8645,8 +9374,8 @@ void ImGui::UpdateMouseWheel()
{
const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
SetWindowPos(window, window->Pos + offset, 0);
- window->Size = ImFloor(window->Size * scale);
- window->SizeFull = ImFloor(window->SizeFull * scale);
+ window->Size = ImTrunc(window->Size * scale);
+ window->SizeFull = ImTrunc(window->SizeFull * scale);
}
return;
}
@@ -8682,15 +9411,17 @@ void ImGui::UpdateMouseWheel()
{
LockWheelingWindow(window, wheel.x);
float max_step = window->InnerRect.GetWidth() * 0.67f;
- float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
+ float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step));
SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
+ g.WheelingWindowScrolledFrame = g.FrameCount;
}
if (do_scroll[ImGuiAxis_Y])
{
LockWheelingWindow(window, wheel.y);
float max_step = window->InnerRect.GetHeight() * 0.67f;
- float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
+ float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step));
SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
+ g.WheelingWindowScrolledFrame = g.FrameCount;
}
}
}
@@ -8710,7 +9441,7 @@ void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
static const char* GetInputSourceName(ImGuiInputSource source)
{
- const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Clipboard" };
+ const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);
return input_source_names[source];
}
@@ -8723,8 +9454,8 @@ static const char* GetMouseSourceName(ImGuiMouseSource source)
static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
{
ImGuiContext& g = *GImGui;
- if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }
- if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseWheel.MouseSource)); return; }
+ if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; }
+ if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; }
if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }
if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
@@ -8756,6 +9487,8 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
if (e->Type == ImGuiInputEventType_MousePos)
{
+ if (g.IO.WantSetMousePos)
+ continue;
// Trickling Rule: Stop processing queued events if we already handled a mouse button change
ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
@@ -8859,8 +9592,8 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
{
- if (!IsNamedKeyOrModKey(key))
- return ImGuiKeyOwner_None;
+ if (!IsNamedKeyOrMod(key))
+ return ImGuiKeyOwner_NoOwner;
ImGuiContext& g = *GImGui;
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
@@ -8868,7 +9601,7 @@ ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
- return ImGuiKeyOwner_None;
+ return ImGuiKeyOwner_NoOwner;
return owner_id;
}
@@ -8879,7 +9612,7 @@ ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
{
- if (!IsNamedKeyOrModKey(key))
+ if (!IsNamedKeyOrMod(key))
return true;
ImGuiContext& g = *GImGui;
@@ -8898,7 +9631,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
{
if (owner_data->LockThisFrame)
return false;
- if (owner_data->OwnerCurr != ImGuiKeyOwner_None)
+ if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
return false;
}
@@ -8912,10 +9645,11 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
// - SetKeyOwner(..., Any or None, Lock) : set lock
void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
{
- IM_ASSERT(IsNamedKeyOrModKey(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)
- IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
-
ImGuiContext& g = *GImGui;
+ IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)
+ IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
+ //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
+
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
@@ -8932,7 +9666,6 @@ void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, I
if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
- if (key_chord & ImGuiMod_Shortcut) { SetKeyOwner(ImGuiMod_Shortcut, owner_id, flags); }
if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
}
@@ -8957,18 +9690,17 @@ void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
}
}
-bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
+// This is the only public API until we expose owner_id versions of the API as replacements.
+bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
+{
+ return IsKeyChordPressed(key_chord, 0, ImGuiInputFlags_None);
+}
+
+// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
+bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
{
ImGuiContext& g = *GImGui;
-
- // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
- if ((flags & ImGuiInputFlags_RouteMask_) == 0)
- flags |= ImGuiInputFlags_RouteFocused;
- if (!SetShortcutRouting(key_chord, owner_id, flags))
- return false;
-
- if (key_chord & ImGuiMod_Shortcut)
- key_chord = ConvertShortcutMod(key_chord);
+ key_chord = FixupKeyChord(key_chord);
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
if (g.IO.KeyMods != mods)
return false;
@@ -8976,12 +9708,77 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags
// Special storage location for mods
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
if (key == ImGuiKey_None)
- key = ConvertSingleModFlagToKey(&g, mods);
-
- if (!IsKeyPressed(key, owner_id, (flags & (ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateMask_))))
+ key = ConvertSingleModFlagToKey(mods);
+ if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
return false;
- IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
+ return true;
+}
+void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasShortcut;
+ g.NextItemData.Shortcut = key_chord;
+ g.NextItemData.ShortcutFlags = flags;
+}
+
+void ImGui::ItemHandleShortcut(ImGuiID id)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
+ IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
+
+ if (flags & ImGuiInputFlags_Tooltip)
+ {
+ g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
+ g.LastItemData.Shortcut = g.NextItemData.Shortcut;
+ }
+ if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
+ return;
+
+ // FIXME: Generalize Activation queue?
+ g.NavActivateId = id; // Will effectively disable clipping.
+ g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
+ //if (g.ActiveId == 0 || g.ActiveId == id)
+ g.NavActivateDownId = g.NavActivatePressedId = id;
+ NavHighlightActivated(id);
+}
+
+bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
+{
+ return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
+}
+
+bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
+{
+ //ImGuiContext& g = *GImGui;
+ //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
+
+ // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
+ if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
+ flags |= ImGuiInputFlags_RouteFocused;
+
+ // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
+ // Effectively makes Shortcut() always input-owner aware.
+ if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
+ owner_id = GetRoutingIdFromOwnerId(owner_id);
+
+ // Submit route
+ if (!SetShortcutRouting(key_chord, flags, owner_id))
+ return false;
+
+ // Default repeat behavior for Shortcut()
+ // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
+ if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
+ flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
+
+ if (!IsKeyChordPressed(key_chord, flags, owner_id))
+ return false;
+
+ // Claim mods during the press
+ SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
+
+ IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
return true;
}
@@ -8990,24 +9787,26 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags
// [SECTION] ERROR CHECKING
//-----------------------------------------------------------------------------
-// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
+// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
+// Called by IMGUI_CHECKVERSION().
// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
-// If this triggers you have an issue:
-// - Most commonly: mismatched headers and compiled code version.
-// - Or: mismatched configuration #define, compilation settings, packing pragma etc.
-// The configuration settings mentioned in imconfig.h must be set for all compilation units involved with Dear ImGui,
-// which is way it is required you put them in your imconfig file (and not just before including imgui.h).
-// Otherwise it is possible that different compilation units would see different structure layout
+// If this triggers you have mismatched headers and compiled code versions.
+// - It could be because of a build issue (using new headers with old compiled code)
+// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
+// THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
+// Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
+// Otherwise it is possible that different compilation units would see different structure layout.
+// If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
{
bool error = false;
if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
- if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
+ if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
- if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
- if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
- if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
- if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
+ if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
+ if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
+ if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
+ if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
return !error;
}
@@ -9262,12 +10061,120 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!");
}
+//-----------------------------------------------------------------------------
+// [SECTION] ITEM SUBMISSION
+//-----------------------------------------------------------------------------
+// - KeepAliveID()
+// - ItemAdd()
+//-----------------------------------------------------------------------------
+
+// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
+void ImGui::KeepAliveID(ImGuiID id)
+{
+ ImGuiContext& g = *GImGui;
+ if (g.ActiveId == id)
+ g.ActiveIdIsAlive = id;
+ if (g.ActiveIdPreviousFrame == id)
+ g.ActiveIdPreviousFrameIsAlive = true;
+}
+
+// Declare item bounding box for clipping and interaction.
+// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
+// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
+// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
+bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ // Set item data
+ // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
+ g.LastItemData.ID = id;
+ g.LastItemData.Rect = bb;
+ g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
+ g.LastItemData.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
+ g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
+ // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
+
+ if (id != 0)
+ {
+ KeepAliveID(id);
+
+ // Directional navigation processing
+ // Runs prior to clipping early-out
+ // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
+ // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
+ // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
+ // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
+ // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
+ // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
+ // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
+ // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
+ if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav))
+ {
+ // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
+ window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
+ if (g.NavId == id || g.NavAnyRequest)
+ if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
+ if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
+ NavProcessItem();
+ }
+
+ if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasShortcut)
+ ItemHandleShortcut(id);
+ }
+
+ // Lightweight clear of SetNextItemXXX data.
+ g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
+ g.NextItemData.ItemFlags = ImGuiItemFlags_None;
+
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ if (id != 0)
+ IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
+#endif
+
+ // Clipping test
+ // (this is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value)
+ //const bool is_clipped = IsClippedEx(bb, id);
+ //if (is_clipped)
+ // return false;
+ // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
+ const bool is_rect_visible = bb.Overlaps(window->ClipRect);
+ if (!is_rect_visible)
+ if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
+ if (!g.LogEnabled)
+ return false;
+
+ // [DEBUG]
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+ if (id != 0)
+ {
+ if (id == g.DebugLocateId)
+ DebugLocateItemResolveWithLastItem();
+
+ // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
+ // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
+ // READ THE FAQ: https://dearimgui.com/faq
+ IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!");
+ }
+ //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
+ //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0)
+ // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
+#endif
+
+ // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
+ if (is_rect_visible)
+ g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
+ if (IsMouseHoveringRect(bb.Min, bb.Max))
+ g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
+ return true;
+}
+
//-----------------------------------------------------------------------------
// [SECTION] LAYOUT
//-----------------------------------------------------------------------------
// - ItemSize()
-// - ItemAdd()
// - SameLine()
// - GetCursorScreenPos()
// - SetCursorScreenPos()
@@ -9298,6 +10205,7 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
// Advance cursor given item size for layout.
// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
+// THIS IS IN THE PERFORMANCE CRITICAL PATH.
void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
{
ImGuiContext& g = *GImGui;
@@ -9317,8 +10225,8 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
//if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
window->DC.CursorPosPrevLine.y = line_y1;
- window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
- window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
+ window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
+ window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
@@ -9334,89 +10242,10 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
SameLine();
}
-// Declare item bounding box for clipping and interaction.
-// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
-// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
-bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
-{
- ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
-
- // Set item data
- // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
- g.LastItemData.ID = id;
- g.LastItemData.Rect = bb;
- g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
- g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags;
- g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
-
- // Directional navigation processing
- if (id != 0)
- {
- KeepAliveID(id);
-
- // Runs prior to clipping early-out
- // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
- // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
- // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
- // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
- // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
- // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
- // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
- // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
- if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav))
- {
- window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
- if (g.NavId == id || g.NavAnyRequest)
- if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
- if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
- NavProcessItem();
- }
-
- // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
- // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
- // READ THE FAQ: https://dearimgui.com/faq
- IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!");
- }
- g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
-
-#ifdef IMGUI_ENABLE_TEST_ENGINE
- if (id != 0)
- IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
-#endif
-
- // Clipping test
- // (FIXME: This is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value)
- //const bool is_clipped = IsClippedEx(bb, id);
- //if (is_clipped)
- // return false;
- const bool is_rect_visible = bb.Overlaps(window->ClipRect);
- if (!is_rect_visible)
- if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId))
- if (!g.LogEnabled)
- return false;
-
- // [DEBUG]
-#ifndef IMGUI_DISABLE_DEBUG_TOOLS
- if (id != 0 && id == g.DebugLocateId)
- DebugLocateItemResolveWithLastItem();
-#endif
- //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
- //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0)
- // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
-
- // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
- if (is_rect_visible)
- g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
- if (IsMouseHoveringRect(bb.Min, bb.Max))
- g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
- return true;
-}
-
// Gets back to previous line and continue with horizontal layout
// offset_from_start_x == 0 : follow right after previous item
// offset_from_start_x != 0 : align to specified x position (relative to window/group left)
-// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
+// spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
// spacing_w >= 0 : enforce spacing amount
void ImGui::SameLine(float offset_from_start_x, float spacing_w)
{
@@ -9450,9 +10279,6 @@ ImVec2 ImGui::GetCursorScreenPos()
return window->DC.CursorPos;
}
-// 2022/08/05: Setting cursor position also extend boundaries (via modifying CursorMaxPos) used to compute window size, group size etc.
-// I believe this was is a judicious choice but it's probably being relied upon (it has been the case since 1.31 and 1.50)
-// It would be sane if we requested user to use SetCursorPos() + Dummy(ImVec2(0,0)) to extend CursorMaxPos...
void ImGui::SetCursorScreenPos(const ImVec2& pos)
{
ImGuiWindow* window = GetCurrentWindow();
@@ -9549,14 +10375,18 @@ void ImGui::PushMultiItemsWidths(int components, float w_full)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
+ IM_ASSERT(components > 0);
const ImGuiStyle& style = g.Style;
- const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
- const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
- window->DC.ItemWidthStack.push_back(w_item_last);
- for (int i = 0; i < components - 2; i++)
- window->DC.ItemWidthStack.push_back(w_item_one);
- window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one;
+ float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
+ float prev_split = w_items;
+ for (int i = components - 1; i > 0; i--)
+ {
+ float next_split = IM_TRUNC(w_items * i / components);
+ window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
+ prev_split = next_split;
+ }
+ window->DC.ItemWidth = ImMax(prev_split, 1.0f);
g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
}
@@ -9583,7 +10413,7 @@ float ImGui::CalcItemWidth()
float region_max_x = GetContentRegionMaxAbs().x;
w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
}
- w = IM_FLOOR(w);
+ w = IM_TRUNC(w);
return w;
}
@@ -9644,10 +10474,8 @@ ImVec2 ImGui::GetContentRegionMax()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
- if (window->DC.CurrentColumns || g.CurrentTable)
- mx.x = window->WorkRect.Max.x - window->Pos.x;
- return mx;
+ ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
+ return mx - window->Pos;
}
// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
@@ -9655,9 +10483,7 @@ ImVec2 ImGui::GetContentRegionMaxAbs()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImVec2 mx = window->ContentRegionRect.Max;
- if (window->DC.CurrentColumns || g.CurrentTable)
- mx.x = window->WorkRect.Max.x;
+ ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
return mx;
}
@@ -9692,6 +10518,7 @@ void ImGui::BeginGroup()
ImGuiGroupData& group_data = g.GroupStack.back();
group_data.WindowID = window->ID;
group_data.BackupCursorPos = window->DC.CursorPos;
+ group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
group_data.BackupIndent = window->DC.Indent;
group_data.BackupGroupOffset = window->DC.GroupOffset;
@@ -9699,6 +10526,7 @@ void ImGui::BeginGroup()
group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
+ group_data.BackupIsSameLine = window->DC.IsSameLine;
group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
group_data.EmitItem = true;
@@ -9725,11 +10553,13 @@ void ImGui::EndGroup()
ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
window->DC.CursorPos = group_data.BackupCursorPos;
+ window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
window->DC.Indent = group_data.BackupIndent;
window->DC.GroupOffset = group_data.BackupGroupOffset;
window->DC.CurrLineSize = group_data.BackupCurrLineSize;
window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
+ window->DC.IsSameLine = group_data.BackupIsSameLine;
if (g.LogEnabled)
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
@@ -9770,7 +10600,8 @@ void ImGui::EndGroup()
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
g.GroupStack.pop_back();
- //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
+ if (g.DebugShowGroupRects)
+ window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
}
@@ -9809,7 +10640,7 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
}
scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
}
- scroll[axis] = IM_FLOOR(ImMax(scroll[axis], 0.0f));
+ scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f));
if (!window->Collapsed && !window->SkipItems)
scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);
}
@@ -9864,7 +10695,7 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
{
if (can_be_fully_visible_x)
- SetScrollFromPosX(window, ImFloor((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
+ SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
else
SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
}
@@ -9879,7 +10710,7 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
{
if (can_be_fully_visible_y)
- SetScrollFromPosY(window, ImFloor((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
+ SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
else
SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
}
@@ -9964,7 +10795,7 @@ void ImGui::SetScrollY(float scroll_y)
void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
{
IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
- window->ScrollTarget.x = IM_FLOOR(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
+ window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
window->ScrollTargetCenterRatio.x = center_x_ratio;
window->ScrollTargetEdgeSnapDist.x = 0.0f;
}
@@ -9972,7 +10803,7 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x
void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
{
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
- window->ScrollTarget.y = IM_FLOOR(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
+ window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
window->ScrollTargetCenterRatio.y = center_y_ratio;
window->ScrollTargetEdgeSnapDist.y = 0.0f;
}
@@ -10024,31 +10855,40 @@ bool ImGui::BeginTooltip()
return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
}
+bool ImGui::BeginItemTooltip()
+{
+ if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
+ return false;
+ return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
+}
+
bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
{
ImGuiContext& g = *GImGui;
if (g.DragDropWithinSource || g.DragDropWithinTarget)
{
- // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor)
- // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
- // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do.
+ // Drag and Drop tooltips are positioning differently than other tooltips:
+ // - offset visibility to increase visibility around mouse.
+ // - never clamp within outer viewport boundary.
+ // We call SetNextWindowPos() to enforce position and disable clamping.
+ // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
//ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
- ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
+ ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale;
SetNextWindowPos(tooltip_pos);
SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
//PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
- tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
+ tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
}
char window_name[16];
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
- if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
+ if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious)
if (ImGuiWindow* window = FindWindowByName(window_name))
if (window->Active)
{
// Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
- SetWindowHiddendAndSkipItemsForCurrentFrame(window);
+ SetWindowHiddenAndSkipItemsForCurrentFrame(window);
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
}
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
@@ -10067,14 +10907,6 @@ void ImGui::EndTooltip()
End();
}
-void ImGui::SetTooltipV(const char* fmt, va_list args)
-{
- if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None))
- return;
- TextV(fmt, args);
- EndTooltip();
-}
-
void ImGui::SetTooltip(const char* fmt, ...)
{
va_list args;
@@ -10083,6 +10915,32 @@ void ImGui::SetTooltip(const char* fmt, ...)
va_end(args);
}
+void ImGui::SetTooltipV(const char* fmt, va_list args)
+{
+ if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
+ return;
+ TextV(fmt, args);
+ EndTooltip();
+}
+
+// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
+// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
+void ImGui::SetItemTooltip(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
+ SetTooltipV(fmt, args);
+ va_end(args);
+}
+
+void ImGui::SetItemTooltipV(const char* fmt, va_list args)
+{
+ if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
+ SetTooltipV(fmt, args);
+}
+
+
//-----------------------------------------------------------------------------
// [SECTION] POPUPS
//-----------------------------------------------------------------------------
@@ -10180,7 +11038,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
popup_ref.PopupId = id;
popup_ref.Window = NULL;
- popup_ref.BackupNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type).
+ popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type).
popup_ref.OpenFrameCount = g.FrameCount;
popup_ref.OpenParentId = parent_window->IDStack.back();
popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
@@ -10193,17 +11051,23 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
}
else
{
- // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui
- // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing
- // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand.
- if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
+ // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
+ // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
+ // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
+ // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
+ bool keep_existing = false;
+ if (g.OpenPopupStack[current_stack_size].PopupId == id)
+ if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
+ keep_existing = true;
+ if (keep_existing)
{
+ // No reopen
g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
}
else
{
- // Close child popups if any, then flag popup for open/reopen
- ClosePopupToLevel(current_stack_size, false);
+ // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
+ ClosePopupToLevel(current_stack_size, true);
g.OpenPopupStack.push_back(popup_ref);
}
@@ -10223,6 +11087,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to
return;
// Don't close our own child popup windows.
+ //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "", restore_focus_to_window_under_popup);
int popup_count_to_keep = 0;
if (ref_window)
{
@@ -10233,14 +11098,15 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to
if (!popup.Window)
continue;
IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
- if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
- continue;
// Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
- // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
- // Window -> Popup1 -> Popup2 -> Popup3
+ // - Clicking/Focusing Window2 won't close Popup1:
+ // Window -> Popup1 -> Window2(Ref)
+ // - Clicking/focusing Popup1 will close Popup2 and Popup3:
+ // Window -> Popup1(Ref) -> Popup2 -> Popup3
// - Each popups may contain child windows, which is why we compare ->RootWindow!
// Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
+ // We step through every popup from bottom to top to validate their position relative to reference window.
bool ref_window_is_descendent_of_popup = false;
for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
@@ -10278,18 +11144,19 @@ void ImGui::ClosePopupsExceptModals()
void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
{
ImGuiContext& g = *GImGui;
- IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
+ IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
// Trim open popup stack
- ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
- ImGuiWindow* popup_backup_nav_window = g.OpenPopupStack[remaining].BackupNavWindow;
+ ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
g.OpenPopupStack.resize(remaining);
- if (restore_focus_to_window_under_popup)
+ // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case)
+ if (restore_focus_to_window_under_popup && prev_popup.Window)
{
- ImGuiWindow* focus_window = (popup_window && popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : popup_backup_nav_window;
- if (focus_window && !focus_window->WasActive && popup_window)
+ ImGuiWindow* popup_window = prev_popup.Window;
+ ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
+ if (focus_window && !focus_window->WasActive)
FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
else
FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
@@ -10339,7 +11206,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
char name[20];
if (flags & ImGuiWindowFlags_ChildMenu)
- ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuDepth); // Recycle windows based on depth
else
ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
@@ -10348,6 +11215,8 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
EndPopup();
+ //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
+
return is_open;
}
@@ -10365,7 +11234,9 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
}
// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
-// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here.
+// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
+// - *p_open set back to false in BeginPopupModal() when popup is not open.
+// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
ImGuiContext& g = *GImGui;
@@ -10374,6 +11245,8 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla
if (!IsPopupOpen(id, ImGuiPopupFlags_None))
{
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
+ if (p_open && *p_open)
+ *p_open = false;
return false;
}
@@ -10605,15 +11478,20 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
}
if (window->Flags & ImGuiWindowFlags_Tooltip)
{
- // Position tooltip (always follows mouse)
- float sc = g.Style.MouseCursorScale;
- ImVec2 ref_pos = NavCalcPreferredRefPos();
+ // Position tooltip (always follows mouse + clamp within outer boundaries)
+ // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position.
+ // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin()
+ IM_ASSERT(g.CurrentWindow == window);
+ const float scale = g.Style.MouseCursorScale;
+ const ImVec2 ref_pos = NavCalcPreferredRefPos();
+ const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale;
ImRect r_avoid;
if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
else
- r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
- return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
+ r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
+ //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
+ return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
}
IM_ASSERT(0);
return window->Pos;
@@ -10634,11 +11512,19 @@ void ImGui::SetNavWindow(ImGuiWindow* window)
{
IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "");
g.NavWindow = window;
+ g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
}
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
NavUpdateAnyRequestFlag();
}
+void ImGui::NavHighlightActivated(ImGuiID id)
+{
+ ImGuiContext& g = *GImGui;
+ g.NavHighlightActivatedId = id;
+ g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
+}
+
void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
{
ImGuiContext& g = *GImGui;
@@ -10652,7 +11538,7 @@ void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id
IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
g.NavId = id;
g.NavLayer = nav_layer;
- g.NavFocusScopeId = focus_scope_id;
+ SetNavFocusScope(focus_scope_id);
g.NavWindow->NavLastIds[nav_layer] = id;
g.NavWindow->NavRectRel[nav_layer] = rect_rel;
@@ -10674,7 +11560,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
g.NavId = id;
g.NavLayer = nav_layer;
- g.NavFocusScopeId = g.CurrentFocusScopeId;
+ SetNavFocusScope(g.CurrentFocusScopeId);
window->NavLastIds[nav_layer] = id;
if (g.LastItemData.ID == id)
window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
@@ -10853,6 +11739,11 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
result->FocusScopeId = g.CurrentFocusScopeId;
result->InFlags = g.LastItemData.InFlags;
result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
+ if (result->InFlags & ImGuiItemFlags_HasSelectionUserData)
+ {
+ IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
+ result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
+ }
}
// True when current work location may be scrolled horizontally when moving left / right.
@@ -10865,7 +11756,7 @@ void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
}
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
-// This is called after LastItemData is set.
+// This is called after LastItemData is set, but NextItemData is also still valid.
static void ImGui::NavProcessItem()
{
ImGuiContext& g = *GImGui;
@@ -10901,23 +11792,26 @@ static void ImGui::NavProcessItem()
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
{
- const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0;
- if (is_tabbing)
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
{
- NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
- }
- else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
- {
- ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
- if (NavScoreItem(result))
- NavApplyItemToResult(result);
+ const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
+ if (is_tabbing)
+ {
+ NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
+ }
+ else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
+ {
+ ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
+ if (NavScoreItem(result))
+ NavApplyItemToResult(result);
- // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
- const float VISIBLE_RATIO = 0.70f;
- if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
- if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
- if (NavScoreItem(&g.NavMoveResultLocalVisible))
- NavApplyItemToResult(&g.NavMoveResultLocalVisible);
+ // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
+ const float VISIBLE_RATIO = 0.70f;
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
+ if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
+ if (NavScoreItem(&g.NavMoveResultLocalVisible))
+ NavApplyItemToResult(&g.NavMoveResultLocalVisible);
+ }
}
}
@@ -10927,8 +11821,14 @@ static void ImGui::NavProcessItem()
if (g.NavWindow != window)
SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
g.NavLayer = window->DC.NavLayerCurrent;
+ SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
g.NavFocusScopeId = g.CurrentFocusScopeId;
g.NavIdIsAlive = true;
+ if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData)
+ {
+ IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
+ g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
+ }
window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
}
}
@@ -10945,8 +11845,12 @@ void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flag
ImGuiContext& g = *GImGui;
if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
+ {
if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
return;
+ if (g.NavFocusScopeId != g.CurrentFocusScopeId)
+ return;
+ }
// - Can always land on an item when using API call.
// - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
@@ -11007,7 +11911,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM
ImGuiContext& g = *GImGui;
IM_ASSERT(g.NavWindow != NULL);
- if (move_flags & ImGuiNavMoveFlags_Tabbing)
+ if (move_flags & ImGuiNavMoveFlags_IsTabbing)
move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
g.NavMoveSubmitted = g.NavMoveScoringItems = true;
@@ -11017,7 +11921,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM
g.NavMoveFlags = move_flags;
g.NavMoveScrollFlags = scroll_flags;
g.NavMoveForwardToNextFrame = false;
- g.NavMoveKeyMods = g.IO.KeyMods;
+ g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
g.NavMoveResultLocal.Clear();
g.NavMoveResultLocalVisible.Clear();
g.NavMoveResultOther.Clear();
@@ -11034,6 +11938,19 @@ void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
NavUpdateAnyRequestFlag();
}
+// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere
+void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data)
+{
+ ImGuiContext& g = *GImGui;
+ g.NavMoveScoringItems = false;
+ g.LastItemData.ID = tree_node_data->ID;
+ g.LastItemData.InFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
+ g.LastItemData.NavRect = tree_node_data->NavRect;
+ NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
+ NavClearPreferredPosForAxis(ImGuiAxis_Y);
+ NavUpdateAnyRequestFlag();
+}
+
void ImGui::NavMoveRequestCancel()
{
ImGuiContext& g = *GImGui;
@@ -11094,6 +12011,7 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
{
ImGuiWindow* prev_nav_window = g.NavWindow;
g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
+ g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
if (prev_nav_window)
IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
}
@@ -11133,7 +12051,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
if (window->Flags & ImGuiWindowFlags_NoNavInputs)
{
g.NavId = 0;
- g.NavFocusScopeId = window->NavRootFocusScopeId;
+ SetNavFocusScope(window->NavRootFocusScopeId);
return;
}
@@ -11152,7 +12070,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
else
{
g.NavId = window->NavLastIds[0];
- g.NavFocusScopeId = window->NavRootFocusScopeId;
+ SetNavFocusScope(window->NavRootFocusScopeId);
}
}
@@ -11160,7 +12078,10 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.NavWindow;
- if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window)
+ const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
+
+ // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
+ if ((g.NavDisableHighlight || !g.NavDisableMouseHover || !window) && !activated_shortcut)
{
// Mouse (we need a fallback in case the mouse becomes invalid after being used)
// The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.
@@ -11171,16 +12092,21 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
else
{
// When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
+ ImRect ref_rect;
+ if (activated_shortcut)
+ ref_rect = g.LastItemData.NavRect;
+ else
+ ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
+
// Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
- ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
{
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
- rect_rel.Translate(window->Scroll - next_scroll);
+ ref_rect.Translate(window->Scroll - next_scroll);
}
- ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
+ ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight()));
ImGuiViewport* viewport = GetMainViewport();
- return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
+ return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
}
}
@@ -11273,10 +12199,10 @@ static void ImGui::NavUpdate()
g.NavActivateFlags = ImGuiActivateFlags_None;
if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
{
- const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate));
- const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false)));
- const bool input_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Enter)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput));
- const bool input_pressed = input_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Enter, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, false)));
+ const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
+ const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
+ const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner));
+ const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, 0, ImGuiKeyOwner_NoOwner)));
if (g.ActiveId == 0 && activate_pressed)
{
g.NavActivateId = g.NavId;
@@ -11290,13 +12216,22 @@ static void ImGui::NavUpdate()
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
g.NavActivateDownId = g.NavId;
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
+ {
g.NavActivatePressedId = g.NavId;
+ NavHighlightActivated(g.NavId);
+ }
}
if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
g.NavDisableHighlight = true;
if (g.NavActivateId != 0)
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
+ // Highlight
+ if (g.NavHighlightActivatedTimer > 0.0f)
+ g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
+ if (g.NavHighlightActivatedTimer == 0.0f)
+ g.NavHighlightActivatedId = 0;
+
// Process programmatic activation request
// FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
if (g.NavNextActivateId != 0)
@@ -11323,9 +12258,9 @@ static void ImGui::NavUpdate()
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
{
if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
- SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
+ SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
- SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
+ SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
}
// *Normal* Manual scroll with LStick
@@ -11335,9 +12270,9 @@ static void ImGui::NavUpdate()
const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
if (scroll_dir.x != 0.0f && window->ScrollbarX)
- SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
+ SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
if (scroll_dir.y != 0.0f)
- SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
+ SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
}
}
@@ -11351,11 +12286,7 @@ static void ImGui::NavUpdate()
// Update mouse position if requested
// (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
- {
- io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
- io.WantSetMousePos = true;
- //IMGUI_DEBUG_LOG_IO("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
- }
+ TeleportMousePos(NavCalcPreferredRefPos());
// [DEBUG]
g.NavScoringDebugCount = 0;
@@ -11389,6 +12320,8 @@ void ImGui::NavInitRequestApplyResult()
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
+ if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
+ g.NavLastValidSelectionUserData = result->SelectionUserData;
if (g.NavInitRequestFromMove)
NavRestoreHighlightAfterMove();
}
@@ -11443,10 +12376,10 @@ void ImGui::NavUpdateCreateMoveRequest()
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
{
const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
- if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; }
- if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Down; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; }
+ if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; }
}
g.NavMoveClipDir = g.NavMoveDir;
g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
@@ -11526,7 +12459,7 @@ void ImGui::NavUpdateCreateMoveRequest()
scoring_rect.TranslateY(scoring_rect_offset_y);
if (g.NavMoveSubmitted)
NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
- IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
+ IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
//GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
//if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
}
@@ -11542,22 +12475,22 @@ void ImGui::NavUpdateCreateTabbingRequest()
if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
return;
- const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
+ const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
if (!tab_pressed)
return;
// Initiate tabbing request
// (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
- // Initially this was designed to use counters and modulo arithmetic, but that could not work with unsubmitted items (list clipper). Instead we use a strategy close to other move requests.
// See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
if (nav_keyboard_active)
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1;
else
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
+ ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
- NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
+ NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
g.NavTabbingCounter = -1;
}
@@ -11574,7 +12507,7 @@ void ImGui::NavMoveRequestApplyResult()
ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
// Tabbing forward wrap
- if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && result == NULL)
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
result = &g.NavTabbingResultFirst;
@@ -11582,9 +12515,9 @@ void ImGui::NavMoveRequestApplyResult()
const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
if (result == NULL)
{
- if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing)
- g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight;
- if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0)
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
+ g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight;
+ if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
NavRestoreHighlightAfterMove();
NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
@@ -11620,12 +12553,19 @@ void ImGui::NavMoveRequestApplyResult()
{
IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
g.NavWindow = result->Window;
+ g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
}
- if (g.ActiveId != result->ID)
+
+ // Clear active id unless requested not to
+ // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
+ // so this is mostly provided as a gateway for further experiments (see #1418, #2890)
+ if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
ClearActiveID();
- if (g.NavId != result->ID)
+
+ // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
+ // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
+ if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
{
- // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
g.NavJustMovedToId = result->ID;
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
@@ -11635,32 +12575,32 @@ void ImGui::NavMoveRequestApplyResult()
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
+ if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
+ g.NavLastValidSelectionUserData = result->SelectionUserData;
// Restore last preferred position for current axis
// (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
- if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) == 0)
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
{
preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
}
- // Tabbing: Activates Inputable or Focus non-Inputable
- if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable))
- {
- g.NavNextActivateId = result->ID;
- g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState;
- g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight;
- }
+ // Tabbing: Activates Inputable, otherwise only Focus
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0)
+ g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
// Activate
if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
{
g.NavNextActivateId = result->ID;
g.NavNextActivateFlags = ImGuiActivateFlags_None;
+ if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
+ g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
}
// Enable nav highlight
- if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0)
+ if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
NavRestoreHighlightAfterMove();
}
@@ -11673,7 +12613,7 @@ static void ImGui::NavUpdateCancelRequest()
ImGuiContext& g = *GImGui;
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
- if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, ImGuiKeyOwner_None)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, ImGuiKeyOwner_None)))
+ if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
return;
IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
@@ -11687,15 +12627,14 @@ static void ImGui::NavUpdateCancelRequest()
NavRestoreLayer(ImGuiNavLayer_Main);
NavRestoreHighlightAfterMove();
}
- else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
+ else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
{
// Exit child window
- ImGuiWindow* child_window = g.NavWindow;
- ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
+ ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
+ ImGuiWindow* parent_window = child_window->ParentWindow;
IM_ASSERT(child_window->ChildId != 0);
- ImRect child_rect = child_window->Rect();
FocusWindow(parent_window);
- SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect));
+ SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
NavRestoreHighlightAfterMove();
}
else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
@@ -11723,10 +12662,10 @@ static float ImGui::NavUpdatePageUpPageDown()
if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
return 0.0f;
- const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_None);
- const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_None);
- const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat);
- const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat);
+ const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
+ const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
+ const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
+ const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
return 0.0f;
@@ -11736,9 +12675,9 @@ static float ImGui::NavUpdatePageUpPageDown()
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY)
{
// Fallback manual-scroll when window has no navigable item
- if (IsKeyPressed(ImGuiKey_PageUp, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat))
+ if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
- else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat))
+ else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
else if (home_pressed)
SetScrollY(window, 0.0f);
@@ -11755,14 +12694,14 @@ static float ImGui::NavUpdatePageUpPageDown()
nav_scoring_rect_offset_y = -page_offset_y;
g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
g.NavMoveClipDir = ImGuiDir_Up;
- g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
}
else if (IsKeyPressed(ImGuiKey_PageDown, true))
{
nav_scoring_rect_offset_y = +page_offset_y;
g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
g.NavMoveClipDir = ImGuiDir_Down;
- g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
+ g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
}
else if (home_pressed)
{
@@ -11927,12 +12866,13 @@ static void ImGui::NavUpdateWindowing()
}
// Start CTRL+Tab or Square+L/R window selection
+ // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing");
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
- const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways);
- const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways);
- const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, 0, ImGuiInputFlags_None);
+ const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
+ const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
+ const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_None);
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
@@ -11943,7 +12883,7 @@ static void ImGui::NavUpdateWindowing()
g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
- // Register ownership of our mods. Using ImGuiInputFlags_RouteGlobalHigh in the Shortcut() calls instead would probably be correct but may have more side-effects.
+ // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.
if (keyboard_next_window || keyboard_prev_window)
SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
}
@@ -11989,28 +12929,33 @@ static void ImGui::NavUpdateWindowing()
}
// Keyboard: Press and Release ALT to toggle menu layer
- // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.
- // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
- if (nav_keyboard_active && IsKeyPressed(ImGuiMod_Alt, ImGuiKeyOwner_None))
- {
- g.NavWindowingToggleLayer = true;
- g.NavInputSource = ImGuiInputSource_Keyboard;
- }
+ const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
+ for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
+ if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
+ {
+ g.NavWindowingToggleLayer = true;
+ g.NavWindowingToggleKey = windowing_toggle_key;
+ g.NavInputSource = ImGuiInputSource_Keyboard;
+ break;
+ }
if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
{
// We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
// We cancel toggling nav layer when other modifiers are pressed. (See #4439)
+ // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
// We cancel toggling nav layer if an owner has claimed the key.
- if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_None) == false)
+ if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
+ g.NavWindowingToggleLayer = false;
+ if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
g.NavWindowingToggleLayer = false;
- // Apply layer toggle on release
+ // Apply layer toggle on Alt release
// Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
- if (IsKeyReleased(ImGuiMod_Alt) && g.NavWindowingToggleLayer)
+ if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
apply_toggle_layer = true;
- if (!IsKeyDown(ImGuiMod_Alt))
+ if (!IsKeyDown(g.NavWindowingToggleKey))
g.NavWindowingToggleLayer = false;
}
@@ -12028,7 +12973,7 @@ static void ImGui::NavUpdateWindowing()
const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
g.NavDisableMouseHover = true;
- ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaPos);
+ ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
{
ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
@@ -12160,6 +13105,14 @@ void ImGui::ClearDragDrop()
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
}
+bool ImGui::BeginTooltipHidden()
+{
+ ImGuiContext& g = *GImGui;
+ bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
+ SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
+ return ret;
+}
+
// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
// If the item has an identifier:
// - This assume/require the item to be activated (typically via ButtonBehavior).
@@ -12216,7 +13169,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
// Rely on keeping other window->LastItemXXX fields intact.
source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
KeepAliveID(source_id);
- bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id);
+ bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags);
if (is_hovered && g.IO.MouseClicked[mouse_button])
{
SetActiveID(source_id, window);
@@ -12240,6 +13193,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
source_drag_active = true;
}
+ IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
if (source_drag_active)
{
if (!g.DragDropActive)
@@ -12262,12 +13216,13 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
{
// Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
// We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
- bool ret = BeginTooltip();
+ bool ret;
+ if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
+ ret = BeginTooltipHidden();
+ else
+ ret = BeginTooltip();
IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
IM_UNUSED(ret);
-
- if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
- SetWindowHiddendAndSkipItemsForCurrentFrame(g.CurrentWindow);
}
if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
@@ -12354,15 +13309,16 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
if (window->SkipItems)
return false;
- IM_ASSERT(g.DragDropWithinTarget == false);
+ IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
g.DragDropTargetRect = bb;
+ g.DragDropTargetClipRect = window->ClipRect; // May want to be overriden by user depending on use case?
g.DragDropTargetId = id;
g.DragDropWithinTarget = true;
return true;
}
// We don't use BeginDragDropTargetCustom() and duplicate its code because:
-// 1) we use LastItemRectHoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
+// 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
bool ImGui::BeginDragDropTarget()
@@ -12388,8 +13344,9 @@ bool ImGui::BeginDragDropTarget()
if (g.DragDropPayload.SourceId == id)
return false;
- IM_ASSERT(g.DragDropWithinTarget == false);
+ IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
g.DragDropTargetRect = display_rect;
+ g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
g.DragDropTargetId = id;
g.DragDropWithinTarget = true;
return true;
@@ -12404,7 +13361,6 @@ bool ImGui::IsDragDropPayloadBeingAccepted()
const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
ImGuiPayload& payload = g.DragDropPayload;
IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
@@ -12428,7 +13384,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop
payload.Preview = was_accepted_previously;
flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
- window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
+ RenderDragDropTargetRect(r, g.DragDropTargetClipRect);
g.DragDropAcceptFrameCount = g.FrameCount;
payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
@@ -12439,10 +13395,20 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop
return &payload;
}
-// FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
-void ImGui::RenderDragDropTargetRect(const ImRect& bb)
+// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
+void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect)
{
- GetWindowDrawList()->AddRect(bb.Min - ImVec2(3.5f, 3.5f), bb.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImRect bb_display = bb;
+ bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
+ bb_display.Expand(3.5f);
+ bool push_clip_rect = !window->ClipRect.Contains(bb_display);
+ if (push_clip_rect)
+ window->DrawList->PushClipRectFullScreen();
+ window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
+ if (push_clip_rect)
+ window->DrawList->PopClipRect();
}
const ImGuiPayload* ImGui::GetDragDropPayload()
@@ -12790,9 +13756,9 @@ ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
{
ImGuiContext& g = *GImGui;
const ImGuiID type_hash = ImHashStr(type_name);
- for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
- if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
- return &g.SettingsHandlers[handler_n];
+ for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
+ if (handler.TypeHash == type_hash)
+ return &handler;
return NULL;
}
@@ -12801,9 +13767,9 @@ void ImGui::ClearIniSettings()
{
ImGuiContext& g = *GImGui;
g.SettingsIniData.clear();
- for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
- if (g.SettingsHandlers[handler_n].ClearAllFn)
- g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
+ for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
+ if (handler.ClearAllFn != NULL)
+ handler.ClearAllFn(&g, &handler);
}
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
@@ -12838,9 +13804,9 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
// Call pre-read handlers
// Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
- for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
- if (g.SettingsHandlers[handler_n].ReadInitFn)
- g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
+ for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
+ if (handler.ReadInitFn != NULL)
+ handler.ReadInitFn(&g, &handler);
void* entry_data = NULL;
ImGuiSettingsHandler* entry_handler = NULL;
@@ -12884,9 +13850,9 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
memcpy(buf, ini_data, ini_size);
// Call post-read handlers
- for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
- if (g.SettingsHandlers[handler_n].ApplyAllFn)
- g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
+ for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
+ if (handler.ApplyAllFn != NULL)
+ handler.ApplyAllFn(&g, &handler);
}
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
@@ -12912,11 +13878,8 @@ const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
g.SettingsDirtyTimer = 0.0f;
g.SettingsIniData.Buf.resize(0);
g.SettingsIniData.Buf.push_back(0);
- for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
- {
- ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
- handler->WriteAllFn(&g, handler, &g.SettingsIniData);
- }
+ for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
+ handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
if (out_size)
*out_size = (size_t)g.SettingsIniData.size();
return g.SettingsIniData.c_str();
@@ -12926,12 +13889,13 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
{
ImGuiContext& g = *GImGui;
-#if !IMGUI_DEBUG_INI_SETTINGS
- // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
- // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
- if (const char* p = strstr(name, "###"))
- name = p;
-#endif
+ if (g.IO.ConfigDebugIniSettings == false)
+ {
+ // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
+ // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
+ if (const char* p = strstr(name, "###"))
+ name = p;
+ }
const size_t name_len = strlen(name);
// Allocate chunk
@@ -12981,8 +13945,8 @@ void ImGui::ClearWindowSettings(const char* name)
static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
{
ImGuiContext& g = *ctx;
- for (int i = 0; i != g.Windows.Size; i++)
- g.Windows[i]->SettingsOffset = -1;
+ for (ImGuiWindow* window : g.Windows)
+ window->SettingsOffset = -1;
g.SettingsWindows.clear();
}
@@ -13007,6 +13971,7 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*,
if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
+ else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); }
}
// Apply to existing windows (if any)
@@ -13027,9 +13992,8 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
// Gather data from windows that were active during this session
// (if a window wasn't opened in this session we preserve its settings)
ImGuiContext& g = *ctx;
- for (int i = 0; i != g.Windows.Size; i++)
+ for (ImGuiWindow* window : g.Windows)
{
- ImGuiWindow* window = g.Windows[i];
if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
continue;
@@ -13042,7 +14006,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
IM_ASSERT(settings->ID == window->ID);
settings->Pos = ImVec2ih(window->Pos);
settings->Size = ImVec2ih(window->SizeFull);
-
+ settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0;
settings->Collapsed = window->Collapsed;
settings->WantDelete = false;
}
@@ -13055,9 +14019,18 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl
continue;
const char* settings_name = settings->GetName();
buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
- buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
- buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
- buf->appendf("Collapsed=%d\n", settings->Collapsed);
+ if (settings->IsChild)
+ {
+ buf->appendf("IsChild=1\n");
+ buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
+ }
+ else
+ {
+ buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
+ buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
+ if (settings->Collapsed)
+ buf->appendf("Collapsed=1\n");
+ }
buf->append("\n");
}
}
@@ -13108,10 +14081,8 @@ static void ImGui::UpdateViewportsNewFrame()
main_viewport->Pos = ImVec2(0.0f, 0.0f);
main_viewport->Size = g.IO.DisplaySize;
- for (int n = 0; n < g.Viewports.Size; n++)
+ for (ImGuiViewportP* viewport : g.Viewports)
{
- ImGuiViewportP* viewport = g.Viewports[n];
-
// Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
@@ -13298,7 +14269,7 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeDat
//-----------------------------------------------------------------------------
// [SECTION] METRICS/DEBUGGER WINDOW
//-----------------------------------------------------------------------------
-// - RenderViewportThumbnail() [Internal]
+// - DebugRenderViewportThumbnail() [Internal]
// - RenderViewportsThumbnails() [Internal]
// - DebugTextEncoding()
// - MetricsHelpMarker() [Internal]
@@ -13329,16 +14300,15 @@ void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP*
ImVec2 off = bb.Min - viewport->Pos * scale;
float alpha_mul = 1.0f;
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
- for (int i = 0; i != g.Windows.Size; i++)
+ for (ImGuiWindow* thumb_window : g.Windows)
{
- ImGuiWindow* thumb_window = g.Windows[i];
if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
continue;
ImRect thumb_r = thumb_window->Rect();
ImRect title_r = thumb_window->TitleBarRect();
- thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale));
- title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height
+ thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale));
+ title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off + ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height
thumb_r.ClipWithFull(bb);
title_r.ClipWithFull(bb);
const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
@@ -13348,6 +14318,8 @@ void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP*
window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
}
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
+ if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
+ window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
}
static void RenderViewportsThumbnails()
@@ -13355,16 +14327,14 @@ static void RenderViewportsThumbnails()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports.
float SCALE = 1.0f / 8.0f;
- ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
- for (int n = 0; n < g.Viewports.Size; n++)
- bb_full.Add(g.Viewports[n]->GetMainRect());
+ ImRect bb_full(g.Viewports[0]->Pos, g.Viewports[0]->Pos + g.Viewports[0]->Size);
ImVec2 p = window->DC.CursorPos;
ImVec2 off = p - bb_full.Min * SCALE;
- for (int n = 0; n < g.Viewports.Size; n++)
+
+ // Draw viewports
+ for (ImGuiViewportP* viewport : g.Viewports)
{
- ImGuiViewportP* viewport = g.Viewports[n];
ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
}
@@ -13374,14 +14344,15 @@ static void RenderViewportsThumbnails()
// Draw an arbitrary US keyboard layout to visualize translated keys
void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
{
- const ImVec2 key_size = ImVec2(35.0f, 35.0f);
- const float key_rounding = 3.0f;
- const ImVec2 key_face_size = ImVec2(25.0f, 25.0f);
- const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f);
- const float key_face_rounding = 2.0f;
- const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f);
+ const float scale = ImGui::GetFontSize() / 13.0f;
+ const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
+ const float key_rounding = 3.0f * scale;
+ const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
+ const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
+ const float key_face_rounding = 2.0f * scale;
+ const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
- const float key_row_offset = 9.0f;
+ const float key_row_offset = 9.0f * scale;
ImVec2 board_min = GetCursorScreenPos();
ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);
@@ -13456,11 +14427,40 @@ void ImGui::DebugTextEncoding(const char* str)
EndTable();
}
+static void DebugFlashStyleColorStop()
+{
+ ImGuiContext& g = *GImGui;
+ if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
+ g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
+ g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
+}
+
+// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
+void ImGui::DebugFlashStyleColor(ImGuiCol idx)
+{
+ ImGuiContext& g = *GImGui;
+ DebugFlashStyleColorStop();
+ g.DebugFlashStyleColorTime = 0.5f;
+ g.DebugFlashStyleColorIdx = idx;
+ g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
+}
+
+void ImGui::UpdateDebugToolFlashStyleColor()
+{
+ ImGuiContext& g = *GImGui;
+ if (g.DebugFlashStyleColorTime <= 0.0f)
+ return;
+ ColorConvertHSVtoRGB(cosf(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z);
+ g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
+ if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
+ DebugFlashStyleColorStop();
+}
+
// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
static void MetricsHelpMarker(const char* desc)
{
ImGui::TextDisabled("(?)");
- if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip())
+ if (ImGui::BeginItemTooltip())
{
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(desc);
@@ -13472,9 +14472,8 @@ static void MetricsHelpMarker(const char* desc)
// [DEBUG] List fonts in a font atlas and display its texture
void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
{
- for (int i = 0; i < atlas->Fonts.Size; i++)
+ for (ImFont* font : atlas->Fonts)
{
- ImFont* font = atlas->Fonts[i];
PushID(font);
DebugNodeFont(font);
PopID();
@@ -13498,8 +14497,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
if (cfg->ShowDebugLog)
ShowDebugLogWindow(&cfg->ShowDebugLog);
- if (cfg->ShowStackTool)
- ShowStackToolWindow(&cfg->ShowStackTool);
+ if (cfg->ShowIDStackTool)
+ ShowIDStackToolWindow(&cfg->ShowIDStackTool);
if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
{
@@ -13507,11 +14506,14 @@ void ImGui::ShowMetricsWindow(bool* p_open)
return;
}
+ // [DEBUG] Clear debug breaks hooks after exactly one cycle.
+ DebugBreakClearData();
+
// Basic info
Text("Dear ImGui %s", GetVersion());
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
- Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations);
+ Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
//SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
Separator();
@@ -13540,8 +14542,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }
else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
- else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } // Note: y1/y2 not always accurate
- else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); }
+ else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate
+ else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); }
else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); }
else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
IM_ASSERT(0);
@@ -13555,7 +14557,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
- else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
+ else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
IM_ASSERT(0);
@@ -13566,34 +14568,24 @@ void ImGui::ShowMetricsWindow(bool* p_open)
// Tools
if (TreeNode("Tools"))
{
- bool show_encoding_viewer = TreeNode("UTF-8 Encoding viewer");
- SameLine();
- MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
- if (show_encoding_viewer)
- {
- static char buf[100] = "";
- SetNextItemWidth(-FLT_MIN);
- InputText("##Text", buf, IM_ARRAYSIZE(buf));
- if (buf[0] != 0)
- DebugTextEncoding(buf);
- TreePop();
- }
-
+ // Debug Break features
// The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
- if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
- DebugStartItemPicker();
+ SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
SameLine();
MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
+ if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
+ DebugStartItemPicker();
+ Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
+
+ SeparatorText("Visualize");
- // Stack Tool is your best friend!
Checkbox("Show Debug Log", &cfg->ShowDebugLog);
SameLine();
MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
- // Stack Tool is your best friend!
- Checkbox("Show Stack Tool", &cfg->ShowStackTool);
+ Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
SameLine();
- MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code.");
+ MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
@@ -13656,11 +14648,26 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Unindent();
}
}
+ Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
+
+ SeparatorText("Validate");
Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
SameLine();
MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
+ Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
+ SameLine();
+ MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
+ if (cfg->ShowTextEncodingViewer)
+ {
+ static char buf[64] = "";
+ SetNextItemWidth(-FLT_MIN);
+ InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
+ if (buf[0] != 0)
+ DebugTextEncoding(buf);
+ }
+
TreePop();
}
@@ -13675,9 +14682,9 @@ void ImGui::ShowMetricsWindow(bool* p_open)
// Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
ImVector& temp_buffer = g.WindowsTempSortBuffer;
temp_buffer.resize(0);
- for (int i = 0; i < g.Windows.Size; i++)
- if (g.Windows[i]->LastFrameActive + 1 >= g.FrameCount)
- temp_buffer.push_back(g.Windows[i]);
+ for (ImGuiWindow* window : g.Windows)
+ if (window->LastFrameActive + 1 >= g.FrameCount)
+ temp_buffer.push_back(window);
struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } };
ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
@@ -13689,44 +14696,44 @@ void ImGui::ShowMetricsWindow(bool* p_open)
// DrawLists
int drawlist_count = 0;
- for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
- drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
+ for (ImGuiViewportP* viewport : g.Viewports)
+ drawlist_count += viewport->DrawDataP.CmdLists.Size;
if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
{
Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
- for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
- {
- ImGuiViewportP* viewport = g.Viewports[viewport_i];
- for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
- for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
- DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
- }
+ for (ImGuiViewportP* viewport : g.Viewports)
+ for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
+ DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
TreePop();
}
// Viewports
if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
{
- Indent(GetTreeNodeToLabelSpacing());
- RenderViewportsThumbnails();
- Unindent(GetTreeNodeToLabelSpacing());
- for (int i = 0; i < g.Viewports.Size; i++)
- DebugNodeViewport(g.Viewports[i]);
+ SetNextItemOpen(true, ImGuiCond_Once);
+ if (TreeNode("Windows Minimap"))
+ {
+ RenderViewportsThumbnails();
+ TreePop();
+ }
+ cfg->HighlightViewportID = 0;
+
+ for (ImGuiViewportP* viewport : g.Viewports)
+ DebugNodeViewport(viewport);
TreePop();
}
// Details for Popups
if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
{
- for (int i = 0; i < g.OpenPopupStack.Size; i++)
+ for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
{
// As it's difficult to interact with tree nodes while popups are open, we display everything inline.
- const ImGuiPopupData* popup_data = &g.OpenPopupStack[i];
- ImGuiWindow* window = popup_data->Window;
- BulletText("PopupID: %08x, Window: '%s' (%s%s), BackupNavWindow '%s', ParentWindow '%s'",
- popup_data->PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
- popup_data->BackupNavWindow ? popup_data->BackupNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
+ ImGuiWindow* window = popup_data.Window;
+ BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
+ popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
+ popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
}
TreePop();
}
@@ -13768,6 +14775,13 @@ void ImGui::ShowMetricsWindow(bool* p_open)
TreePop();
}
+ // Details for TypingSelect
+ if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
+ {
+ DebugNodeTypingSelectState(&g.TypingSelectState);
+ TreePop();
+ }
+
// Details for Docking
#ifdef IMGUI_HAS_DOCK
if (TreeNode("Docking"))
@@ -13792,11 +14806,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("\"%s\"", g.IO.IniFilename);
else
TextUnformatted("");
+ Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
{
- for (int n = 0; n < g.SettingsHandlers.Size; n++)
- BulletText("%s", g.SettingsHandlers[n].TypeName);
+ for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
+ BulletText("\"%s\"", handler.TypeName);
TreePop();
}
if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
@@ -13824,6 +14839,22 @@ void ImGui::ShowMetricsWindow(bool* p_open)
TreePop();
}
+ // Settings
+ if (TreeNode("Memory allocations"))
+ {
+ ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
+ Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
+ if (SmallButton("GC now")) { g.GcCompactAll = true; }
+ Text("Recent frames with allocations:");
+ int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);
+ for (int n = buf_size - 1; n >= 0; n--)
+ {
+ ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
+ BulletText("Frame %06d: %+3d ( %2d malloc, %2d free )%s", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount, (n == 0) ? " (most recent)" : "");
+ }
+ TreePop();
+ }
+
if (TreeNode("Inputs"))
{
Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
@@ -13834,7 +14865,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
#else
- struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
+ struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
//Text("Legacy raw:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } }
#endif
Text("Keys down:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
@@ -13859,6 +14890,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
Text("Mouse wheel: %.1f", io.MouseWheel);
+ Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
Unindent();
@@ -13876,41 +14908,44 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("KEY OWNERS");
{
Indent();
- if (BeginListBox("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6)))
- {
+ if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
{
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
- if (owner_data->OwnerCurr == ImGuiKeyOwner_None)
+ if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
continue;
Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
DebugLocateItemOnHover(owner_data->OwnerCurr);
}
- EndListBox();
- }
+ EndChild();
Unindent();
}
Text("SHORTCUT ROUTING");
+ SameLine();
+ MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
{
Indent();
- if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6)))
- {
+ if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
{
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
{
- char key_chord_name[64];
ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
- GetKeyChordName(key | routing_data->Mods, key_chord_name, IM_ARRAYSIZE(key_chord_name));
- Text("%s: 0x%08X", key_chord_name, routing_data->RoutingCurr);
+ ImGuiKeyChord key_chord = key | routing_data->Mods;
+ Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
DebugLocateItemOnHover(routing_data->RoutingCurr);
+ if (g.IO.ConfigDebugIsDebuggerPresent)
+ {
+ SameLine();
+ if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
+ g.DebugBreakInShortcutRouting = key_chord;
+ }
idx = routing_data->NextEntryIndex;
}
}
- EndListBox();
- }
+ EndChild();
Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
Unindent();
}
@@ -13934,7 +14969,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
- Text("HoverDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverDelayId, g.HoverDelayTimer, g.HoverDelayClearTimer);
+ Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
DebugLocateItemOnHover(g.DragDropPayload.SourceId);
Unindent();
@@ -13945,11 +14980,20 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
DebugLocateItemOnHover(g.NavId);
Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
+ Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
Text("NavActivateFlags: %04X", g.NavActivateFlags);
Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
+ Text("NavFocusRoute[] = ");
+ for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
+ {
+ const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
+ SameLine(0.0f, 0.0f);
+ Text("0x%08X/", focus_scope.ID);
+ SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
+ }
Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
Unindent();
@@ -13959,9 +15003,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
// Overlay: Display windows Rectangles and Begin Order
if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
{
- for (int n = 0; n < g.Windows.Size; n++)
+ for (ImGuiWindow* window : g.Windows)
{
- ImGuiWindow* window = g.Windows[n];
if (!window->WasActive)
continue;
ImDrawList* draw_list = GetForegroundDrawList(window);
@@ -14018,21 +15061,90 @@ void ImGui::ShowMetricsWindow(bool* p_open)
End();
}
+void ImGui::DebugBreakClearData()
+{
+ // Those fields are scattered in their respective subsystem to stay in hot-data locations
+ ImGuiContext& g = *GImGui;
+ g.DebugBreakInWindow = 0;
+ g.DebugBreakInTable = 0;
+ g.DebugBreakInShortcutRouting = ImGuiKey_None;
+}
+
+void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
+{
+ if (!BeginItemTooltip())
+ return;
+ Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
+ Separator();
+ TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.");
+ Separator();
+ TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
+ EndTooltip();
+}
+
+// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
+// In order to reduce interferences with the contents we are trying to debug into.
+bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ ImGuiContext& g = *GImGui;
+ const ImGuiID id = window->GetID(label);
+ const ImVec2 label_size = CalcTextSize(label, NULL, true);
+ ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
+ ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
+
+ const ImRect bb(pos, pos + size);
+ ItemSize(size, 0.0f);
+ if (!ItemAdd(bb, id))
+ return false;
+
+ // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
+ bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
+ bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
+ DebugBreakButtonTooltip(false, description_of_location);
+
+ ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
+ ImVec4 hsv;
+ ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
+ ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
+
+ RenderNavHighlight(bb, id);
+ RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
+ RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
+
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
+ return pressed;
+}
+
// [DEBUG] Display contents of Columns
void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
{
if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
return;
BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
- for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
- BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
+ for (ImGuiOldColumnData& column : columns->Columns)
+ BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
TreePop();
}
+static void FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
+{
+ union { void* ptr; int integer; } tex_id_opaque;
+ memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
+ if (sizeof(tex_id) >= sizeof(void*))
+ ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
+ else
+ ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
+}
+
// [DEBUG] Display contents of ImDrawList
-void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label)
+void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
{
ImGuiContext& g = *GImGui;
+ IM_UNUSED(viewport); // Used in docking branch
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
int cmd_count = draw_list->CmdBuffer.Size;
if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
@@ -14064,10 +15176,11 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list,
continue;
}
+ char texid_desc[20];
+ FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TextureId);
char buf[300];
- ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
- pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
- pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
+ pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
@@ -14147,8 +15260,8 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co
// Draw bounding boxes
if (show_aabb)
{
- out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
- out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
+ out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
+ out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
}
out_draw_list->Flags = backup_flags;
}
@@ -14173,7 +15286,7 @@ void ImGui::DebugNodeFont(ImFont* font)
SetNextItemWidth(GetFontSize() * 8);
DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
SameLine(); MetricsHelpMarker(
- "Note than the default embedded font is NOT meant to be scaled.\n\n"
+ "Note that the default embedded font is NOT meant to be scaled.\n\n"
"Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
"You may oversample them to get some flexibility with scaling. "
"You can also render at multiple sizes and select which one to use at runtime.\n\n"
@@ -14259,11 +15372,8 @@ void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
{
if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
return;
- for (int n = 0; n < storage->Data.Size; n++)
- {
- const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
+ for (const ImGuiStorage::ImGuiStoragePair& p : storage->Data)
BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
- }
TreePop();
}
@@ -14310,8 +15420,12 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
{
+ ImGuiContext& g = *GImGui;
SetNextItemOpen(true, ImGuiCond_Once);
- if (TreeNode("viewport0", "Viewport #%d", 0))
+ bool open = TreeNode("viewport0", "Viewport #%d", 0);
+ if (IsItemHovered())
+ g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
+ if (open)
{
ImGuiWindowFlags flags = viewport->Flags;
BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
@@ -14321,9 +15435,8 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "",
(flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
(flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "");
- for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
- for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
- DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
+ for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
+ DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
TreePop();
}
}
@@ -14350,8 +15463,11 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
if (window->MemoryCompacted)
TextDisabled("Note: some memory buffers have been compacted/freed.");
+ if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
+ g.DebugBreakInWindow = window->ID;
+
ImGuiWindowFlags flags = window->Flags;
- DebugNodeDrawList(window, window->DrawList, "DrawList");
+ DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
(flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
@@ -14373,13 +15489,14 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
- if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
- if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
- if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
+ if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
+ if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
+ if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
+ if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
{
- for (int n = 0; n < window->ColumnsStorage.Size; n++)
- DebugNodeColumns(&window->ColumnsStorage[n]);
+ for (ImGuiOldColumns& columns : window->ColumnsStorage)
+ DebugNodeColumns(&columns);
TreePop();
}
DebugNodeStorage(&window->StateStorage, "Storage");
@@ -14445,9 +15562,36 @@ void ImGui::DebugLogV(const char* fmt, va_list args)
const int old_size = g.DebugLogBuf.size();
g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
g.DebugLogBuf.appendfv(fmt, args);
+ g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size);
- g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
+ IMGUI_TEST_ENGINE_LOG("%s", g.DebugLogBuf.begin() + old_size);
+#endif
+}
+
+// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
+static void SameLineOrWrap(const ImVec2& size)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
+ if (window->ClipRect.Contains(ImRect(pos, pos + size)))
+ ImGui::SameLine();
+}
+
+static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
+ SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
+ if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
+ {
+ g.DebugLogAutoDisableFrames = 2;
+ g.DebugLogAutoDisableFlags |= flags;
+ }
+ ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
}
void ImGui::ShowDebugLogWindow(bool* p_open)
@@ -14461,14 +15605,18 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
return;
}
- CheckboxFlags("All", &g.DebugLogFlags, ImGuiDebugLogFlags_EventMask_);
- SameLine(); CheckboxFlags("ActiveId", &g.DebugLogFlags, ImGuiDebugLogFlags_EventActiveId);
- SameLine(); CheckboxFlags("Focus", &g.DebugLogFlags, ImGuiDebugLogFlags_EventFocus);
- SameLine(); CheckboxFlags("Popup", &g.DebugLogFlags, ImGuiDebugLogFlags_EventPopup);
- SameLine(); CheckboxFlags("Nav", &g.DebugLogFlags, ImGuiDebugLogFlags_EventNav);
- SameLine(); if (CheckboxFlags("Clipper", &g.DebugLogFlags, ImGuiDebugLogFlags_EventClipper)) { g.DebugLogClipperAutoDisableFrames = 2; } if (IsItemHovered()) SetTooltip("Clipper log auto-disabled after 2 frames");
- //SameLine(); CheckboxFlags("Selection", &g.DebugLogFlags, ImGuiDebugLogFlags_EventSelection);
- SameLine(); CheckboxFlags("IO", &g.DebugLogFlags, ImGuiDebugLogFlags_EventIO);
+ ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
+ CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
+ SetItemTooltip("(except InputRouting which is spammy)");
+
+ ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
+ ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
+ ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
+ ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
+ ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
+ ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
+ //ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
+ ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
if (SmallButton("Clear"))
{
@@ -14478,7 +15626,10 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
SameLine();
if (SmallButton("Copy"))
SetClipboardText(g.DebugLogBuf.c_str());
- BeginChild("##log", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
+ BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
+
+ const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
+ g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
ImGuiListClipper clipper;
clipper.Begin(g.DebugLogIndex.size());
@@ -14487,10 +15638,10 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
{
const char* line_begin = g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no);
const char* line_end = g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no);
- TextUnformatted(line_begin, line_end);
+ TextUnformatted(line_begin, line_end); // Display line
ImRect text_rect = g.LastItemData.Rect;
if (IsItemHovered())
- for (const char* p = line_begin; p <= line_end - 10; p++)
+ for (const char* p = line_begin; p <= line_end - 10; p++) // Search for 0x???????? identifiers
{
ImGuiID id = 0;
if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1)
@@ -14503,6 +15654,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
p += 10;
}
}
+ g.DebugLogFlags = backup_log_flags;
if (GetScrollY() >= GetScrollMaxY())
SetScrollHereY(1.0f);
EndChild();
@@ -14511,9 +15663,41 @@ void ImGui::ShowDebugLogWindow(bool* p_open)
}
//-----------------------------------------------------------------------------
-// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL)
+// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
//-----------------------------------------------------------------------------
+// Draw a small cross at current CursorPos in current window's DrawList
+void ImGui::DebugDrawCursorPos(ImU32 col)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImVec2 pos = window->DC.CursorPos;
+ window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
+ window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
+}
+
+// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
+void ImGui::DebugDrawLineExtents(ImU32 col)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ float curr_x = window->DC.CursorPos.x;
+ float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
+ float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
+ window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
+ window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
+ window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
+}
+
+// Draw last item rect in ForegroundDrawList (so it is always visible)
+void ImGui::DebugDrawItemRect(ImU32 col)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
+}
+
+// [DEBUG] Locate item position/rectangle given an ID.
static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green
void ImGui::DebugLocateItem(ImGuiID target_id)
@@ -14521,8 +15705,10 @@ void ImGui::DebugLocateItem(ImGuiID target_id)
ImGuiContext& g = *GImGui;
g.DebugLocateId = target_id;
g.DebugLocateFrames = 2;
+ g.DebugBreakInLocateId = false;
}
+// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
{
if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
@@ -14530,11 +15716,24 @@ void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
ImGuiContext& g = *GImGui;
DebugLocateItem(target_id);
GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);
+
+ // Can't easily use a context menu here because it will mess with focus, active id etc.
+ if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
+ {
+ DebugBreakButtonTooltip(false, "in ItemAdd()");
+ if (IsKeyChordPressed(g.DebugBreakKeyChord))
+ g.DebugBreakInLocateId = true;
+ }
}
void ImGui::DebugLocateItemResolveWithLastItem()
{
ImGuiContext& g = *GImGui;
+
+ // [DEBUG] Debug break requested by user
+ if (g.DebugBreakInLocateId)
+ IM_DEBUG_BREAK();
+
ImGuiLastItemData item_data = g.LastItemData;
g.DebugLocateId = 0;
ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
@@ -14546,6 +15745,12 @@ void ImGui::DebugLocateItemResolveWithLastItem()
draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
}
+void ImGui::DebugStartItemPicker()
+{
+ ImGuiContext& g = *GImGui;
+ g.DebugItemPickerActive = true;
+}
+
// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
void ImGui::UpdateDebugToolItemPicker()
{
@@ -14580,13 +15785,13 @@ void ImGui::UpdateDebugToolItemPicker()
EndTooltip();
}
-// [DEBUG] Stack Tool: update queries. Called by NewFrame()
+// [DEBUG] ID Stack Tool: update queries. Called by NewFrame()
void ImGui::UpdateDebugToolStackQueries()
{
ImGuiContext& g = *GImGui;
- ImGuiStackTool* tool = &g.DebugStackTool;
+ ImGuiIDStackTool* tool = &g.DebugIDStackTool;
- // Clear hook when stack tool is not visible
+ // Clear hook when id stack tool is not visible
g.DebugHookIdInfo = 0;
if (g.FrameCount != tool->LastActiveFrame + 1)
return;
@@ -14620,12 +15825,12 @@ void ImGui::UpdateDebugToolStackQueries()
}
}
-// [DEBUG] Stack tool: hooks called by GetID() family functions
+// [DEBUG] ID Stack tool: hooks called by GetID() family functions
void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImGuiStackTool* tool = &g.DebugStackTool;
+ ImGuiIDStackTool* tool = &g.DebugIDStackTool;
// Step 0: stack query
// This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
@@ -14668,7 +15873,7 @@ void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* dat
info->DataType = data_type;
}
-static int StackToolFormatLevelInfo(ImGuiStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
+static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
{
ImGuiStackLevelInfo* info = &tool->Results[n];
ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
@@ -14685,20 +15890,20 @@ static int StackToolFormatLevelInfo(ImGuiStackTool* tool, int n, bool format_for
return ImFormatString(buf, buf_size, "???");
}
-// Stack Tool: Display UI
-void ImGui::ShowStackToolWindow(bool* p_open)
+// ID Stack Tool: Display UI
+void ImGui::ShowIDStackToolWindow(bool* p_open)
{
ImGuiContext& g = *GImGui;
if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize))
SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
- if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
+ if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
{
End();
return;
}
// Display hovered/active status
- ImGuiStackTool* tool = &g.DebugStackTool;
+ ImGuiIDStackTool* tool = &g.DebugIDStackTool;
const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
const ImGuiID active_id = g.ActiveId;
#ifdef IMGUI_ENABLE_TEST_ENGINE
@@ -14714,7 +15919,7 @@ void ImGui::ShowStackToolWindow(bool* p_open)
Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC);
SameLine();
TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*");
- if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiMod_Ctrl) && IsKeyPressed(ImGuiKey_C))
+ if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
{
tool->CopyToClipboardLastTime = (float)g.Time;
char* p = g.TempBuffer.Data;
@@ -14767,7 +15972,7 @@ void ImGui::ShowStackToolWindow(bool* p_open)
void ImGui::ShowMetricsWindow(bool*) {}
void ImGui::ShowFontAtlas(ImFontAtlas*) {}
void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
-void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
+void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
void ImGui::DebugNodeFont(ImFont*) {}
void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
@@ -14780,10 +15985,9 @@ void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
void ImGui::DebugLog(const char*, ...) {}
void ImGui::DebugLogV(const char*, va_list) {}
void ImGui::ShowDebugLogWindow(bool*) {}
-void ImGui::ShowStackToolWindow(bool*) {}
+void ImGui::ShowIDStackToolWindow(bool*) {}
+void ImGui::DebugStartItemPicker() {}
void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
-void ImGui::UpdateDebugToolItemPicker() {}
-void ImGui::UpdateDebugToolStackQueries() {}
#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
diff --git a/extern/src/imgui/imgui.h b/extern/src/imgui/imgui.h
index 8da4ea1b..572cae80 100644
--- a/extern/src/imgui/imgui.h
+++ b/extern/src/imgui/imgui.h
@@ -1,29 +1,34 @@
-// dear imgui, v1.89.6
+// dear imgui, v1.90.7
// (headers)
// Help:
-// - Read FAQ at http://dearimgui.com/faq
-// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
+// - See links below.
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
-// Read imgui.cpp for details, links and comments.
+// - Read top of imgui.cpp for more details, links and comments.
// Resources:
-// - FAQ http://dearimgui.com/faq
-// - Homepage & latest https://github.com/ocornut/imgui
-// - Releases & changelog https://github.com/ocornut/imgui/releases
-// - Gallery https://github.com/ocornut/imgui/issues/6478 (please post your screenshots/video there!)
-// - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
-// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
-// - Issues & support https://github.com/ocornut/imgui/issues
+// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
+// - Homepage ................... https://github.com/ocornut/imgui
+// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
+// - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!)
+// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
+// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
+// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
+// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
+// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
+// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools
+// - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
+// - Issues & support ........... https://github.com/ocornut/imgui/issues
+// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
-// Getting Started?
-// - For first-time users having issues compiling/linking/running or issues loading fonts:
-// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
+// For first-time users having issues compiling/linking/running/loading fonts:
+// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
+// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
-#define IMGUI_VERSION "1.89.6"
-#define IMGUI_VERSION_NUM 18960
+#define IMGUI_VERSION "1.90.7"
+#define IMGUI_VERSION_NUM 19070
#define IMGUI_HAS_TABLE
/*
@@ -33,10 +38,11 @@ Index of this file:
// [SECTION] Forward declarations and basic types
// [SECTION] Dear ImGui end-user API functions
// [SECTION] Flags & Enumerations
+// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
// [SECTION] Helpers: Memory allocations macros, ImVector<>
// [SECTION] ImGuiStyle
// [SECTION] ImGuiIO
-// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
+// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)
// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)
// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont)
@@ -49,7 +55,7 @@ Index of this file:
#pragma once
// Configuration file with compile-time options
-// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system')
+// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system)
#ifdef IMGUI_USER_CONFIG
#include IMGUI_USER_CONFIG
#endif
@@ -69,7 +75,7 @@ Index of this file:
// Define attributes of all API symbols declarations (e.g. for DLL under Windows)
// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h)
-// Using dear imgui via a shared library is not recommended, because we don't guarantee backward nor forward ABI compatibility (also function call overhead, as dear imgui is a call-heavy API)
+// Using dear imgui via a shared library is not recommended: we don't guarantee backward nor forward ABI compatibility + this is a call-heavy library and function call overhead adds up.
#ifndef IMGUI_API
#define IMGUI_API
#endif
@@ -84,10 +90,13 @@ Index of this file:
#endif
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers!
#define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
-#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11
+
+// Check that version and structures layouts are matching between compiled imgui code and caller. Read comments above DebugCheckVersionAndDataLayout() for details.
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.
+// (MSVC provides an equivalent mechanism via SAL Annotations but it would require the macros in a different
+// location. e.g. #include + void myprintf(_Printf_format_string_ const char* format, ...))
#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)
#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1)))
#define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0)))
@@ -115,10 +124,15 @@ Index of this file:
#endif
#if defined(__clang__)
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#if __has_warning("-Wzero-as-null-pointer-constant")
-#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx'
#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
@@ -164,8 +178,9 @@ struct ImGuiViewport; // A Platform Window (always only one in 'ma
// Enumerations
// - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
-// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
-// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
+// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
+// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value)
enum ImGuiMouseSource : int; // -> enum ImGuiMouseSource // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen)
typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling
@@ -178,23 +193,26 @@ typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A
typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling
typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor()
-// Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file)
+// Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file)
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
-// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
-// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
+// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
+// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance
typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build
typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags
typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for InvisibleButton()
+typedef int ImGuiChildFlags; // -> enum ImGuiChildFlags_ // Flags: for BeginChild()
typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc.
typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags
typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo()
typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload()
typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused()
typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc.
+typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for Shortcut(), SetNextItemShortcut()
typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline()
-typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for storage only for now: an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.
+typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.
typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
@@ -234,8 +252,8 @@ typedef unsigned long long ImU64; // 64-bit unsigned integer
// 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)
-typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings.
typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings.
+typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings.
#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16]
typedef ImWchar32 ImWchar;
#else
@@ -250,6 +268,7 @@ typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data);
// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type]
// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type.
+// Add '#define IMGUI_DEFINE_MATH_OPERATORS' in your imconfig.h file to benefit from courtesy maths operators for those types.
IM_MSVC_RUNTIME_CHECKS_OFF
struct ImVec2
{
@@ -293,7 +312,7 @@ namespace ImGui
// Main
IMGUI_API ImGuiIO& GetIO(); // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags)
- IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleCol(), PushStyleVar() to modify style mid-frame!
+ IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame!
IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame().
IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all!
IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData().
@@ -303,7 +322,7 @@ namespace ImGui
IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!
IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc.
IMGUI_API void ShowDebugLogWindow(bool* p_open = NULL); // create Debug Log window. display a simplified log of important dear imgui events.
- IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID.
+ IMGUI_API void ShowIDStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID.
IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information.
IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style)
IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles.
@@ -324,23 +343,33 @@ namespace ImGui
// Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin().
// - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting
// anything to the window. Always call a matching End() for each Begin() call, regardless of its return value!
- // [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu,
- // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function
- // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]
+ // [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions
+ // such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding
+ // BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]
// - Note that the bottom of window stack always contains a window called "Debug".
IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0);
IMGUI_API void End();
// Child Windows
// - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child.
- // - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400).
- // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window.
- // Always call a matching EndChild() for each BeginChild() call, regardless of its return value.
- // [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu,
- // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function
- // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]
- IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0);
- IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0);
+ // - Before 1.90 (November 2023), the "ImGuiChildFlags child_flags = 0" parameter was "bool border = false".
+ // This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Border == true.
+ // Consider updating your old code:
+ // BeginChild("Name", size, false) -> Begin("Name", size, 0); or Begin("Name", size, ImGuiChildFlags_None);
+ // BeginChild("Name", size, true) -> Begin("Name", size, ImGuiChildFlags_Border);
+ // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)):
+ // == 0.0f: use remaining parent window size for this axis.
+ // > 0.0f: use specified size for this axis.
+ // < 0.0f: right/bottom-align to specified distance from available content boundaries.
+ // - Specifying ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY makes the sizing automatic based on child contents.
+ // Combining both ImGuiChildFlags_AutoResizeX _and_ ImGuiChildFlags_AutoResizeY defeats purpose of a scrolling region and is NOT recommended.
+ // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting
+ // anything to the window. Always call a matching EndChild() for each BeginChild() call, regardless of its return value.
+ // [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions
+ // such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding
+ // BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]
+ IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);
+ IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);
IMGUI_API void EndChild();
// Windows Utilities
@@ -348,10 +377,10 @@ namespace ImGui
IMGUI_API bool IsWindowAppearing();
IMGUI_API bool IsWindowCollapsed();
IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options.
- IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ!
+ IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives
- IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API)
- IMGUI_API ImVec2 GetWindowSize(); // get current window size
+ IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (note: it is unlikely you need to use this. Consider using current layout pos instead, GetCursorScreenPos())
+ IMGUI_API ImVec2 GetWindowSize(); // get current window size (note: it is unlikely you need to use this. Consider using GetCursorScreenPos() and e.g. GetContentRegionAvail() instead)
IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x)
IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y)
@@ -359,7 +388,7 @@ namespace ImGui
// - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin).
IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc.
IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin()
- IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints.
+ IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use 0.0f or FLT_MAX if you don't want limits. Use -1 for both min and max of same axis to preserve current size (which itself is a constraint). Use callback to apply non-trivial programmatic constraints.
IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin()
IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin()
IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin()
@@ -426,16 +455,28 @@ namespace ImGui
IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API
IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList
IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList
- IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList
+ IMGUI_API ImU32 GetColorU32(ImU32 col, float alpha_mul = 1.0f); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList
IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in.
- // Cursor / Layout
+ // Layout cursor positioning
// - By "cursor" we mean the current output position.
// - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down.
// - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget.
// - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API:
- // Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos()
- // Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions.
+ // - Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward.
+ // - Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos()
+ // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates.
+ IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (prefer using this, also more useful to work with ImDrawList API).
+ IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates
+ IMGUI_API ImVec2 GetCursorPos(); // [window-local] cursor position in window coordinates (relative to window position)
+ IMGUI_API float GetCursorPosX(); // [window-local] "
+ IMGUI_API float GetCursorPosY(); // [window-local] "
+ IMGUI_API void SetCursorPos(const ImVec2& local_pos); // [window-local] "
+ IMGUI_API void SetCursorPosX(float local_x); // [window-local] "
+ IMGUI_API void SetCursorPosY(float local_y); // [window-local] "
+ IMGUI_API ImVec2 GetCursorStartPos(); // [window-local] initial cursor position, in window coordinates
+
+ // Other layout functions
IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator.
IMGUI_API void SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f); // call between widgets or groups to layout them horizontally. X position given in window coordinates.
IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in a horizontal-layout context.
@@ -445,15 +486,6 @@ namespace ImGui
IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0
IMGUI_API void BeginGroup(); // lock horizontal starting position
IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
- IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position)
- IMGUI_API float GetCursorPosX(); // (some functions are using window-relative coordinates, such as: GetCursorPos, GetCursorStartPos, GetContentRegionMax, GetWindowContentRegion* etc.
- IMGUI_API float GetCursorPosY(); // other functions such as GetCursorScreenPos or everything in ImDrawList::
- IMGUI_API void SetCursorPos(const ImVec2& local_pos); // are using the main, absolute coordinate system.
- IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.)
- IMGUI_API void SetCursorPosY(float local_y); //
- IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates
- IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (useful to work with ImDrawList API). generally top-left == GetMainViewport()->Pos == (0,0) in single viewport mode, and bottom-right == GetMainViewport()->Pos+Size == io.DisplaySize in single-viewport mode.
- IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates
IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item)
IMGUI_API float GetTextLineHeight(); // ~ FontSize
IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text)
@@ -500,7 +532,7 @@ namespace ImGui
// - Most widgets return true when the value has been changed or when pressed/selected
// - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.
IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)); // button
- IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text
+ IMGUI_API bool SmallButton(const char* label); // button with (FramePadding.y == 0) to easily embed within text
IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape
IMGUI_API bool Checkbox(const char* label, bool* v);
@@ -513,8 +545,10 @@ namespace ImGui
// Widgets: Images
// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
- IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0));
- IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));
+ // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
+ // - Note that Image() may add +2.0f to provided size if a border is visible, ImageButton() adds style.FramePadding*2.0f to provided size.
+ IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0));
+ IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));
// Widgets: Combo Box (Dropdown)
// - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items.
@@ -523,7 +557,7 @@ namespace ImGui
IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true!
IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1);
IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0"
- IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1);
+ IMGUI_API bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1);
// Widgets: Drag Sliders
// - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
@@ -611,9 +645,9 @@ namespace ImGui
IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3);
IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);
IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);
- IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired.
+ IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushID(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired.
IMGUI_API void TreePush(const void* ptr_id); // "
- IMGUI_API void TreePop(); // ~ Unindent()+PopId()
+ IMGUI_API void TreePop(); // ~ Unindent()+PopID()
IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode
IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop().
IMGUI_API bool CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header.
@@ -626,22 +660,22 @@ namespace ImGui
IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper.
// Widgets: List Boxes
- // - This is essentially a thin wrapper to using BeginChild/EndChild with some stylistic changes.
- // - The BeginListBox()/EndListBox() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() or any items.
+ // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
+ // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items.
// - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analoguous to how Combos are created.
// - Choose frame width: size.x > 0.0f: custom / size.x < 0.0f or -FLT_MIN: right-align / size.x = 0.0f (default): use current ItemWidth
// - Choose frame height: size.y > 0.0f: custom / size.y < 0.0f or -FLT_MIN: bottom-align / size.y = 0.0f (default): arbitrary default height which can fit ~7 items
IMGUI_API bool BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region
IMGUI_API void EndListBox(); // only call EndListBox() if BeginListBox() returned true!
IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1);
- IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1);
+ IMGUI_API bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1);
// Widgets: Data Plotting
// - Consider using ImPlot (https://github.com/epezent/implot) which is much better!
IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));
IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
- IMGUI_API void PlotHistogram(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));
+ IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));
// Widgets: Value() Helpers.
// - Those are merely shortcut to calling Text() with a format string. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace)
@@ -665,12 +699,22 @@ namespace ImGui
IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL
// Tooltips
- // - Tooltip are windows following the mouse. They do not take focus away.
- IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items).
- IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip() returns true!
- IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip().
+ // - Tooltips are windows following the mouse. They do not take focus away.
+ // - A tooltip window can contain items of any types.
+ // - SetTooltip() is more or less a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom (with a subtlety that it discard any previously submitted tooltip)
+ IMGUI_API bool BeginTooltip(); // begin/append a tooltip window.
+ IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true!
+ IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip().
IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);
+ // Tooltips: helpers for showing a tooltip when hovering an item
+ // - BeginItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip) && BeginTooltip())' idiom.
+ // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) { SetTooltip(...); }' idiom.
+ // - Where 'ImGuiHoveredFlags_ForTooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.
+ IMGUI_API bool BeginItemTooltip(); // begin/append a tooltip window if preceding item was hovered.
+ IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceding item was hovered. override any previous call to SetTooltip().
+ IMGUI_API void SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);
+
// Popups, Modals
// - They block normal mouse hovering detection (and therefore most mouse interactions) behind them.
// - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
@@ -679,9 +723,7 @@ namespace ImGui
// - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered().
// - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack.
// This is sometimes leading to confusing mistakes. May rework this in the future.
-
- // Popups: begin/end functions
- // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window.
+ // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards if returned true. ImGuiWindowFlags are forwarded to the window.
// - BeginPopupModal(): block every interaction behind the window, cannot be closed by user, add a dimming background, has a title bar.
IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it.
IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it.
@@ -731,12 +773,10 @@ namespace ImGui
// TableNextColumn() will automatically wrap-around into the next row if needed.
// - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column!
// - Summary of possible call flow:
- // --------------------------------------------------------------------------------------------------------
- // TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1") // OK
- // TableNextRow() -> TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK
- // TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK: TableNextColumn() automatically gets to next row!
- // TableNextRow() -> Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear!
- // --------------------------------------------------------------------------------------------------------
+ // - TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1") // OK
+ // - TableNextRow() -> TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK
+ // - TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK: TableNextColumn() automatically gets to next row!
+ // - TableNextRow() -> Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear!
// - 5. Call EndTable()
IMGUI_API bool BeginTable(const char* str_id, int column, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f);
IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true!
@@ -754,8 +794,9 @@ namespace ImGui
// - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled.
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0);
IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled.
- IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu
IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used)
+ IMGUI_API void TableHeadersRow(); // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu
+ IMGUI_API void TableAngledHeadersRow(); // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW.
// Tables: Sorting & Miscellaneous functions
// - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting.
@@ -813,7 +854,7 @@ namespace ImGui
IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget()
IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released.
IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true!
- IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type.
+ IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. returns NULL when drag and drop is finished or inactive. use ImGuiPayload::IsDataType() to test for the payload type.
// Disabling [BETA API]
// - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)
@@ -832,6 +873,9 @@ namespace ImGui
IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window.
IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget.
+ // Overlapping mode
+ IMGUI_API void SetNextItemAllowOverlap(); // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this.
+
// Item/Widgets Utilities and Query Functions
// - Most of the functions are referring to the previous Item that has been submitted.
// - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions.
@@ -852,7 +896,6 @@ namespace ImGui
IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space)
IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space)
IMGUI_API ImVec2 GetItemRectSize(); // get size of last item
- IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area.
// Viewports
// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
@@ -873,8 +916,6 @@ namespace ImGui
IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.).
IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it)
IMGUI_API ImGuiStorage* GetStateStorage();
- IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame
- IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window)
// Text Utilities
IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
@@ -893,10 +934,29 @@ namespace ImGui
IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held.
IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)?
+ IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord); // was key chord (mods + key) pressed, e.g. you can pass 'ImGuiMod_Ctrl | ImGuiKey_S' as a key-chord. This doesn't do any routing or focus check, please consider using Shortcut() function instead.
IMGUI_API int GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate
IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call.
+ // Inputs Utilities: Shortcut Testing & Routing [BETA]
+ // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
+ // ImGuiKey_C // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments)
+ // ImGuiMod_Ctrl | ImGuiKey_C // Accepted by functions taking ImGuiKeyChord arguments)
+ // only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values.
+ // - The general idea is that several callers may register interest in a shortcut, and only one owner gets it.
+ // Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut.
+ // Child1 -> call Shortcut(Ctrl+S) // When Child1 is focused, Child1 gets the shortcut (Child1 overrides Parent shortcuts)
+ // Child2 -> no call // When Child2 is focused, Parent gets the shortcut.
+ // The whole system is order independent, so if Child1 makes its calls before Parent, results will be identical.
+ // This is an important property as it facilitate working with foreign code or larger codebase.
+ // - To understand the difference:
+ // - IsKeyChordPressed() compares mods and call IsKeyPressed() -> function has no side-effect.
+ // - Shortcut() submits a route, routes are resolved, if it currently can be routed it calls IsKeyChordPressed() -> function has (desirable) side-effects as it can prevents another call from getting the route.
+ // - Visualize registered routes in 'Metrics/Debugger->Inputs'.
+ IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0);
+ IMGUI_API void SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0);
+
// Inputs Utilities: Mouse specific
// - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right.
// - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle.
@@ -911,8 +971,8 @@ namespace ImGui
IMGUI_API bool IsAnyMouseDown(); // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls
IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves)
- IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold)
- IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold)
+ IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (uses io.MouseDraggingThreshold if lock_threshold < 0.0f)
+ IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (uses io.MouseDraggingThreshold if lock_threshold < 0.0f)
IMGUI_API void ResetMouseDragDelta(ImGuiMouseButton button = 0); //
IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired mouse cursor shape. Important: reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you
IMGUI_API void SetMouseCursor(ImGuiMouseCursor cursor_type); // set desired mouse cursor shape
@@ -933,7 +993,10 @@ namespace ImGui
IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
// Debug Utilities
+ // - Your main debugging friend is the ShowMetricsWindow() function, which is also accessible from Demo->Tools->Metrics Debugger
IMGUI_API void DebugTextEncoding(const char* text);
+ IMGUI_API void DebugFlashStyleColor(ImGuiCol idx);
+ IMGUI_API void DebugStartItemPicker();
IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro.
// Memory Allocators
@@ -972,21 +1035,47 @@ enum ImGuiWindowFlags_
ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)
ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y)
ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x)
- ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient)
- ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window
- ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB)
- ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
+ ImGuiWindowFlags_NoNavInputs = 1 << 16, // No gamepad/keyboard navigation within the window
+ ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB)
+ ImGuiWindowFlags_UnsavedDocument = 1 << 18, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,
ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
// [Internal]
- ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows.
+ ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows.
ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild()
ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip()
ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup()
ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal()
ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu()
+
+ // Obsolete names
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call.
+#endif
+};
+
+// Flags for ImGui::BeginChild()
+// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Border to be backward compatible with old API using 'bool border = false'.
+// About using AutoResizeX/AutoResizeY flags:
+// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
+// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
+// - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped.
+// While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional "resizing after becoming visible again" glitch.
+// - You may also use ImGuiChildFlags_AlwaysAutoResize to force an update even when child window is not in view.
+// HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping.
+enum ImGuiChildFlags_
+{
+ ImGuiChildFlags_None = 0,
+ ImGuiChildFlags_Border = 1 << 0, // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason)
+ ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense)
+ ImGuiChildFlags_ResizeX = 1 << 2, // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags)
+ ImGuiChildFlags_ResizeY = 1 << 3, // Allow resize from bottom border (layout direction). "
+ ImGuiChildFlags_AutoResizeX = 1 << 4, // Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above.
+ ImGuiChildFlags_AutoResizeY = 1 << 5, // Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above.
+ ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.
+ ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.
};
// Flags for ImGui::InputText()
@@ -1026,25 +1115,31 @@ enum ImGuiTreeNodeFlags_
ImGuiTreeNodeFlags_None = 0,
ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected
ImGuiTreeNodeFlags_Framed = 1 << 1, // Draw frame with background (e.g. for CollapsingHeader)
- ImGuiTreeNodeFlags_AllowItemOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one
+ ImGuiTreeNodeFlags_AllowOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one
ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack
ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes)
ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open
ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node
ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open.
ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes).
- ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow
+ ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag!
ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding().
- ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line. In the future we may refactor the hit system to be front-to-back, allowing natural overlaps and then this can become the default.
- ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (bypass the indented area).
- ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop)
- //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 14, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible
+ ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode.
+ ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (cover the indent area).
+ ImGuiTreeNodeFlags_SpanTextWidth = 1 << 13, // Narrow hit box + narrow hovering highlight, will only cover the label text.
+ ImGuiTreeNodeFlags_SpanAllColumns = 1 << 14, // Frame will span all columns of its container table (text will still fit in current column)
+ ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 15, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop)
+ //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 16, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible
ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog,
+
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7
+#endif
};
// Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions.
-// - To be backward compatible with older API which took an 'int mouse_button = 1' argument, we need to treat
-// small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags.
+// - To be backward compatible with older API which took an 'int mouse_button = 1' argument instead of 'ImGuiPopupFlags flags',
+// we need to treat small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags.
// It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags.
// - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0.
// IMPORTANT: because the default parameter is 1 (==ImGuiPopupFlags_MouseButtonRight), if you rely on the default parameter
@@ -1058,10 +1153,12 @@ enum ImGuiPopupFlags_
ImGuiPopupFlags_MouseButtonMiddle = 2, // For BeginPopupContext*(): open on Middle Mouse release. Guaranteed to always be == 2 (same as ImGuiMouseButton_Middle)
ImGuiPopupFlags_MouseButtonMask_ = 0x1F,
ImGuiPopupFlags_MouseButtonDefault_ = 1,
- ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack
- ImGuiPopupFlags_NoOpenOverItems = 1 << 6, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space
- ImGuiPopupFlags_AnyPopupId = 1 << 7, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup.
- ImGuiPopupFlags_AnyPopupLevel = 1 << 8, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level)
+ ImGuiPopupFlags_NoReopen = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation)
+ //ImGuiPopupFlags_NoReopenAlwaysNavInit = 1 << 6, // For OpenPopup*(), BeginPopupContext*(): focus and initialize navigation even when not reopening.
+ ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack
+ ImGuiPopupFlags_NoOpenOverItems = 1 << 8, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space
+ ImGuiPopupFlags_AnyPopupId = 1 << 10, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup.
+ ImGuiPopupFlags_AnyPopupLevel = 1 << 11, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level)
ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel,
};
@@ -1070,10 +1167,14 @@ enum ImGuiSelectableFlags_
{
ImGuiSelectableFlags_None = 0,
ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this doesn't close parent popup window
- ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column)
+ ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Frame will span all columns of its container table (text will still fit in current column)
ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too
ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text
- ImGuiSelectableFlags_AllowItemOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one
+ ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one
+
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7
+#endif
};
// Flags for ImGui::BeginCombo()
@@ -1087,6 +1188,7 @@ enum ImGuiComboFlags_
ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible
ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button
ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button
+ ImGuiComboFlags_WidthFitPreview = 1 << 7, // Width dynamically calculated from preview contents
ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest,
};
@@ -1097,7 +1199,7 @@ enum ImGuiTabBarFlags_
ImGuiTabBarFlags_Reorderable = 1 << 0, // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list
ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, // Automatically select new tabs when they appear
ImGuiTabBarFlags_TabListPopupButton = 1 << 2, // Disable buttons to open the tab list popup
- ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
+ ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll)
ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab
ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 6, // Resize tabs when they don't fit
@@ -1110,147 +1212,15 @@ enum ImGuiTabBarFlags_
enum ImGuiTabItemFlags_
{
ImGuiTabItemFlags_None = 0,
- ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
+ ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + set ImGuiTabItemFlags_NoAssumedClosure.
ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem()
- ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
- ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()
+ ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
+ ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID()/PopID() on BeginTabItem()/EndTabItem()
ImGuiTabItemFlags_NoTooltip = 1 << 4, // Disable tooltip for the given tab
ImGuiTabItemFlags_NoReorder = 1 << 5, // Disable reordering this tab or having another tab cross over this tab
ImGuiTabItemFlags_Leading = 1 << 6, // Enforce the tab position to the left of the tab bar (after the tab list popup button)
ImGuiTabItemFlags_Trailing = 1 << 7, // Enforce the tab position to the right of the tab bar (before the scrolling buttons)
-};
-
-// Flags for ImGui::BeginTable()
-// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect.
-// Read comments/demos carefully + experiment with live demos to get acquainted with them.
-// - The DEFAULT sizing policies are:
-// - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize.
-// - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off.
-// - When ScrollX is off:
-// - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight.
-// - Columns sizing policy allowed: Stretch (default), Fixed/Auto.
-// - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all).
-// - Stretch Columns will share the remaining width according to their respective weight.
-// - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors.
-// The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
-// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
-// - When ScrollX is on:
-// - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed
-// - Columns sizing policy allowed: Fixed/Auto mostly.
-// - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed.
-// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.
-// - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().
-// If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again.
-// - Read on documentation at the top of imgui_tables.cpp for details.
-enum ImGuiTableFlags_
-{
- // Features
- ImGuiTableFlags_None = 0,
- ImGuiTableFlags_Resizable = 1 << 0, // Enable resizing columns.
- ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)
- ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu.
- ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate.
- ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file.
- ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow().
- // Decorations
- ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)
- ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows.
- ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom.
- ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns.
- ImGuiTableFlags_BordersOuterV = 1 << 10, // Draw vertical borders on the left and right sides.
- ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders.
- ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders.
- ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders.
- ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders.
- ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders.
- ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style
- ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style
- // Sizing Policy (read above for defaults)
- ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.
- ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible.
- ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths.
- ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn().
- // Sizing Extra Options
- ImGuiTableFlags_NoHostExtendX = 1 << 16, // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
- ImGuiTableFlags_NoHostExtendY = 1 << 17, // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.
- ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable.
- ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.
- // Clipping
- ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().
- // Padding
- ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers.
- ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outermost padding.
- ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).
- // Scrolling
- ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX.
- ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.
- // Sorting
- ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).
- ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).
-
- // [Internal] Combinations and masks
- ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,
-};
-
-// Flags for ImGui::TableSetupColumn()
-enum ImGuiTableColumnFlags_
-{
- // Input configuration flags
- ImGuiTableColumnFlags_None = 0,
- ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state)
- ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column.
- ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column.
- ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp).
- ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable).
- ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing.
- ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column.
- ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column.
- ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command).
- ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).
- ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction.
- ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction.
- ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu.
- ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width.
- ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default).
- ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column.
- ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0).
- ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.
-
- // Output status flags, read-only via TableGetColumnFlags()
- ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags.
- ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling.
- ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs
- ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse
-
- // [Internal] Combinations and masks
- ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed,
- ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,
- ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered,
- ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)
-};
-
-// Flags for ImGui::TableNextRow()
-enum ImGuiTableRowFlags_
-{
- ImGuiTableRowFlags_None = 0,
- ImGuiTableRowFlags_Headers = 1 << 0, // Identify header row (set default background color + width of its contents accounted differently for auto column width)
-};
-
-// Enum for ImGui::TableSetBgColor()
-// Background colors are rendering in 3 layers:
-// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
-// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
-// - Layer 2: draw with CellBg color if set.
-// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color.
-// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
-// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
-// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
-enum ImGuiTableBgTarget_
-{
- ImGuiTableBgTarget_None = 0,
- ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)
- ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking)
- ImGuiTableBgTarget_CellBg = 3, // Set cell background color (top-most color)
+ ImGuiTabItemFlags_NoAssumedClosure = 1 << 8, // Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
};
// Flags for ImGui::IsWindowFocused()
@@ -1279,16 +1249,30 @@ enum ImGuiHoveredFlags_
ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window
//ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
- ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window
- ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled
- ImGuiHoveredFlags_NoNavOverride = 1 << 10, // Disable using gamepad/keyboard navigation state when active, always query mouse.
+ ImGuiHoveredFlags_AllowWhenOverlappedByItem = 1 << 8, // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item.
+ ImGuiHoveredFlags_AllowWhenOverlappedByWindow = 1 << 9, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window.
+ ImGuiHoveredFlags_AllowWhenDisabled = 1 << 10, // IsItemHovered() only: Return true even if the item is disabled
+ ImGuiHoveredFlags_NoNavOverride = 1 << 11, // IsItemHovered() only: Disable using gamepad/keyboard navigation state when active, always query mouse
+ ImGuiHoveredFlags_AllowWhenOverlapped = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow,
ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,
ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows,
- // Hovering delays (for tooltips)
- ImGuiHoveredFlags_DelayNormal = 1 << 11, // Return true after io.HoverDelayNormal elapsed (~0.30 sec)
- ImGuiHoveredFlags_DelayShort = 1 << 12, // Return true after io.HoverDelayShort elapsed (~0.10 sec)
- ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)
+ // Tooltips mode
+ // - typically used in IsItemHovered() + SetTooltip() sequence.
+ // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior.
+ // e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.
+ // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often.
+ // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay.
+ ImGuiHoveredFlags_ForTooltip = 1 << 12, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.
+
+ // (Advanced) Mouse Hovering delays.
+ // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags.
+ // - use those if you need specific overrides.
+ ImGuiHoveredFlags_Stationary = 1 << 13, // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay.
+ ImGuiHoveredFlags_DelayNone = 1 << 14, // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this.
+ ImGuiHoveredFlags_DelayShort = 1 << 15, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
+ ImGuiHoveredFlags_DelayNormal = 1 << 16, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
+ ImGuiHoveredFlags_NoSharedDelay = 1 << 17, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)
};
// Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload()
@@ -1348,11 +1332,17 @@ enum ImGuiSortDirection_
ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc.
};
+// Since 1.90, defining IMGUI_DISABLE_OBSOLETE_FUNCTIONS automatically defines IMGUI_DISABLE_OBSOLETE_KEYIO as well.
+#if defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(IMGUI_DISABLE_OBSOLETE_KEYIO)
+#define IMGUI_DISABLE_OBSOLETE_KEYIO
+#endif
+
// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values.
// All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87).
// Since >= 1.89 we increased typing (went from int to enum), some legacy code may need a cast to ImGuiKey.
// Read details about the 1.87 and 1.89 transition : https://github.com/ocornut/imgui/issues/4921
// Note that "Keys" related to physical keys and are not the same concept as input "Characters", the later are submitted via io.AddInputCharacter().
+// The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps.
enum ImGuiKey : int
{
// Keyboard
@@ -1381,6 +1371,8 @@ enum ImGuiKey : int
ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z,
ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6,
ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12,
+ ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18,
+ ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24,
ImGuiKey_Apostrophe, // '
ImGuiKey_Comma, // ,
ImGuiKey_Minus, // -
@@ -1406,6 +1398,8 @@ enum ImGuiKey : int
ImGuiKey_KeypadAdd,
ImGuiKey_KeypadEnter,
ImGuiKey_KeypadEqual,
+ ImGuiKey_AppBack, // Available on some keyboard/mouses. Often referred as "Browser Back"
+ ImGuiKey_AppForward,
// Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION ACTION
// (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets)
@@ -1450,13 +1444,13 @@ enum ImGuiKey : int
// - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
// In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
// backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
+ // - On macOS, we swap Cmd(Super) and Ctrl keys at the time of the io.AddKeyEvent() call.
ImGuiMod_None = 0,
- ImGuiMod_Ctrl = 1 << 12, // Ctrl
+ ImGuiMod_Ctrl = 1 << 12, // Ctrl (non-macOS), Cmd (macOS)
ImGuiMod_Shift = 1 << 13, // Shift
ImGuiMod_Alt = 1 << 14, // Option/Menu
- ImGuiMod_Super = 1 << 15, // Cmd/Super/Windows
- ImGuiMod_Shortcut = 1 << 11, // Alias for Ctrl (non-macOS) _or_ Super (macOS).
- ImGuiMod_Mask_ = 0xF800, // 5-bits
+ ImGuiMod_Super = 1 << 15, // Windows/Super (non-macOS), Ctrl (macOS)
+ ImGuiMod_Mask_ = 0xF000, // 4-bits
// [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + the io.KeyMap[] array.
// We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE)
@@ -1473,11 +1467,37 @@ enum ImGuiKey : int
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ ImGuiMod_Shortcut = ImGuiMod_Ctrl, // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl
ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89
- ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87
+ //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87
#endif
};
+// Flags for Shortcut(), SetNextItemShortcut(),
+// (and for upcoming extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() that are still in imgui_internal.h)
+// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function)
+enum ImGuiInputFlags_
+{
+ ImGuiInputFlags_None = 0,
+ ImGuiInputFlags_Repeat = 1 << 0, // Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.
+
+ // Flags for Shortcut(), SetNextItemShortcut()
+ // - Routing policies: RouteGlobal+OverActive >> RouteActive or RouteFocused (if owner is active item) >> RouteGlobal+OverFocused >> RouteFocused (if in focused window stack) >> RouteGlobal.
+ // - Default policy is RouteFocused. Can select only 1 policy among all available.
+ ImGuiInputFlags_RouteActive = 1 << 10, // Route to active item only.
+ ImGuiInputFlags_RouteFocused = 1 << 11, // Route to windows in the focus stack (DEFAULT). Deep-most focused window takes inputs. Active item takes inputs over deep-most focused window.
+ ImGuiInputFlags_RouteGlobal = 1 << 12, // Global route (unless a focused window or active item registered the route).
+ ImGuiInputFlags_RouteAlways = 1 << 13, // Do not register route, poll keys directly.
+ // - Routing options
+ ImGuiInputFlags_RouteOverFocused = 1 << 14, // Option: global route: higher priority than focused route (unless active item in focused route).
+ ImGuiInputFlags_RouteOverActive = 1 << 15, // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. CTRL+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active.
+ ImGuiInputFlags_RouteUnlessBgFocused = 1 << 16, // Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.
+ ImGuiInputFlags_RouteFromRootWindow = 1 << 17, // Option: route evaluated from the point of view of root window rather than current window.
+
+ // Flags for SetNextItemShortcut()
+ ImGuiInputFlags_Tooltip = 1 << 18, // Automatically display a tooltip when hovering item [BETA] Unsure of right api (opt-in/opt-out)
+};
+
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
// OBSOLETED in 1.88 (from July 2022): ImGuiNavInput and io.NavInputs[].
// Official backends between 1.60 and 1.86: will keep working and feed gamepad inputs as long as IMGUI_DISABLE_OBSOLETE_KEYIO is not set.
@@ -1529,15 +1549,15 @@ enum ImGuiCol_
ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input
ImGuiCol_FrameBgHovered,
ImGuiCol_FrameBgActive,
- ImGuiCol_TitleBg,
- ImGuiCol_TitleBgActive,
- ImGuiCol_TitleBgCollapsed,
+ ImGuiCol_TitleBg, // Title bar
+ ImGuiCol_TitleBgActive, // Title bar when focused
+ ImGuiCol_TitleBgCollapsed, // Title bar when collapsed
ImGuiCol_MenuBarBg,
ImGuiCol_ScrollbarBg,
ImGuiCol_ScrollbarGrab,
ImGuiCol_ScrollbarGrabHovered,
ImGuiCol_ScrollbarGrabActive,
- ImGuiCol_CheckMark,
+ ImGuiCol_CheckMark, // Checkbox tick and RadioButton circle
ImGuiCol_SliderGrab,
ImGuiCol_SliderGrabActive,
ImGuiCol_Button,
@@ -1579,40 +1599,45 @@ enum ImGuiCol_
// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.
// During initialization or between frames, feel free to just poke into ImGuiStyle directly.
// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description.
-// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
-// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
+// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
+// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
// - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type.
enum ImGuiStyleVar_
{
- // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions)
- ImGuiStyleVar_Alpha, // float Alpha
- ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha
- ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding
- ImGuiStyleVar_WindowRounding, // float WindowRounding
- ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize
- ImGuiStyleVar_WindowMinSize, // ImVec2 WindowMinSize
- ImGuiStyleVar_WindowTitleAlign, // ImVec2 WindowTitleAlign
- ImGuiStyleVar_ChildRounding, // float ChildRounding
- ImGuiStyleVar_ChildBorderSize, // float ChildBorderSize
- ImGuiStyleVar_PopupRounding, // float PopupRounding
- ImGuiStyleVar_PopupBorderSize, // float PopupBorderSize
- ImGuiStyleVar_FramePadding, // ImVec2 FramePadding
- ImGuiStyleVar_FrameRounding, // float FrameRounding
- ImGuiStyleVar_FrameBorderSize, // float FrameBorderSize
- ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing
- ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing
- ImGuiStyleVar_IndentSpacing, // float IndentSpacing
- ImGuiStyleVar_CellPadding, // ImVec2 CellPadding
- ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize
- ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
- ImGuiStyleVar_GrabMinSize, // float GrabMinSize
- ImGuiStyleVar_GrabRounding, // float GrabRounding
- ImGuiStyleVar_TabRounding, // float TabRounding
- ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign
- ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign
- ImGuiStyleVar_SeparatorTextBorderSize,// float SeparatorTextBorderSize
- ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign
- ImGuiStyleVar_SeparatorTextPadding,// ImVec2 SeparatorTextPadding
+ // Enum name -------------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions)
+ ImGuiStyleVar_Alpha, // float Alpha
+ ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha
+ ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding
+ ImGuiStyleVar_WindowRounding, // float WindowRounding
+ ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize
+ ImGuiStyleVar_WindowMinSize, // ImVec2 WindowMinSize
+ ImGuiStyleVar_WindowTitleAlign, // ImVec2 WindowTitleAlign
+ ImGuiStyleVar_ChildRounding, // float ChildRounding
+ ImGuiStyleVar_ChildBorderSize, // float ChildBorderSize
+ ImGuiStyleVar_PopupRounding, // float PopupRounding
+ ImGuiStyleVar_PopupBorderSize, // float PopupBorderSize
+ ImGuiStyleVar_FramePadding, // ImVec2 FramePadding
+ ImGuiStyleVar_FrameRounding, // float FrameRounding
+ ImGuiStyleVar_FrameBorderSize, // float FrameBorderSize
+ ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing
+ ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing
+ ImGuiStyleVar_IndentSpacing, // float IndentSpacing
+ ImGuiStyleVar_CellPadding, // ImVec2 CellPadding
+ ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize
+ ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
+ ImGuiStyleVar_GrabMinSize, // float GrabMinSize
+ ImGuiStyleVar_GrabRounding, // float GrabRounding
+ ImGuiStyleVar_TabRounding, // float TabRounding
+ ImGuiStyleVar_TabBorderSize, // float TabBorderSize
+ ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize
+ ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle
+ ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign
+ ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign
+ ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign
+ ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize
+ ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign
+ ImGuiStyleVar_SeparatorTextPadding, // ImVec2 SeparatorTextPadding
ImGuiStyleVar_COUNT
};
@@ -1728,7 +1753,7 @@ enum ImGuiMouseSource : int
ImGuiMouseSource_COUNT
};
-// Enumeration for ImGui::SetWindow***(), SetNextWindow***(), SetNextItem***() functions
+// Enumeration for ImGui::SetNextWindow***(), SetWindow***(), SetNextItem***() functions
// Represent a condition.
// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always.
enum ImGuiCond_
@@ -1740,6 +1765,170 @@ enum ImGuiCond_
ImGuiCond_Appearing = 1 << 3, // Set the variable if the object/window is appearing after being hidden/inactive (or the first time)
};
+//-----------------------------------------------------------------------------
+// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
+//-----------------------------------------------------------------------------
+
+// Flags for ImGui::BeginTable()
+// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect.
+// Read comments/demos carefully + experiment with live demos to get acquainted with them.
+// - The DEFAULT sizing policies are:
+// - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize.
+// - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off.
+// - When ScrollX is off:
+// - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight.
+// - Columns sizing policy allowed: Stretch (default), Fixed/Auto.
+// - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all).
+// - Stretch Columns will share the remaining width according to their respective weight.
+// - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors.
+// The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
+// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
+// - When ScrollX is on:
+// - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed
+// - Columns sizing policy allowed: Fixed/Auto mostly.
+// - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed.
+// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.
+// - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().
+// If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again.
+// - Read on documentation at the top of imgui_tables.cpp for details.
+enum ImGuiTableFlags_
+{
+ // Features
+ ImGuiTableFlags_None = 0,
+ ImGuiTableFlags_Resizable = 1 << 0, // Enable resizing columns.
+ ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)
+ ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu.
+ ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate.
+ ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file.
+ ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow().
+ // Decorations
+ ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)
+ ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows.
+ ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom.
+ ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns.
+ ImGuiTableFlags_BordersOuterV = 1 << 10, // Draw vertical borders on the left and right sides.
+ ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders.
+ ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders.
+ ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders.
+ ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders.
+ ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders.
+ ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style
+ ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style
+ // Sizing Policy (read above for defaults)
+ ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.
+ ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible.
+ ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths.
+ ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn().
+ // Sizing Extra Options
+ ImGuiTableFlags_NoHostExtendX = 1 << 16, // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
+ ImGuiTableFlags_NoHostExtendY = 1 << 17, // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.
+ ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable.
+ ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.
+ // Clipping
+ ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().
+ // Padding
+ ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers.
+ ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outermost padding.
+ ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).
+ // Scrolling
+ ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX.
+ ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.
+ // Sorting
+ ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).
+ ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).
+ // Miscellaneous
+ ImGuiTableFlags_HighlightHoveredColumn = 1 << 28, // Highlight column headers when hovered (may evolve into a fuller highlight)
+
+ // [Internal] Combinations and masks
+ ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,
+};
+
+// Flags for ImGui::TableSetupColumn()
+enum ImGuiTableColumnFlags_
+{
+ // Input configuration flags
+ ImGuiTableColumnFlags_None = 0,
+ ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state)
+ ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column.
+ ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column.
+ ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp).
+ ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable).
+ ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing.
+ ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column.
+ ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column.
+ ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command).
+ ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).
+ ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction.
+ ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction.
+ ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit horizontal label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers.
+ ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width.
+ ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default).
+ ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column.
+ ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0).
+ ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.
+ ImGuiTableColumnFlags_AngledHeader = 1 << 18, // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row.
+
+ // Output status flags, read-only via TableGetColumnFlags()
+ ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags.
+ ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling.
+ ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs
+ ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse
+
+ // [Internal] Combinations and masks
+ ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed,
+ ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,
+ ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered,
+ ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)
+};
+
+// Flags for ImGui::TableNextRow()
+enum ImGuiTableRowFlags_
+{
+ ImGuiTableRowFlags_None = 0,
+ ImGuiTableRowFlags_Headers = 1 << 0, // Identify header row (set default background color + width of its contents accounted differently for auto column width)
+};
+
+// Enum for ImGui::TableSetBgColor()
+// Background colors are rendering in 3 layers:
+// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
+// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
+// - Layer 2: draw with CellBg color if set.
+// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color.
+// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
+// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
+// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
+enum ImGuiTableBgTarget_
+{
+ ImGuiTableBgTarget_None = 0,
+ ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)
+ ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking)
+ ImGuiTableBgTarget_CellBg = 3, // Set cell background color (top-most color)
+};
+
+// Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
+// Obtained by calling TableGetSortSpecs().
+// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time.
+// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!
+struct ImGuiTableSortSpecs
+{
+ const ImGuiTableColumnSortSpecs* Specs; // Pointer to sort spec array.
+ int SpecsCount; // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled.
+ bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag.
+
+ ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); }
+};
+
+// Sorting specification for one column of a table (sizeof == 12 bytes)
+struct ImGuiTableColumnSortSpecs
+{
+ ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call)
+ ImS16 ColumnIndex; // Index of the column
+ ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)
+ ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending
+
+ ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); }
+};
+
//-----------------------------------------------------------------------------
// [SECTION] Helpers: Memory allocations macros, ImVector<>
//-----------------------------------------------------------------------------
@@ -1829,6 +2018,7 @@ struct ImVector
inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
inline T* find(const T& v) { T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
inline const T* find(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
+ inline int find_index(const T& v) const { const T* data_end = Data + Size; const T* it = find(v); if (it == data_end) return -1; const ptrdiff_t off = it - Data; return (int)off; }
inline bool find_erase(const T& v) { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; }
inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; }
inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; }
@@ -1862,7 +2052,7 @@ struct ImGuiStyle
float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).
ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines.
ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label).
- ImVec2 CellPadding; // Padding within a table cell
+ ImVec2 CellPadding; // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
@@ -1874,6 +2064,9 @@ struct ImGuiStyle
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
float TabBorderSize; // Thickness of border around tabs.
float TabMinWidthForCloseButton; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
+ float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
+ float TableAngledHeadersAngle; // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees).
+ ImVec2 TableAngledHeadersTextAlign;// Alignment of angled headers within the cell
ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).
ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
@@ -1890,6 +2083,14 @@ struct ImGuiStyle
float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
ImVec4 Colors[ImGuiCol_COUNT];
+ // Behaviors
+ // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO)
+ float HoverStationaryDelay; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
+ float HoverDelayShort; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
+ float HoverDelayNormal; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
+ ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
+ ImGuiHoveredFlags HoverFlagsForTooltipNav; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
+
IMGUI_API ImGuiStyle();
IMGUI_API void ScaleAllSizes(float scale_factor);
};
@@ -1899,6 +2100,9 @@ struct ImGuiStyle
//-----------------------------------------------------------------------------
// Communicate most settings and inputs/outputs to Dear ImGui using this structure.
// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage.
+// It is generally expected that:
+// - initialization: backends and user code writes to ImGuiIO.
+// - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO.
//-----------------------------------------------------------------------------
// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.
@@ -1924,13 +2128,6 @@ struct ImGuiIO
float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds.
const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions.
const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified).
- float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds.
- float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels.
- float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging.
- float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).
- float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds.
- float HoverDelayNormal; // = 0.30 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true.
- float HoverDelayShort; // = 0.10 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true.
void* UserData; // = NULL // Store your own data.
ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture.
@@ -1941,7 +2138,7 @@ struct ImGuiIO
// Miscellaneous options
bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
- bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl.
+ bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl.
bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.
bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting).
bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will keep item active and select contents (single-line only).
@@ -1950,18 +2147,39 @@ struct ImGuiIO
bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
+ // Inputs Behaviors
+ // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)
+ float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds.
+ float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels.
+ float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging.
+ float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).
+ float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds.
+
+ //------------------------------------------------------------------
// Debug options
- // - tools to test correct Begin/End and BeginChild/EndChild behaviors.
- // - presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
- // this is inconsistent with other BeginXXX functions and create confusion for many users.
- // - we expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.
+ //------------------------------------------------------------------
+
+ // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
+ // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
+ // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
+ // e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
+ bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK().
+
+ // Tools to test correct Begin/End and BeginChild/EndChild behaviors.
+ // - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
+ // - This is inconsistent with other BeginXXX functions and create confusion for many users.
+ // - We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.
bool ConfigDebugBeginReturnValueOnce;// = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows.
bool ConfigDebugBeginReturnValueLoop;// = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running.
- // - option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.
- // - backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
- // - consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
+
+ // Option to deactivate io.AddFocusEvent(false) handling.
+ // - May facilitate interactions with a debugger when focus loss leads to clearing inputs data.
+ // - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing.
+ // Options to audit .ini data
+ bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
+
//------------------------------------------------------------------
// Platform Functions
// (the imgui_impl_xxxx backend files are setting those up for you)
@@ -1983,11 +2201,9 @@ struct ImGuiIO
// Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows)
// (default to use native imm32 api on Windows)
void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- void* ImeWindowHandle; // = NULL // [Obsolete] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.
-#else
- void* _UnusedPadding; // Unused field to keep data structure the same size.
-#endif
+
+ // Optional: Platform locale
+ ImWchar PlatformLocaleDecimalPoint; // '.' // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point
//------------------------------------------------------------------
// Input - Call before calling NewFrame()
@@ -2007,8 +2223,11 @@ struct ImGuiIO
IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode.
IMGUI_API void SetAppAcceptingEvents(bool accepting_events); // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
- IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually
- IMGUI_API void ClearInputKeys(); // [Internal] Release all keys
+ IMGUI_API void ClearEventsQueue(); // Clear all incoming events.
+ IMGUI_API void ClearInputKeys(); // Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ IMGUI_API void ClearInputCharacters(); // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys().
+#endif
//------------------------------------------------------------------
// Output - Updated by NewFrame() or EndFrame()/Render()
@@ -2028,18 +2247,8 @@ struct ImGuiIO
int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3
int MetricsRenderWindows; // Number of visible windows
int MetricsActiveWindows; // Number of active windows
- int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts.
ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta.
- // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.
- // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
- // Old (<1.87): ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
- int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
- bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.
- float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.
-#endif
-
//------------------------------------------------------------------
// [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed!
//------------------------------------------------------------------
@@ -2060,7 +2269,7 @@ struct ImGuiIO
bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows
// Other state maintained from data above + IO function calls
- ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. DOES NOT CONTAINS ImGuiMod_Shortcut which is pretranslated). Read-only, updated by NewFrame()
+ ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. Read-only, updated by NewFrame()
ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this.
bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
@@ -2074,6 +2283,7 @@ struct ImGuiIO
bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window.
bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system.
+ bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a ctrl-click that spawned a simulated right click
float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked)
float MouseDownDurationPrev[5]; // Previous time the mouse button has been down
float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)
@@ -2085,11 +2295,21 @@ struct ImGuiIO
ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16()
ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.
+ // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.
+ // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
+ // Old (<1.87): ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+ int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
+ bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.
+ float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.
+ //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.
+#endif
+
IMGUI_API ImGuiIO();
};
//-----------------------------------------------------------------------------
-// [SECTION] Misc data structures
+// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)
//-----------------------------------------------------------------------------
// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used.
@@ -2109,6 +2329,8 @@ struct ImGuiInputTextCallbackData
void* UserData; // What user passed to InputText() // Read-only
// Arguments for the different callback events
+ // - During Resize callback, Buf will be same as your input buffer.
+ // - However, during Completion/History/Always callback, Buf always points to our own internal data (it is not the same as your buffer)! Changes to it will be reflected into your own buffer shortly after the callback.
// - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary.
// - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state.
ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0;
@@ -2163,30 +2385,6 @@ struct ImGuiPayload
bool IsDelivery() const { return Delivery; }
};
-// Sorting specification for one column of a table (sizeof == 12 bytes)
-struct ImGuiTableColumnSortSpecs
-{
- ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call)
- ImS16 ColumnIndex; // Index of the column
- ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)
- ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending (you can use this or SortSign, whichever is more convenient for your sort function)
-
- ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); }
-};
-
-// Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
-// Obtained by calling TableGetSortSpecs().
-// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time.
-// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!
-struct ImGuiTableSortSpecs
-{
- const ImGuiTableColumnSortSpecs* Specs; // Pointer to sort spec array.
- int SpecsCount; // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled.
- bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag.
-
- ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); }
-};
-
//-----------------------------------------------------------------------------
// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)
//-----------------------------------------------------------------------------
@@ -2270,9 +2468,9 @@ struct ImGuiStorage
{
ImGuiID key;
union { int val_i; float val_f; void* val_p; };
- ImGuiStoragePair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; }
- ImGuiStoragePair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; }
- ImGuiStoragePair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; }
+ ImGuiStoragePair(ImGuiID _key, int _val) { key = _key; val_i = _val; }
+ ImGuiStoragePair(ImGuiID _key, float _val) { key = _key; val_f = _val; }
+ ImGuiStoragePair(ImGuiID _key, void* _val) { key = _key; val_p = _val; }
};
ImVector Data;
@@ -2299,11 +2497,10 @@ struct ImGuiStorage
IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0.0f);
IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL);
- // Use on your own storage if you know only integer are being stored (open/close all tree nodes)
- IMGUI_API void SetAllInt(int val);
-
- // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
+ // Advanced: for quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
IMGUI_API void BuildSortByKey();
+ // Obsolete: use on your own storage if you know only integer are being stored (open/close all tree nodes)
+ IMGUI_API void SetAllInt(int val);
};
// Helper: Manually clip large list of items.
@@ -2344,12 +2541,14 @@ struct ImGuiListClipper
IMGUI_API void End(); // Automatically called on the last call of Step() that returns false.
IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items.
- // Call IncludeRangeByIndices() *BEFORE* first call to Step() if you need a range of items to not be clipped, regardless of their visibility.
+ // Call IncludeItemByIndex() or IncludeItemsByIndex() *BEFORE* first call to Step() if you need a range of items to not be clipped, regardless of their visibility.
// (Due to alignment / padding of certain items it is possible that an extra item may be included on either end of the display range).
- IMGUI_API void IncludeRangeByIndices(int item_begin, int item_end); // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped.
+ inline void IncludeItemByIndex(int item_index) { IncludeItemsByIndex(item_index, item_index + 1); }
+ IMGUI_API void IncludeItemsByIndex(int item_begin, int item_end); // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeRangeByIndices(item_begin, item_end); } // [renamed in 1.89.6]
+ inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9]
+ inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6]
//inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79]
#endif
};
@@ -2374,9 +2573,13 @@ static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x
static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }
static inline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs) { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; }
static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }
+static inline bool operator==(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; }
+static inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y; }
static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }
static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }
static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
+static inline bool operator==(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w; }
+static inline bool operator!=(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z || lhs.w != rhs.w; }
IM_MSVC_RUNTIME_CHECKS_RESTORE
#endif
@@ -2413,8 +2616,8 @@ struct ImColor
constexpr ImColor() { }
constexpr ImColor(float r, float g, float b, float a = 1.0f) : Value(r, g, b, a) { }
constexpr ImColor(const ImVec4& col) : Value(col) {}
- ImColor(int r, int g, int b, int a = 255) { float sc = 1.0f / 255.0f; Value.x = (float)r * sc; Value.y = (float)g * sc; Value.z = (float)b * sc; Value.w = (float)a * sc; }
- ImColor(ImU32 rgba) { float sc = 1.0f / 255.0f; Value.x = (float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * sc; Value.y = (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * sc; Value.z = (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * sc; Value.w = (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * sc; }
+ constexpr ImColor(int r, int g, int b, int a = 255) : Value((float)r * (1.0f / 255.0f), (float)g * (1.0f / 255.0f), (float)b * (1.0f / 255.0f), (float)a* (1.0f / 255.0f)) {}
+ constexpr ImColor(ImU32 rgba) : Value((float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * (1.0f / 255.0f)) {}
inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); }
inline operator ImVec4() const { return Value; }
@@ -2446,9 +2649,9 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c
// Special Draw callback value to request renderer backend to reset the graphics/render state.
// The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address.
-// This is useful for example if you submitted callbacks which you know have altered the render state and you want it to be restored.
-// It is not done by default because they are many perfectly useful way of altering render state for imgui contents (e.g. changing shader/blending settings before an Image call).
-#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1)
+// This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored.
+// Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call).
+#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8)
// Typically, 1 command = 1 GPU draw call (unless command is a callback)
// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,
@@ -2571,15 +2774,15 @@ struct ImDrawList
// [Internal, used while building lists]
unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)
- const char* _OwnerName; // Pointer to owner window's name for debugging
ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
- ImVector _ClipRectStack; // [Internal]
- ImVector _TextureIdStack; // [Internal]
ImVector _Path; // [Internal] current path building
ImDrawCmdHeader _CmdHeader; // [Internal] template of active commands. Fields should match those of CmdBuffer.back().
ImDrawListSplitter _Splitter; // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)
+ ImVector _ClipRectStack; // [Internal]
+ ImVector _TextureIdStack; // [Internal]
float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content
+ const char* _OwnerName; // Pointer to owner window's name for debugging
// If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui)
ImDrawList(ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; }
@@ -2612,13 +2815,20 @@ struct ImDrawList
IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0);
IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f);
IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments);
+ IMGUI_API void AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f);
+ IMGUI_API void AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0);
IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);
IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
- IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);
- IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col);
IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)
IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points)
+ // General polygon
+ // - Only simple polygons are supported by filling functions (no self-intersections, no holes).
+ // - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience fo user but not used by main library.
+ IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);
+ IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col);
+ IMGUI_API void AddConcavePolyFilled(const ImVec2* points, int num_points, ImU32 col);
+
// Image primitives
// - Read FAQ to understand what ImTextureID is.
// - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle.
@@ -2628,14 +2838,17 @@ struct ImDrawList
IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0);
// Stateful path API, add points then finish with PathFillConvex() or PathStroke()
- // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
+ // - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
+ // so e.g. 'PathArcTo(center, radius, PI * -0.5f, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5f)' won't have correct anti-aliasing when followed by PathFillConvex().
inline void PathClear() { _Path.Size = 0; }
inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); }
inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }
inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }
+ inline void PathFillConcave(ImU32 col) { AddConcavePolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }
inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; }
IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0);
IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
+ IMGUI_API void PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse
IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points)
IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points)
IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0);
@@ -2648,7 +2861,7 @@ struct ImDrawList
// Advanced: Channels
// - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives)
// - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end)
- // - FIXME-OBSOLETE: This API shouldn't have been in ImDrawList in the first place!
+ // - This API shouldn't have been in ImDrawList in the first place!
// Prefer using your own persistent instance of ImDrawListSplitter as you can stack them.
// Using the ImDrawList::ChannelsXXXX you cannot stack a split over another.
inline void ChannelsSplit(int count) { _Splitter.Split(this, count); }
@@ -2668,6 +2881,9 @@ struct ImDrawList
inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index
// Obsolete names
+ //inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024)
+ //inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
+ //inline void PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
//inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
//inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
@@ -2689,18 +2905,20 @@ struct ImDrawList
// as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList)
struct ImDrawData
{
- bool Valid; // Only valid after Render() is called and before the next NewFrame() is called.
- int CmdListsCount; // Number of ImDrawList* to render
- int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size
- int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size
- ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here.
- ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications)
- ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)
- ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display.
+ bool Valid; // Only valid after Render() is called and before the next NewFrame() is called.
+ int CmdListsCount; // Number of ImDrawList* to render (should always be == CmdLists.size)
+ int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size
+ int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size
+ ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here.
+ ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications)
+ ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)
+ ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display.
+ ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not).
// Functions
ImDrawData() { Clear(); }
- void Clear() { memset(this, 0, sizeof(*this)); } // The ImDrawList are owned by ImGuiContext!
+ IMGUI_API void Clear();
+ IMGUI_API void AddDrawList(ImDrawList* draw_list); // Helper to add an external draw list into an existing ImDrawData.
IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution.
};
@@ -2716,7 +2934,7 @@ struct ImFontConfig
bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).
int FontNo; // 0 // Index of font within TTF/OTF file
float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height).
- int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal so you can reduce this to 2 to save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
+ int OversampleH; // 2 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis.
bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1.
ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now.
@@ -2726,7 +2944,8 @@ struct ImFontConfig
float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs
bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.
- float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable.
+ float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.
+ float RasterizerDensity; // 1.0f // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display. IMPORTANT: If you increase this it is expected that you increase font scale accordingly, otherwise quality may look lowered.
ImWchar EllipsisChar; // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
// [Internal]
@@ -2810,8 +3029,8 @@ struct ImFontAtlas
IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg);
IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL);
IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);
- IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed.
- IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp.
+ IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed.
+ IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp.
IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.
IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts.
IMGUI_API void ClearTexData(); // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory.
@@ -2853,7 +3072,7 @@ struct ImFontAtlas
// You can request arbitrary rectangles to be packed into the atlas, for your own purposes.
// - After calling Build(), you can query the rectangle position and render your pixels.
- // - If you render colored output, set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of prefered texture format.
+ // - If you render colored output, set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of preferred texture format.
// - You can also request your rectangles to be mapped as font glyph (given a font + Unicode point),
// so you can render e.g. custom colorful icons and use them as regular glyphs.
// - Read docs/FONTS.md for more details about using colorful icons.
@@ -2982,6 +3201,7 @@ enum ImGuiViewportFlags_
// - Windows are generally trying to stay within the Work Area of their host viewport.
struct ImGuiViewport
{
+ ImGuiID ID; // Unique identifier for the viewport
ImGuiViewportFlags Flags; // See ImGuiViewportFlags_
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.
@@ -3018,32 +3238,36 @@ struct ImGuiPlatformImeData
// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead.
//-----------------------------------------------------------------------------
-namespace ImGui
-{
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
- IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]
-#else
- static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; }
-#endif
-}
-
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui
{
+ // OBSOLETED in 1.90.0 (from September 2023)
+ static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); }
+ static inline void EndChildFrame() { EndChild(); }
+ //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border
+ //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border
+ static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); }
+ IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1);
+ IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1);
+ // OBSOLETED in 1.89.7 (from June 2023)
+ IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item.
// OBSOLETED in 1.89.4 (from March 2023)
static inline void PushAllowKeyboardFocus(bool tab_stop) { PushTabStop(tab_stop); }
static inline void PopAllowKeyboardFocus() { PopTabStop(); }
// OBSOLETED in 1.89 (from August 2022)
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding)
- // OBSOLETED in 1.88 (from May 2022)
- static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
- static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value.
- // OBSOLETED in 1.86 (from November 2021)
- IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper.
- // OBSOLETED in 1.85 (from August 2021)
- static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }
+ // OBSOLETED in 1.87 (from February 2022)
+ IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value!
+ //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; }
// Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
+ //-- OBSOLETED in 1.88 (from May 2022)
+ //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
+ //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value.
+ //-- OBSOLETED in 1.86 (from November 2021)
+ //IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper.
+ //-- OBSOLETED in 1.85 (from August 2021)
+ //static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }
//-- OBSOLETED in 1.81 (from February 2021)
//static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); }
//static inline bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1) { float height = GetTextLineHeightWithSpacing() * ((height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f) + GetStyle().FramePadding.y * 2.0f; return BeginListBox(label, ImVec2(0.0f, height)); } // Helper to calculate size from items_count and height_in_items
@@ -3094,21 +3318,21 @@ namespace ImGui
//static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42
}
-// OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect()
-typedef ImDrawFlags ImDrawCornerFlags;
-enum ImDrawCornerFlags_
-{
- ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit
- ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally).
- ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82.
- ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82.
- ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82.
- ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82
- ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight,
- ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight,
- ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft,
- ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight,
-};
+//-- OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect()
+//typedef ImDrawFlags ImDrawCornerFlags;
+//enum ImDrawCornerFlags_
+//{
+// ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit
+// ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally).
+// ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82.
+// ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82.
+// ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82.
+// ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82
+// ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight,
+// ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight,
+// ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft,
+// ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight,
+//};
// RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022)
// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place.
@@ -3117,6 +3341,8 @@ enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl
//typedef ImGuiKeyChord ImGuiKeyModFlags; // == int
//enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super };
+#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // OBSOLETED IN 1.90 (now using C++11 standard version)
+
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022)
@@ -3139,9 +3365,14 @@ enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl
#pragma warning (pop)
#endif
-// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h)
+// Include imgui_user.h at the end of imgui.h
+// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
#ifdef IMGUI_INCLUDE_IMGUI_USER_H
+#ifdef IMGUI_USER_H_FILENAME
+#include IMGUI_USER_H_FILENAME
+#else
#include "imgui_user.h"
#endif
+#endif
#endif // #ifndef IMGUI_DISABLE
diff --git a/extern/src/imgui/imgui_draw.cpp b/extern/src/imgui/imgui_draw.cpp
index 0280c6e7..b9669113 100644
--- a/extern/src/imgui/imgui_draw.cpp
+++ b/extern/src/imgui/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.89.6
+// dear imgui, v1.90.7
// (drawing and font code)
/*
@@ -8,6 +8,7 @@ Index of this file:
// [SECTION] STB libraries implementation
// [SECTION] Style functions
// [SECTION] ImDrawList
+// [SECTION] ImTriangulator, ImDrawList concave polygon fill
// [SECTION] ImDrawListSplitter
// [SECTION] ImDrawData
// [SECTION] Helpers ShadeVertsXXX functions
@@ -63,6 +64,8 @@ Index of this file:
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
+#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@@ -134,7 +137,7 @@ namespace IMGUI_STB_NAMESPACE
#define STBTT_sqrt(x) ImSqrt(x)
#define STBTT_pow(x,y) ImPow(x,y)
#define STBTT_fabs(x) ImFabs(x)
-#define STBTT_ifloor(x) ((int)ImFloorSigned(x))
+#define STBTT_ifloor(x) ((int)ImFloor(x))
#define STBTT_iceil(x) ((int)ImCeil(x))
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
@@ -382,12 +385,13 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
}
// Initialize before use in a new frame. We always have a command ready in the buffer.
+// In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this.
void ImDrawList::_ResetForNewFrame()
{
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
- IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
- IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
- IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
+ IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);
+ IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4));
+ IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
if (_Splitter._Count > 1)
_Splitter.Merge(this);
@@ -474,7 +478,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
}
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
-#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
+#define ImDrawCmd_HeaderSize (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
@@ -560,7 +564,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
{
// Automatic segment count
const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy
- if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
+ if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
else
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
@@ -640,7 +644,7 @@ void ImDrawList::PrimReserve(int idx_count, int vtx_count)
_IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;
}
-// Release the a number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
+// Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
void ImDrawList::PrimUnreserve(int idx_count, int vtx_count)
{
IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
@@ -1190,8 +1194,8 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
- const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
- const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f);
+ const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
+ const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f);
const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
@@ -1216,6 +1220,26 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
}
}
+void ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments)
+{
+ if (num_segments <= 0)
+ num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
+
+ _Path.reserve(_Path.Size + (num_segments + 1));
+
+ const float cos_rot = ImCos(rot);
+ const float sin_rot = ImSin(rot);
+ for (int i = 0; i <= num_segments; i++)
+ {
+ const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
+ ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y);
+ const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot));
+ point.x = rel.x + center.x;
+ point.y = rel.y + center.y;
+ _Path.push_back(point);
+ }
+}
+
ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)
{
float u = 1.0f - t;
@@ -1311,33 +1335,22 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
}
}
-IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
{
+ /*
+ IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- // Obsoleted in 1.82 (from February 2021)
- // Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
- // ~0 --> ImDrawFlags_RoundCornersAll or 0
- if (flags == ~0)
- return ImDrawFlags_RoundCornersAll;
-
- // Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations)
- // 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!)
- // 0x02 --> ImDrawFlags_RoundCornersTopRight
- // 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight
- // 0x04 --> ImDrawFlags_RoundCornersBotLeft
- // 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft
- // ...
- // 0x0F --> ImDrawFlags_RoundCornersAll or 0
- // (See all values in ImDrawCornerFlags_)
- if (flags >= 0x01 && flags <= 0x0F)
- return (flags << 4);
-
+ // Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)
+ // - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
+ if (flags == ~0) { return ImDrawFlags_RoundCornersAll; }
+ // - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
+ if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }
// We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
#endif
-
- // If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
- // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc...
+ */
+ // If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
+ // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.
+ // See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section.
IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!");
if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
@@ -1348,10 +1361,12 @@ static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)
{
- flags = FixRectCornerFlags(flags);
- rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f);
- rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f);
-
+ if (rounding >= 0.5f)
+ {
+ flags = FixRectCornerFlags(flags);
+ rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);
+ rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);
+ }
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
{
PathLineTo(a);
@@ -1544,6 +1559,35 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
PathFillConvex(col);
}
+// Ellipse
+void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness)
+{
+ if ((col & IM_COL32_A_MASK) == 0)
+ return;
+
+ if (num_segments <= 0)
+ num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
+
+ // Because we are filling a closed shape we remove 1 from the count of segments/points
+ const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
+ PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
+ PathStroke(col, true, thickness);
+}
+
+void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments)
+{
+ if ((col & IM_COL32_A_MASK) == 0)
+ return;
+
+ if (num_segments <= 0)
+ num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
+
+ // Because we are filling a closed shape we remove 1 from the count of segments/points
+ const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
+ PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
+ PathFillConvex(col);
+}
+
// Cubic Bezier takes 4 controls points
void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)
{
@@ -1571,10 +1615,11 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos,
if ((col & IM_COL32_A_MASK) == 0)
return;
+ // Accept null ranges
+ if (text_begin == text_end || text_begin[0] == 0)
+ return;
if (text_end == NULL)
text_end = text_begin + strlen(text_begin);
- if (text_begin == text_end)
- return;
// Pull default font/size from the shared ImDrawListSharedData instance
if (font == NULL)
@@ -1658,6 +1703,316 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi
PopTextureID();
}
+//-----------------------------------------------------------------------------
+// [SECTION] ImTriangulator, ImDrawList concave polygon fill
+//-----------------------------------------------------------------------------
+// Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity.
+// Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
+// Provided as a convenience for user but not used by main library.
+//-----------------------------------------------------------------------------
+// - ImTriangulator [Internal]
+// - AddConcavePolyFilled()
+//-----------------------------------------------------------------------------
+
+enum ImTriangulatorNodeType
+{
+ ImTriangulatorNodeType_Convex,
+ ImTriangulatorNodeType_Ear,
+ ImTriangulatorNodeType_Reflex
+};
+
+struct ImTriangulatorNode
+{
+ ImTriangulatorNodeType Type;
+ int Index;
+ ImVec2 Pos;
+ ImTriangulatorNode* Next;
+ ImTriangulatorNode* Prev;
+
+ void Unlink() { Next->Prev = Prev; Prev->Next = Next; }
+};
+
+struct ImTriangulatorNodeSpan
+{
+ ImTriangulatorNode** Data = NULL;
+ int Size = 0;
+
+ void push_back(ImTriangulatorNode* node) { Data[Size++] = node; }
+ void find_erase_unsorted(int idx) { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } }
+};
+
+struct ImTriangulator
+{
+ static int EstimateTriangleCount(int points_count) { return (points_count < 3) ? 0 : points_count - 2; }
+ static int EstimateScratchBufferSize(int points_count) { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; }
+
+ void Init(const ImVec2* points, int points_count, void* scratch_buffer);
+ void GetNextTriangle(unsigned int out_triangle[3]); // Return relative indexes for next triangle
+
+ // Internal functions
+ void BuildNodes(const ImVec2* points, int points_count);
+ void BuildReflexes();
+ void BuildEars();
+ void FlipNodeList();
+ bool IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const;
+ void ReclassifyNode(ImTriangulatorNode* node);
+
+ // Internal members
+ int _TrianglesLeft = 0;
+ ImTriangulatorNode* _Nodes = NULL;
+ ImTriangulatorNodeSpan _Ears;
+ ImTriangulatorNodeSpan _Reflexes;
+};
+
+// Distribute storage for nodes, ears and reflexes.
+// FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer
+// (this would require first building reflexes to bail to convex if empty, without even building nodes)
+void ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer)
+{
+ IM_ASSERT(scratch_buffer != NULL && points_count >= 3);
+ _TrianglesLeft = EstimateTriangleCount(points_count);
+ _Nodes = (ImTriangulatorNode*)scratch_buffer; // points_count x Node
+ _Ears.Data = (ImTriangulatorNode**)(_Nodes + points_count); // points_count x Node*
+ _Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node*
+ BuildNodes(points, points_count);
+ BuildReflexes();
+ BuildEars();
+}
+
+void ImTriangulator::BuildNodes(const ImVec2* points, int points_count)
+{
+ for (int i = 0; i < points_count; i++)
+ {
+ _Nodes[i].Type = ImTriangulatorNodeType_Convex;
+ _Nodes[i].Index = i;
+ _Nodes[i].Pos = points[i];
+ _Nodes[i].Next = _Nodes + i + 1;
+ _Nodes[i].Prev = _Nodes + i - 1;
+ }
+ _Nodes[0].Prev = _Nodes + points_count - 1;
+ _Nodes[points_count - 1].Next = _Nodes;
+}
+
+void ImTriangulator::BuildReflexes()
+{
+ ImTriangulatorNode* n1 = _Nodes;
+ for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
+ {
+ if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos))
+ continue;
+ n1->Type = ImTriangulatorNodeType_Reflex;
+ _Reflexes.push_back(n1);
+ }
+}
+
+void ImTriangulator::BuildEars()
+{
+ ImTriangulatorNode* n1 = _Nodes;
+ for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
+ {
+ if (n1->Type != ImTriangulatorNodeType_Convex)
+ continue;
+ if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos))
+ continue;
+ n1->Type = ImTriangulatorNodeType_Ear;
+ _Ears.push_back(n1);
+ }
+}
+
+void ImTriangulator::GetNextTriangle(unsigned int out_triangle[3])
+{
+ if (_Ears.Size == 0)
+ {
+ FlipNodeList();
+
+ ImTriangulatorNode* node = _Nodes;
+ for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next)
+ node->Type = ImTriangulatorNodeType_Convex;
+ _Reflexes.Size = 0;
+ BuildReflexes();
+ BuildEars();
+
+ // If we still don't have ears, it means geometry is degenerated.
+ if (_Ears.Size == 0)
+ {
+ // Return first triangle available, mimicking the behavior of convex fill.
+ IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated
+ _Ears.Data[0] = _Nodes;
+ _Ears.Size = 1;
+ }
+ }
+
+ ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size];
+ out_triangle[0] = ear->Prev->Index;
+ out_triangle[1] = ear->Index;
+ out_triangle[2] = ear->Next->Index;
+
+ ear->Unlink();
+ if (ear == _Nodes)
+ _Nodes = ear->Next;
+
+ ReclassifyNode(ear->Prev);
+ ReclassifyNode(ear->Next);
+ _TrianglesLeft--;
+}
+
+void ImTriangulator::FlipNodeList()
+{
+ ImTriangulatorNode* prev = _Nodes;
+ ImTriangulatorNode* temp = _Nodes;
+ ImTriangulatorNode* current = _Nodes->Next;
+ prev->Next = prev;
+ prev->Prev = prev;
+ while (current != _Nodes)
+ {
+ temp = current->Next;
+
+ current->Next = prev;
+ prev->Prev = current;
+ _Nodes->Next = current;
+ current->Prev = _Nodes;
+
+ prev = current;
+ current = temp;
+ }
+ _Nodes = prev;
+}
+
+// A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)
+bool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const
+{
+ ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size;
+ for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++)
+ {
+ ImTriangulatorNode* reflex = *p;
+ if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2)
+ if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos))
+ return false;
+ }
+ return true;
+}
+
+void ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1)
+{
+ // Classify node
+ ImTriangulatorNodeType type;
+ const ImTriangulatorNode* n0 = n1->Prev;
+ const ImTriangulatorNode* n2 = n1->Next;
+ if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos))
+ type = ImTriangulatorNodeType_Reflex;
+ else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos))
+ type = ImTriangulatorNodeType_Ear;
+ else
+ type = ImTriangulatorNodeType_Convex;
+
+ // Update lists when a type changes
+ if (type == n1->Type)
+ return;
+ if (n1->Type == ImTriangulatorNodeType_Reflex)
+ _Reflexes.find_erase_unsorted(n1->Index);
+ else if (n1->Type == ImTriangulatorNodeType_Ear)
+ _Ears.find_erase_unsorted(n1->Index);
+ if (type == ImTriangulatorNodeType_Reflex)
+ _Reflexes.push_back(n1);
+ else if (type == ImTriangulatorNodeType_Ear)
+ _Ears.push_back(n1);
+ n1->Type = type;
+}
+
+// Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).
+// (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!
+// It is up to caller to ensure not making costly calls that will be outside of visible area.
+// As concave fill is noticeably more expensive than other primitives, be mindful of this...
+// Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')
+void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col)
+{
+ if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
+ return;
+
+ const ImVec2 uv = _Data->TexUvWhitePixel;
+ ImTriangulator triangulator;
+ unsigned int triangle[3];
+ if (Flags & ImDrawListFlags_AntiAliasedFill)
+ {
+ // Anti-aliased Fill
+ const float AA_SIZE = _FringeScale;
+ const ImU32 col_trans = col & ~IM_COL32_A_MASK;
+ const int idx_count = (points_count - 2) * 3 + points_count * 6;
+ const int vtx_count = (points_count * 2);
+ PrimReserve(idx_count, vtx_count);
+
+ // Add indexes for fill
+ unsigned int vtx_inner_idx = _VtxCurrentIdx;
+ unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
+
+ _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
+ triangulator.Init(points, points_count, _Data->TempBuffer.Data);
+ while (triangulator._TrianglesLeft > 0)
+ {
+ triangulator.GetNextTriangle(triangle);
+ _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1));
+ _IdxWritePtr += 3;
+ }
+
+ // Compute normals
+ _Data->TempBuffer.reserve_discard(points_count);
+ ImVec2* temp_normals = _Data->TempBuffer.Data;
+ for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
+ {
+ const ImVec2& p0 = points[i0];
+ const ImVec2& p1 = points[i1];
+ float dx = p1.x - p0.x;
+ float dy = p1.y - p0.y;
+ IM_NORMALIZE2F_OVER_ZERO(dx, dy);
+ temp_normals[i0].x = dy;
+ temp_normals[i0].y = -dx;
+ }
+
+ for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
+ {
+ // Average normals
+ const ImVec2& n0 = temp_normals[i0];
+ const ImVec2& n1 = temp_normals[i1];
+ float dm_x = (n0.x + n1.x) * 0.5f;
+ float dm_y = (n0.y + n1.y) * 0.5f;
+ IM_FIXNORMAL2F(dm_x, dm_y);
+ dm_x *= AA_SIZE * 0.5f;
+ dm_y *= AA_SIZE * 0.5f;
+
+ // Add vertices
+ _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
+ _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
+ _VtxWritePtr += 2;
+
+ // Add indexes for fringes
+ _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
+ _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
+ _IdxWritePtr += 6;
+ }
+ _VtxCurrentIdx += (ImDrawIdx)vtx_count;
+ }
+ else
+ {
+ // Non Anti-aliased Fill
+ const int idx_count = (points_count - 2) * 3;
+ const int vtx_count = points_count;
+ PrimReserve(idx_count, vtx_count);
+ for (int i = 0; i < vtx_count; i++)
+ {
+ _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
+ _VtxWritePtr++;
+ }
+ _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
+ triangulator.Init(points, points_count, _Data->TempBuffer.Data);
+ while (triangulator._TrianglesLeft > 0)
+ {
+ triangulator.GetNextTriangle(triangle);
+ _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]);
+ _IdxWritePtr += 3;
+ }
+ _VtxCurrentIdx += (ImDrawIdx)vtx_count;
+ }
+}
//-----------------------------------------------------------------------------
// [SECTION] ImDrawListSplitter
@@ -1808,6 +2163,63 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
// [SECTION] ImDrawData
//-----------------------------------------------------------------------------
+void ImDrawData::Clear()
+{
+ Valid = false;
+ CmdListsCount = TotalIdxCount = TotalVtxCount = 0;
+ CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.
+ DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f);
+ OwnerViewport = NULL;
+}
+
+// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list
+// as long at it is expected that the result will be later merged into draw_data->CmdLists[].
+void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list)
+{
+ if (draw_list->CmdBuffer.Size == 0)
+ return;
+ if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)
+ return;
+
+ // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
+ // May trigger for you if you are using PrimXXX functions incorrectly.
+ IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
+ IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
+ if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
+ IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
+
+ // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
+ // If this assert triggers because you are drawing lots of stuff manually:
+ // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
+ // Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
+ // - If you want large meshes with more than 64K vertices, you can either:
+ // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
+ // Most example backends already support this from 1.71. Pre-1.71 backends won't.
+ // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
+ // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
+ // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
+ // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
+ // Your own engine or render API may use different parameters or function calls to specify index sizes.
+ // 2 and 4 bytes indices are generally supported by most graphics API.
+ // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
+ // the 64K limit to split your draw commands in multiple draw lists.
+ if (sizeof(ImDrawIdx) == 2)
+ IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
+
+ // Add to output list + records state in ImDrawData
+ out_list->push_back(draw_list);
+ draw_data->CmdListsCount++;
+ draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;
+ draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;
+}
+
+void ImDrawData::AddDrawList(ImDrawList* draw_list)
+{
+ IM_ASSERT(CmdLists.Size == CmdListsCount);
+ draw_list->_PopUnusedDrawCmd();
+ ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list);
+}
+
// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
void ImDrawData::DeIndexAllBuffers()
{
@@ -1832,15 +2244,9 @@ void ImDrawData::DeIndexAllBuffers()
// or if there is a difference between your window resolution and framebuffer resolution.
void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
{
- for (int i = 0; i < CmdListsCount; i++)
- {
- ImDrawList* cmd_list = CmdLists[i];
- for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
- {
- ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i];
- cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y);
- }
- }
+ for (ImDrawList* draw_list : CmdLists)
+ for (ImDrawCmd& cmd : draw_list->CmdBuffer)
+ cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y);
}
//-----------------------------------------------------------------------------
@@ -1896,6 +2302,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve
}
}
+void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)
+{
+ ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
+ ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
+ for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
+ vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;
+}
+
//-----------------------------------------------------------------------------
// [SECTION] ImFontConfig
//-----------------------------------------------------------------------------
@@ -1904,10 +2318,11 @@ ImFontConfig::ImFontConfig()
{
memset(this, 0, sizeof(*this));
FontDataOwnedByAtlas = true;
- OversampleH = 3; // FIXME: 2 may be a better default?
+ OversampleH = 2;
OversampleV = 1;
GlyphMaxAdvanceX = FLT_MAX;
RasterizerMultiply = 1.0f;
+ RasterizerDensity = 1.0f;
EllipsisChar = (ImWchar)-1;
}
@@ -1981,19 +2396,19 @@ ImFontAtlas::~ImFontAtlas()
void ImFontAtlas::ClearInputData()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
- for (int i = 0; i < ConfigData.Size; i++)
- if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas)
+ for (ImFontConfig& font_cfg : ConfigData)
+ if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas)
{
- IM_FREE(ConfigData[i].FontData);
- ConfigData[i].FontData = NULL;
+ IM_FREE(font_cfg.FontData);
+ font_cfg.FontData = NULL;
}
// When clearing this we lose access to the font name and other information used to build the font.
- for (int i = 0; i < Fonts.Size; i++)
- if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size)
+ for (ImFont* font : Fonts)
+ if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size)
{
- Fonts[i]->ConfigData = NULL;
- Fonts[i]->ConfigDataCount = 0;
+ font->ConfigData = NULL;
+ font->ConfigDataCount = 0;
}
ConfigData.clear();
CustomRects.clear();
@@ -2090,6 +2505,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
+ ImFontAtlasUpdateConfigDataPointers(this);
+
// Invalidate texture
TexReady = false;
ClearTexData();
@@ -2126,7 +2543,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
if (font_cfg.Name[0] == '\0')
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
font_cfg.EllipsisChar = (ImWchar)0x0085;
- font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
+ font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
@@ -2156,13 +2573,14 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
}
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
-ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
+ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
IM_ASSERT(font_cfg.FontData == NULL);
- font_cfg.FontData = ttf_data;
- font_cfg.FontDataSize = ttf_size;
+ IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.
+ font_cfg.FontData = font_data;
+ font_cfg.FontDataSize = font_data_size;
font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
if (glyph_ranges)
font_cfg.GlyphRanges = glyph_ranges;
@@ -2377,7 +2795,10 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
+ {
+ IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
return false;
+ }
// Measure highest codepoints
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
@@ -2459,7 +2880,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// Convert our ranges in the format stb_truetype wants
ImFontConfig& cfg = atlas->ConfigData[src_i];
- src_tmp.PackRange.font_size = cfg.SizePixels;
+ src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity;
src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;
@@ -2468,7 +2889,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
- const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels);
+ const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity);
const int padding = atlas->TexGlyphPadding;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
{
@@ -2564,12 +2985,14 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
- const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
- const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
+ const float ascent = ImCeil(unscaled_ascent * font_scale);
+ const float descent = ImFloor(unscaled_descent * font_scale);
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
+ const float inv_rasterization_scale = 1.0f / cfg.RasterizerDensity;
+
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
{
// Register glyph
@@ -2578,7 +3001,11 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
stbtt_aligned_quad q;
float unused_x = 0.0f, unused_y = 0.0f;
stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0);
- dst_font->AddGlyph(&cfg, (ImWchar)codepoint, q.x0 + font_off_x, q.y0 + font_off_y, q.x1 + font_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance);
+ float x0 = q.x0 * inv_rasterization_scale + font_off_x;
+ float y0 = q.y0 * inv_rasterization_scale + font_off_y;
+ float x1 = q.x1 * inv_rasterization_scale + font_off_x;
+ float y1 = q.y1 * inv_rasterization_scale + font_off_y;
+ dst_font->AddGlyph(&cfg, (ImWchar)codepoint, x0, y0, x1, y1, q.s0, q.t0, q.s1, q.t1, pc.xadvance * inv_rasterization_scale);
}
}
@@ -2598,19 +3025,31 @@ const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype()
#endif // IMGUI_ENABLE_STB_TRUETYPE
+void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas)
+{
+ for (ImFontConfig& font_cfg : atlas->ConfigData)
+ {
+ ImFont* font = font_cfg.DstFont;
+ if (!font_cfg.MergeMode)
+ {
+ font->ConfigData = &font_cfg;
+ font->ConfigDataCount = 0;
+ }
+ font->ConfigDataCount++;
+ }
+}
+
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
{
if (!font_config->MergeMode)
{
font->ClearOutputData();
font->FontSize = font_config->SizePixels;
- font->ConfigData = font_config;
- font->ConfigDataCount = 0;
+ IM_ASSERT(font->ConfigData == font_config);
font->ContainerAtlas = atlas;
font->Ascent = ascent;
font->Descent = descent;
}
- font->ConfigDataCount++;
}
void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)
@@ -2757,6 +3196,13 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
// Note: this is called / shared by both the stb_truetype and the FreeType builder
void ImFontAtlasBuildInit(ImFontAtlas* atlas)
{
+ // Round font size
+ // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
+ // - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.
+ // - We may support it better later and remove this rounding.
+ for (ImFontConfig& cfg : atlas->ConfigData)
+ cfg.SizePixels = ImTrunc(cfg.SizePixels);
+
// Register texture region for mouse cursors or standard white pixels
if (atlas->PackIdMouseCursors < 0)
{
@@ -2798,9 +3244,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
}
// Build all fonts lookup tables
- for (int i = 0; i < atlas->Fonts.Size; i++)
- if (atlas->Fonts[i]->DirtyLookupTables)
- atlas->Fonts[i]->BuildLookupTable();
+ for (ImFont* font : atlas->Fonts)
+ if (font->DirtyLookupTables)
+ font->BuildLookupTable();
atlas->TexReady = true;
}
@@ -3165,6 +3611,7 @@ void ImFont::BuildLookupTable()
max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);
// Build lookup table
+ IM_ASSERT(Glyphs.Size > 0 && "Font has not loaded glyph!");
IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
IndexAdvanceX.clear();
IndexLookup.clear();
@@ -3281,7 +3728,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa
advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX);
if (advance_x != advance_x_original)
{
- float char_off_x = cfg->PixelSnapH ? ImFloor((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
+ float char_off_x = cfg->PixelSnapH ? ImTrunc((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
x0 += char_off_x;
x1 += char_off_x;
}
@@ -3549,8 +3996,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
if (glyph->Colored)
col |= ~IM_COL32_A_MASK;
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
- float x = IM_FLOOR(pos.x);
- float y = IM_FLOOR(pos.y);
+ float x = IM_TRUNC(pos.x);
+ float y = IM_TRUNC(pos.y);
draw_list->PrimReserve(6, 4);
draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
}
@@ -3562,8 +4009,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
// Align to be pixel perfect
- float x = IM_FLOOR(pos.x);
- float y = IM_FLOOR(pos.y);
+ float x = IM_TRUNC(pos.x);
+ float y = IM_TRUNC(pos.y);
if (y > clip_rect.w)
return;
@@ -3634,6 +4081,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
{
x = start_x;
y += line_height;
+ if (y > clip_rect.w)
+ break; // break out of main loop
word_wrap_eol = NULL;
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
continue;
@@ -3863,8 +4312,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
}
else
{
- draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL
- draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR
+ draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL
+ draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR
}
if (p1.x > rect.Min.x + rounding)
{
@@ -3883,8 +4332,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
}
else
{
- draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR
- draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR
+ draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR
+ draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR
}
}
draw_list->PathFillConvex(col);
@@ -4071,8 +4520,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
//-----------------------------------------------------------------------------
// ProggyClean.ttf
// Copyright (c) 2004, 2005 Tristan Grimmer
-// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip)
-// Download and more information at http://upperbounds.net
+// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
+// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
//-----------------------------------------------------------------------------
// File: 'ProggyClean.ttf' (41208 bytes)
// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).
diff --git a/extern/src/imgui/imgui_impl_glfw.cpp b/extern/src/imgui/imgui_impl_glfw.cpp
index 6e81819e..f5d7daa2 100644
--- a/extern/src/imgui/imgui_impl_glfw.cpp
+++ b/extern/src/imgui/imgui_impl_glfw.cpp
@@ -12,11 +12,18 @@
// 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
+// Learn about Dear ImGui:
+// - FAQ https://dearimgui.com/faq
+// - Getting Started https://dearimgui.com/getting-started
+// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
+// - Introduction, links and more at the top of imgui.cpp
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
+// 2023-12-19: Emscripten: Added ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to register canvas selector and auto-resize GLFW window.
+// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys.
+// 2023-07-18: Inputs: Revert ignoring mouse data on GLFW_CURSOR_DISABLED as it can be used differently. User may set ImGuiConfigFLags_NoMouse if desired. (#5625, #6609)
+// 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491)
// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702)
// 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034)
// 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240)
@@ -26,7 +33,7 @@
// 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785)
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
-// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position.
+// 2022-09-01: Inputs: Honor GLFW_CURSOR_DISABLED by not setting mouse position *EDIT* Reverted 2023-07-18.
// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX.
// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11.
// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend.
@@ -63,6 +70,7 @@
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
+#ifndef IMGUI_DISABLE
#include "imgui_impl_glfw.h"
// Clang warnings with -Weverything
@@ -77,11 +85,15 @@
#ifdef _WIN32
#undef APIENTRY
+#ifndef GLFW_EXPOSE_NATIVE_WIN32
#define GLFW_EXPOSE_NATIVE_WIN32
+#endif
#include // for glfwGetWin32Window()
#endif
#ifdef __APPLE__
+#ifndef GLFW_EXPOSE_NATIVE_COCOA
#define GLFW_EXPOSE_NATIVE_COCOA
+#endif
#include // for glfwGetCocoaWindow()
#endif
@@ -106,7 +118,7 @@ enum GlfwClientApi
{
GlfwClientApi_Unknown,
GlfwClientApi_OpenGL,
- GlfwClientApi_Vulkan
+ GlfwClientApi_Vulkan,
};
struct ImGui_ImplGlfw_Data
@@ -119,6 +131,9 @@ struct ImGui_ImplGlfw_Data
ImVec2 LastValidMousePos;
bool InstalledCallbacks;
bool CallbacksChainForAllWindows;
+#ifdef __EMSCRIPTEN__
+ const char* CanvasSelector;
+#endif
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
GLFWwindowfocusfun PrevUserCallbackWindowFocus;
@@ -268,6 +283,18 @@ static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
case GLFW_KEY_F10: return ImGuiKey_F10;
case GLFW_KEY_F11: return ImGuiKey_F11;
case GLFW_KEY_F12: return ImGuiKey_F12;
+ case GLFW_KEY_F13: return ImGuiKey_F13;
+ case GLFW_KEY_F14: return ImGuiKey_F14;
+ case GLFW_KEY_F15: return ImGuiKey_F15;
+ case GLFW_KEY_F16: return ImGuiKey_F16;
+ case GLFW_KEY_F17: return ImGuiKey_F17;
+ case GLFW_KEY_F18: return ImGuiKey_F18;
+ case GLFW_KEY_F19: return ImGuiKey_F19;
+ case GLFW_KEY_F20: return ImGuiKey_F20;
+ case GLFW_KEY_F21: return ImGuiKey_F21;
+ case GLFW_KEY_F22: return ImGuiKey_F22;
+ case GLFW_KEY_F23: return ImGuiKey_F23;
+ case GLFW_KEY_F24: return ImGuiKey_F24;
default: return ImGuiKey_None;
}
}
@@ -384,8 +411,6 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackCursorPos(window, x, y);
- if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
- return;
ImGuiIO& io = ImGui::GetIO();
io.AddMousePosEvent((float)x, (float)y);
@@ -399,8 +424,6 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window))
bd->PrevUserCallbackCursorEnter(window, entered);
- if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
- return;
ImGuiIO& io = ImGui::GetIO();
if (entered)
@@ -474,7 +497,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara
ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo());
break;
}
- return ::CallWindowProc(bd->GlfwWndProc, hWnd, msg, wParam, lParam);
+ return ::CallWindowProcW(bd->GlfwWndProc, hWnd, msg, wParam, lParam);
}
#endif
@@ -533,6 +556,7 @@ void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows)
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
{
ImGuiIO& io = ImGui::GetIO();
+ IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
//printf("GLFW_VERSION: %d.%d.%d (%d)", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION, GLFW_VERSION_COMBINED);
@@ -598,9 +622,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
// Windows: register a WndProc hook so we can intercept some messages.
#ifdef _WIN32
- bd->GlfwWndProc = (WNDPROC)::GetWindowLongPtr((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC);
+ bd->GlfwWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC);
IM_ASSERT(bd->GlfwWndProc != nullptr);
- ::SetWindowLongPtr((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
+ ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
#endif
bd->ClientApi = client_api;
@@ -630,6 +654,9 @@ void ImGui_ImplGlfw_Shutdown()
if (bd->InstalledCallbacks)
ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
+#ifdef __EMSCRIPTEN__
+ emscripten_set_wheel_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr);
+#endif
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
glfwDestroyCursor(bd->MouseCursors[cursor_n]);
@@ -637,7 +664,7 @@ void ImGui_ImplGlfw_Shutdown()
// Windows: register a WndProc hook so we can intercept some messages.
#ifdef _WIN32
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
- ::SetWindowLongPtr((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->GlfwWndProc);
+ ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->GlfwWndProc);
bd->GlfwWndProc = nullptr;
#endif
@@ -652,16 +679,9 @@ static void ImGui_ImplGlfw_UpdateMouseData()
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
- if (glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
- {
- io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
- return;
- }
-
// (those braces are here to reduce diff with multi-viewports support in 'docking' branch)
{
GLFWwindow* window = bd->Window;
-
#ifdef __EMSCRIPTEN__
const bool is_window_focused = true;
#else
@@ -715,7 +735,7 @@ void ImGui_ImplGlfw_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
- IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplGlfw_InitForXXX()?");
+ IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?");
// Setup display size (every frame to accommodate for window resizing)
int w, h;
@@ -727,7 +747,10 @@ void ImGui_ImplGlfw_NewFrame()
io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h);
// Setup time step
+ // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644)
double current_time = glfwGetTime();
+ if (current_time <= bd->Time)
+ current_time = bd->Time + 0.00001f;
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
bd->Time = current_time;
@@ -735,6 +758,46 @@ void ImGui_ImplGlfw_NewFrame()
ImGui_ImplGlfw_UpdateMouseCursor();
}
+#ifdef __EMSCRIPTEN__
+static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data)
+{
+ ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data;
+ double canvas_width, canvas_height;
+ emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height);
+ glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height);
+ return true;
+}
+
+static EM_BOOL ImGui_ImplEmscripten_FullscreenChangeCallback(int event_type, const EmscriptenFullscreenChangeEvent* event, void* user_data)
+{
+ ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data;
+ double canvas_width, canvas_height;
+ emscripten_get_element_css_size(bd->CanvasSelector, &canvas_width, &canvas_height);
+ glfwSetWindowSize(bd->Window, (int)canvas_width, (int)canvas_height);
+ return true;
+}
+
+// 'canvas_selector' is a CSS selector. The event listener is applied to the first element that matches the query.
+// STRING MUST PERSIST FOR THE APPLICATION DURATION. PLEASE USE A STRING LITERAL OR ENSURE POINTER WILL STAY VALID.
+void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector)
+{
+ IM_ASSERT(canvas_selector != nullptr);
+ ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+ IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?");
+
+ bd->CanvasSelector = canvas_selector;
+ emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, bd, false, ImGui_ImplGlfw_OnCanvasSizeChange);
+ emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, bd, false, ImGui_ImplEmscripten_FullscreenChangeCallback);
+
+ // Change the size of the GLFW window according to the size of the canvas
+ ImGui_ImplGlfw_OnCanvasSizeChange(EMSCRIPTEN_EVENT_RESIZE, {}, bd);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
+
+#endif // #ifndef IMGUI_DISABLE
diff --git a/extern/src/imgui/imgui_impl_glfw.h b/extern/src/imgui/imgui_impl_glfw.h
index 698b0d4b..6a9acd05 100644
--- a/extern/src/imgui/imgui_impl_glfw.h
+++ b/extern/src/imgui/imgui_impl_glfw.h
@@ -11,11 +11,15 @@
// 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
+// Learn about Dear ImGui:
+// - FAQ https://dearimgui.com/faq
+// - Getting Started https://dearimgui.com/getting-started
+// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
+// - Introduction, links and more at the top of imgui.cpp
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
+#ifndef IMGUI_DISABLE
struct GLFWwindow;
struct GLFWmonitor;
@@ -26,6 +30,11 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool ins
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
+// Emscripten related initialization phase methods
+#ifdef __EMSCRIPTEN__
+IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector);
+#endif
+
// GLFW callbacks install
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
@@ -45,3 +54,5 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);
+
+#endif // #ifndef IMGUI_DISABLE
diff --git a/extern/src/imgui/imgui_impl_opengl3.cpp b/extern/src/imgui/imgui_impl_opengl3.cpp
index 338ed9e2..58603a04 100644
--- a/extern/src/imgui/imgui_impl_opengl3.cpp
+++ b/extern/src/imgui/imgui_impl_opengl3.cpp
@@ -14,11 +14,20 @@
// 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
+// Learn about Dear ImGui:
+// - FAQ https://dearimgui.com/faq
+// - Getting Started https://dearimgui.com/getting-started
+// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
+// - Introduction, links and more at the top of imgui.cpp
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
+// 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562)
+// 2024-04-16: OpenGL: Detect ES3 contexts on desktop based on version string, to e.g. avoid calling glPolygonMode() on them. (#7447)
+// 2024-01-09: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink.
+// 2023-11-08: OpenGL: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1", accommodating for NetBSD systems having only "libGL.so.3" available. (#6983)
+// 2023-10-05: OpenGL: Rename symbols in our internal loader so that LTO compilation with another copy of gl3w is possible. (#6875, #6668, #4445)
+// 2023-06-20: OpenGL: Fixed erroneous use glGetIntegerv(GL_CONTEXT_PROFILE_MASK) on contexts lower than 3.2. (#6539, #6333)
// 2023-05-09: OpenGL: Support for glBindSampler() backup/restore on ES3. (#6375)
// 2023-04-18: OpenGL: Restore front and back polygon mode separately when supported by context. (#6333)
// 2023-03-23: OpenGL: Properly restoring "no shader program bound" if it was the case prior to running the rendering function. (#6267, #6220, #6224)
@@ -103,13 +112,10 @@
#endif
#include "imgui.h"
+#ifndef IMGUI_DISABLE
#include "imgui_impl_opengl3.h"
#include
-#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
-#include // intptr_t
-#else
#include // intptr_t
-#endif
#if defined(__APPLE__)
#include
#endif
@@ -172,9 +178,21 @@
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
#endif
-// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have.
-#ifdef GL_POLYGON_MODE
-#define IMGUI_IMPL_HAS_POLYGON_MODE
+// Desktop GL 2.0+ has extension and glPolygonMode() which GL ES and WebGL don't have..
+// A desktop ES context can technically compile fine with our loader, so we also perform a runtime checks
+#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
+#define IMGUI_IMPL_OPENGL_HAS_EXTENSIONS // has glGetIntegerv(GL_NUM_EXTENSIONS)
+#define IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE // may have glPolygonMode()
+#endif
+
+// Desktop GL 2.1+ and GL ES 3.0+ have glBindBuffer() with GL_PIXEL_UNPACK_BUFFER target.
+#if !defined(IMGUI_IMPL_OPENGL_ES2)
+#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
+#endif
+
+// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
+#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
+#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
#endif
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
@@ -187,16 +205,6 @@
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
#endif
-// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
-#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
-#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
-#endif
-
-// Desktop GL use extension detection
-#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
-#define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
-#endif
-
// [Debugging]
//#define IMGUI_IMPL_OPENGL_DEBUG
#ifdef IMGUI_IMPL_OPENGL_DEBUG
@@ -225,6 +233,7 @@ struct ImGui_ImplOpenGL3_Data
unsigned int VboHandle, ElementsHandle;
GLsizeiptr VertexBufferSize;
GLsizeiptr IndexBufferSize;
+ bool HasPolygonMode;
bool HasClipOrigin;
bool UseBufferSubData;
@@ -266,6 +275,7 @@ struct ImGui_ImplOpenGL3_VtxAttribState
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
ImGuiIO& io = ImGui::GetIO();
+ IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
// Initialize our loader
@@ -283,23 +293,33 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
io.BackendRendererName = "imgui_impl_opengl3";
// Query for GL version (e.g. 320 for GL 3.2)
-#if !defined(IMGUI_IMPL_OPENGL_ES2)
+#if defined(IMGUI_IMPL_OPENGL_ES2)
+ // GLES 2
+ bd->GlVersion = 200;
+ bd->GlProfileIsES2 = true;
+#else
+ // Desktop or GLES 3
+ const char* gl_version_str = (const char*)glGetString(GL_VERSION);
GLint major = 0;
GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
if (major == 0 && minor == 0)
- {
- // Query GL_VERSION in desktop GL 2.x, the string will start with "."
- const char* gl_version = (const char*)glGetString(GL_VERSION);
- sscanf(gl_version, "%d.%d", &major, &minor);
- }
+ sscanf(gl_version_str, "%d.%d", &major, &minor); // Query GL_VERSION in desktop GL 2.x, the string will start with "."
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
#if defined(GL_CONTEXT_PROFILE_MASK)
- glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask);
+ if (bd->GlVersion >= 320)
+ glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &bd->GlProfileMask);
bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0;
#endif
+#if defined(IMGUI_IMPL_OPENGL_ES3)
+ bd->GlProfileIsES3 = true;
+#else
+ if (strncmp(gl_version_str, "OpenGL ES 3", 11) == 0)
+ bd->GlProfileIsES3 = true;
+#endif
+
bd->UseBufferSubData = false;
/*
// Query vendor to enable glBufferSubData kludge
@@ -309,16 +329,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
bd->UseBufferSubData = true;
#endif
*/
-#elif defined(IMGUI_IMPL_OPENGL_ES2)
- bd->GlVersion = 200; // GLES 2
- bd->GlProfileIsES2 = true;
-#elif defined(IMGUI_IMPL_OPENGL_ES3)
- bd->GlVersion = 200; // Don't raise version as it is intended as a desktop version check for now.
- bd->GlProfileIsES3 = true;
#endif
#ifdef IMGUI_IMPL_OPENGL_DEBUG
- printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]
+ printf("GlVersion = %d, \"%s\"\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2 = %d, GlProfileIsES3 = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, gl_version_str, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]
#endif
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
@@ -350,8 +364,11 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture);
// Detect extensions we support
+#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
+ bd->HasPolygonMode = (!bd->GlProfileIsES2 && !bd->GlProfileIsES3);
+#endif
bd->HasClipOrigin = (bd->GlVersion >= 450);
-#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
+#ifdef IMGUI_IMPL_OPENGL_HAS_EXTENSIONS
GLint num_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for (GLint i = 0; i < num_extensions; i++)
@@ -381,7 +398,7 @@ void ImGui_ImplOpenGL3_Shutdown()
void ImGui_ImplOpenGL3_NewFrame()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
- IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplOpenGL3_Init()?");
+ IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL3_Init()?");
if (!bd->ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
@@ -403,8 +420,9 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
if (bd->GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART);
#endif
-#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
+ if (bd->HasPolygonMode)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
@@ -455,9 +473,9 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos));
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV));
GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor));
- GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)));
- GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)));
- GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)));
+ GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, pos)));
+ GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, uv)));
+ GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col)));
}
// OpenGL3 Render function.
@@ -492,8 +510,8 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif
-#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
- GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
+#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
+ GLint last_polygon_mode[2]; if (bd->HasPolygonMode) { glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); }
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
@@ -631,18 +649,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
#endif
-#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
+#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
// Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons
- if (bd->GlVersion <= 310 || bd->GlProfileIsCompat)
- {
- glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]);
- glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]);
- }
- else
- {
- glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
- }
-#endif // IMGUI_IMPL_HAS_POLYGON_MODE
+ if (bd->HasPolygonMode) { if (bd->GlVersion <= 310 || bd->GlProfileIsCompat) { glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); } else { glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); } }
+#endif // IMGUI_IMPL_OPENGL_MAY_HAVE_POLYGON_MODE
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
@@ -739,6 +749,10 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
+#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
+ GLint last_pixel_unpack_buffer = 0;
+ if (bd->GlVersion >= 210) { glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_pixel_unpack_buffer); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); }
+#endif
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
@@ -912,6 +926,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
+#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_BUFFER_PIXEL_UNPACK
+ if (bd->GlVersion >= 210) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, last_pixel_unpack_buffer); }
+#endif
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(last_vertex_array);
#endif
@@ -928,9 +945,13 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
+//-----------------------------------------------------------------------------
+
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
+
+#endif // #ifndef IMGUI_DISABLE
diff --git a/extern/src/imgui/imgui_impl_opengl3.h b/extern/src/imgui/imgui_impl_opengl3.h
index 77d11801..d66a15eb 100644
--- a/extern/src/imgui/imgui_impl_opengl3.h
+++ b/extern/src/imgui/imgui_impl_opengl3.h
@@ -14,8 +14,11 @@
// 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
+// Learn about Dear ImGui:
+// - FAQ https://dearimgui.com/faq
+// - Getting Started https://dearimgui.com/getting-started
+// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
+// - Introduction, links and more at the top of imgui.cpp
// About GLSL version:
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
@@ -24,6 +27,7 @@
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
+#ifndef IMGUI_DISABLE
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
@@ -37,9 +41,9 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
-// Specific OpenGL ES versions
-//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
-//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
+// Configuration flags to add in your imconfig file:
+//#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten)
+//#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android)
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
@@ -58,3 +62,5 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
#endif
#endif
+
+#endif // #ifndef IMGUI_DISABLE
diff --git a/extern/src/imgui/imgui_impl_opengl3_loader.h b/extern/src/imgui/imgui_impl_opengl3_loader.h
index 7ca72e37..3fbc3481 100644
--- a/extern/src/imgui/imgui_impl_opengl3_loader.h
+++ b/extern/src/imgui/imgui_impl_opengl3_loader.h
@@ -10,7 +10,7 @@
// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE.
//
// IF YOU GET BUILD ERRORS IN THIS FILE (commonly macro redefinitions or function redefinitions):
-// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCUDING 'imgui_impl_opengl3_loader.h'
+// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCLUDING 'imgui_impl_opengl3_loader.h'
// IN THE SAME COMPILATION UNIT AS ONE OF YOUR FILE WHICH IS USING A THIRD-PARTY OPENGL LOADER.
// (e.g. COULD HAPPEN IF YOU ARE DOING A UNITY/JUMBO BUILD, OR INCLUDING .CPP FILES FROM OTHERS)
// YOU SHOULD NOT BUILD BOTH IN THE SAME COMPILATION UNIT.
@@ -18,7 +18,7 @@
// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT.
//
// Regenerate with:
-// python gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
+// python3 gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
//
// More info:
// https://github.com/dearimgui/gl3w_stripped
@@ -346,6 +346,10 @@ GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean
GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
#endif
#endif /* GL_VERSION_2_0 */
+#ifndef GL_VERSION_2_1
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
+#endif /* GL_VERSION_2_1 */
#ifndef GL_VERSION_3_0
typedef khronos_uint16_t GLhalf;
#define GL_MAJOR_VERSION 0x821B
@@ -467,7 +471,7 @@ GL3W_API int imgl3wIsSupported(int major, int minor);
GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
/* gl3w internal state */
-union GL3WProcs {
+union ImGL3WProcs {
GL3WglProc ptr[59];
struct {
PFNGLACTIVETEXTUREPROC ActiveTexture;
@@ -532,7 +536,7 @@ union GL3WProcs {
} gl;
};
-GL3W_API extern union GL3WProcs imgl3wProcs;
+GL3W_API extern union ImGL3WProcs imgl3wProcs;
/* OpenGL functions */
#define glActiveTexture imgl3wProcs.gl.ActiveTexture
@@ -608,7 +612,7 @@ extern "C" {
#include
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define GL3W_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
@@ -661,26 +665,123 @@ static GL3WglProc get_proc(const char *proc)
#else
#include
-static void *libgl;
-static GL3WglProc (*glx_get_proc_address)(const GLubyte *);
+static void* libgl; // OpenGL library
+static void* libglx; // GLX library
+static void* libegl; // EGL library
+static GL3WGetProcAddressProc gl_get_proc_address;
+
+static void close_libgl(void)
+{
+ if (libgl) {
+ dlclose(libgl);
+ libgl = NULL;
+ }
+ if (libegl) {
+ dlclose(libegl);
+ libegl = NULL;
+ }
+ if (libglx) {
+ dlclose(libglx);
+ libglx = NULL;
+ }
+}
+
+static int is_library_loaded(const char* name, void** lib)
+{
+ *lib = dlopen(name, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ return *lib != NULL;
+}
+
+static int open_libs(void)
+{
+ // On Linux we have two APIs to get process addresses: EGL and GLX.
+ // EGL is supported under both X11 and Wayland, whereas GLX is X11-specific.
+
+ libgl = NULL;
+ libegl = NULL;
+ libglx = NULL;
+
+ // First check what's already loaded, the windowing library might have
+ // already loaded either EGL or GLX and we want to use the same one.
+
+ if (is_library_loaded("libEGL.so.1", &libegl) ||
+ is_library_loaded("libGLX.so.0", &libglx)) {
+ libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
+ if (libgl)
+ return GL3W_OK;
+ else
+ close_libgl();
+ }
+
+ if (is_library_loaded("libGL.so", &libgl))
+ return GL3W_OK;
+ if (is_library_loaded("libGL.so.1", &libgl))
+ return GL3W_OK;
+ if (is_library_loaded("libGL.so.3", &libgl))
+ return GL3W_OK;
+
+ // Neither is already loaded, so we have to load one. Try EGL first
+ // because it is supported under both X11 and Wayland.
+
+ // Load OpenGL + EGL
+ libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
+ libegl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL);
+ if (libgl && libegl)
+ return GL3W_OK;
+ else
+ close_libgl();
+
+ // Fall back to legacy libGL, which includes GLX
+ // While most systems use libGL.so.1, NetBSD seems to use that libGL.so.3. See https://github.com/ocornut/imgui/issues/6983
+ libgl = dlopen("libGL.so", RTLD_LAZY | RTLD_LOCAL);
+ if (!libgl)
+ libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL);
+ if (!libgl)
+ libgl = dlopen("libGL.so.3", RTLD_LAZY | RTLD_LOCAL);
+
+ if (libgl)
+ return GL3W_OK;
+
+ return GL3W_ERROR_LIBRARY_OPEN;
+}
static int open_libgl(void)
{
- libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL);
- if (!libgl)
+ int res = open_libs();
+ if (res)
+ return res;
+
+ if (libegl)
+ *(void**)(&gl_get_proc_address) = dlsym(libegl, "eglGetProcAddress");
+ else if (libglx)
+ *(void**)(&gl_get_proc_address) = dlsym(libglx, "glXGetProcAddressARB");
+ else
+ *(void**)(&gl_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
+
+ if (!gl_get_proc_address) {
+ close_libgl();
return GL3W_ERROR_LIBRARY_OPEN;
- *(void **)(&glx_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
+ }
+
return GL3W_OK;
}
-static void close_libgl(void) { dlclose(libgl); }
-
-static GL3WglProc get_proc(const char *proc)
+static GL3WglProc get_proc(const char* proc)
{
- GL3WglProc res;
- res = glx_get_proc_address((const GLubyte *)proc);
+ GL3WglProc res = NULL;
+
+ // Before EGL version 1.5, eglGetProcAddress doesn't support querying core
+ // functions and may return a dummy function if we try, so try to load the
+ // function from the GL library directly first.
+ if (libegl)
+ *(void**)(&res) = dlsym(libgl, proc);
+
if (!res)
- *(void **)(&res) = dlsym(libgl, proc);
+ res = gl_get_proc_address(proc);
+
+ if (!libegl && !res)
+ *(void**)(&res) = dlsym(libgl, proc);
+
return res;
}
#endif
@@ -794,12 +895,12 @@ static const char *proc_names[] = {
"glViewport",
};
-GL3W_API union GL3WProcs imgl3wProcs;
+GL3W_API union ImGL3WProcs imgl3wProcs;
static void load_procs(GL3WGetProcAddressProc proc)
{
size_t i;
- for (i = 0; i < ARRAY_SIZE(proc_names); i++)
+ for (i = 0; i < GL3W_ARRAY_SIZE(proc_names); i++)
imgl3wProcs.ptr[i] = proc(proc_names[i]);
}
diff --git a/extern/src/imgui/imgui_internal.h b/extern/src/imgui/imgui_internal.h
index 3311d659..f2ac5ad0 100644
--- a/extern/src/imgui/imgui_internal.h
+++ b/extern/src/imgui/imgui_internal.h
@@ -1,12 +1,7 @@
-// dear imgui, v1.89.6
+// dear imgui, v1.90.7
// (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
-// To implement maths operators for ImVec2 (disabled by default to not conflict with using IM_VEC2_CLASS_EXTRA with your own math types+operators), use:
-/*
-#define IMGUI_DEFINE_MATH_OPERATORS
-#include "imgui_internal.h"
-*/
/*
@@ -20,9 +15,12 @@ Index of this file:
// [SECTION] Generic helpers
// [SECTION] ImDrawList support
// [SECTION] Widgets support: flags, enums, data structures
+// [SECTION] Data types support
+// [SECTION] Popup support
// [SECTION] Inputs support
// [SECTION] Clipper support
// [SECTION] Navigation support
+// [SECTION] Typing-select support
// [SECTION] Columns support
// [SECTION] Multi-select support
// [SECTION] Docking support
@@ -81,7 +79,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx'
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
-#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloorSigned()
+#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor()
#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h
#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h
#pragma clang diagnostic ignored "-Wold-style-cast"
@@ -89,6 +87,8 @@ Index of this file:
#pragma clang diagnostic ignored "-Wdouble-promotion"
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn'
+#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
@@ -126,7 +126,7 @@ struct ImDrawListSharedData; // Data shared between all ImDrawList instan
struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it
struct ImGuiContext; // Main Dear ImGui context
struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine
-struct ImGuiDataVarInfo; // Variable information (e.g. to avoid style variables from an enum)
+struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum)
struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum
struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup()
struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box
@@ -135,6 +135,7 @@ struct ImGuiLastItemData; // Status storage for last submitted items
struct ImGuiLocEntry; // A localization entry.
struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only
struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result
+struct ImGuiNavTreeNodeData; // Temporary storage for last TreeNode() being a Left arrow landing candidate.
struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions
struct ImGuiNextWindowData; // Storage for SetNextWindow** functions
struct ImGuiNextItemData; // Storage for SetNextItem** functions
@@ -147,11 +148,14 @@ struct ImGuiStyleMod; // Stacked style modifier, backup of modifie
struct ImGuiTabBar; // Storage for a tab bar
struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
struct ImGuiTable; // Storage for a table
+struct ImGuiTableHeaderData; // Storage for TableAngledHeadersRow()
struct ImGuiTableColumn; // Storage for one column of a table
struct ImGuiTableInstanceData; // Storage for one instance of a same table
struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables.
struct ImGuiTableSettings; // Storage for a table .ini settings
struct ImGuiTableColumnsSettings; // Storage for a column .ini settings
+struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest()
+struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public)
struct ImGuiWindow; // Storage for one window
struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window)
struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session)
@@ -165,7 +169,6 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow();
-typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressed(), IsMouseClicked(), SetKeyOwner(), SetItemKeyOwner() etc.
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
@@ -177,6 +180,8 @@ typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // F
typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx()
typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx()
typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx()
+typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest()
+typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);
@@ -196,13 +201,13 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
namespace ImStb
{
-#undef STB_TEXTEDIT_STRING
-#undef STB_TEXTEDIT_CHARTYPE
-#define STB_TEXTEDIT_STRING ImGuiInputTextState
-#define STB_TEXTEDIT_CHARTYPE ImWchar
-#define STB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f)
-#define STB_TEXTEDIT_UNDOSTATECOUNT 99
-#define STB_TEXTEDIT_UNDOCHARCOUNT 999
+#undef IMSTB_TEXTEDIT_STRING
+#undef IMSTB_TEXTEDIT_CHARTYPE
+#define IMSTB_TEXTEDIT_STRING ImGuiInputTextState
+#define IMSTB_TEXTEDIT_CHARTYPE ImWchar
+#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f)
+#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
+#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
#include "imstb_textedit.h"
} // namespace ImStb
@@ -227,13 +232,14 @@ namespace ImStb
#else
#define IMGUI_DEBUG_LOG(...) ((void)0)
#endif
-#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
-#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
-#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
-#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
-#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
-#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
-#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
+#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
// Static Asserts
#define IM_STATIC_ASSERT(_COND) static_assert(_COND, "")
@@ -266,8 +272,13 @@ namespace ImStb
#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
+#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // ImTrunc() is not inlined in MSVC debug builds
#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) //
+#define IM_STRINGIFY_HELPER(_X) #_X
+#define IM_STRINGIFY(_X) IM_STRINGIFY_HELPER(_X) // Preprocessor idiom to stringify e.g. an integer.
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+#define IM_FLOOR IM_TRUNC
+#endif
// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
#ifdef _MSC_VER
@@ -292,16 +303,28 @@ namespace ImStb
#elif defined(__clang__)
#define IM_DEBUG_BREAK() __builtin_debugtrap()
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03")
+#define IM_DEBUG_BREAK() __asm__ volatile("int3;nop")
#elif defined(__GNUC__) && defined(__thumb__)
#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01")
#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)
-#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0");
+#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0")
#else
#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!
#endif
#endif // #ifndef IM_DEBUG_BREAK
+// 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.
+#if defined(_MSC_VER) && !defined(__clang__)
+#define IM_PRId64 "I64d"
+#define IM_PRIu64 "I64u"
+#define IM_PRIX64 "I64X"
+#else
+#define IM_PRId64 "lld"
+#define IM_PRIu64 "llu"
+#define IM_PRIX64 "llX"
+#endif
+
//-----------------------------------------------------------------------------
// [SECTION] Generic helpers
// Note that the ImXXX helpers functions are lower-level than ImGui functions.
@@ -345,18 +368,18 @@ static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v &
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
// Helpers: String
-IMGUI_API int ImStricmp(const char* str1, const char* str2);
-IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count);
-IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count);
-IMGUI_API char* ImStrdup(const char* str);
-IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str);
-IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c);
-IMGUI_API int ImStrlenW(const ImWchar* str);
+IMGUI_API int ImStricmp(const char* str1, const char* str2); // Case insensitive compare.
+IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); // Case insensitive compare to a certain count.
+IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); // Copy to a certain count and always zero terminate (strncpy doesn't).
+IMGUI_API char* ImStrdup(const char* str); // Duplicate a string.
+IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed.
+IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); // Find first occurrence of 'c' in string range.
IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line
-IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
-IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
-IMGUI_API void ImStrTrimBlanks(char* str);
-IMGUI_API const char* ImStrSkipBlank(const char* str);
+IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); // Find a substring in a string range.
+IMGUI_API void ImStrTrimBlanks(char* str); // Remove leading and trailing blanks from a buffer.
+IMGUI_API const char* ImStrSkipBlank(const char* str); // Find first non-blank character.
+IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string)
+IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line (ImWchar string)
IM_MSVC_RUNTIME_CHECKS_OFF
static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
@@ -383,6 +406,8 @@ IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, co
IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count)
IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8
IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8
+IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point.
+IMGUI_API int ImTextCountLines(const char* in_text, const char* in_text_end); // return number of lines taken by text. trailing carriage return doesn't count as an extra line.
// Helpers: File System
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
@@ -418,7 +443,6 @@ IM_MSVC_RUNTIME_CHECKS_OFF
#define ImAcos(X) acosf(X)
#define ImAtan2(Y, X) atan2f((Y), (X))
#define ImAtof(STR) atof(STR)
-//#define ImFloorStd(X) floorf(X) // We use our own, see ImFloor() and ImFloorSigned()
#define ImCeil(X) ceilf(X)
static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
static inline double ImPow(double x, double y) { return pow(x, y); }
@@ -456,10 +480,10 @@ static inline float ImSaturate(float f)
static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
-static inline float ImFloor(float f) { return (float)(int)(f); }
-static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
-static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
-static inline ImVec2 ImFloorSigned(const ImVec2& v) { return ImVec2(ImFloorSigned(v.x), ImFloorSigned(v.y)); }
+static inline float ImTrunc(float f) { return (float)(int)(f); }
+static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
+static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
+static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
@@ -478,7 +502,8 @@ IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const
IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);
IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);
IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w);
-inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; }
+inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; }
+inline bool ImTriangleIsClockwise(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0f; }
// Helper: ImVec1 (1D vector)
// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
@@ -522,6 +547,7 @@ struct IMGUI_API ImRect
ImVec2 GetBR() const { return Max; } // Bottom-right
bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; }
bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; }
+ bool ContainsWithPad(const ImVec2& p, const ImVec2& pad) const { return p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y; }
bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; }
void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; }
void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }
@@ -532,7 +558,7 @@ struct IMGUI_API ImRect
void TranslateY(float dy) { Min.y += dy; Max.y += dy; }
void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.
void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.
- void Floor() { Min.x = IM_FLOOR(Min.x); Min.y = IM_FLOOR(Min.y); Max.x = IM_FLOOR(Max.x); Max.y = IM_FLOOR(Max.y); }
+ void Floor() { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); }
bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); }
};
@@ -671,9 +697,6 @@ struct ImPool
int GetBufSize() const { return Buf.Size; }
int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere
T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); }
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- int GetSize() { return GetMapSize(); } // For ImPlot: should use GetMapSize() from (IMGUI_VERSION_NUM >= 18304)
-#endif
};
// Helper: ImChunkStream<>
@@ -697,7 +720,6 @@ struct ImChunkStream
int offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; }
T* ptr_from_offset(int off) { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); }
void swap(ImChunkStream& rhs) { rhs.Buf.swap(Buf); }
-
};
// Helper: ImGuiTextIndex<>
@@ -770,12 +792,10 @@ struct IMGUI_API ImDrawListSharedData
struct ImDrawDataBuilder
{
- ImVector Layers[2]; // Global layers for: regular, tooltip
+ ImVector* Layers[2]; // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData.
+ ImVector LayerData1;
- void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); }
- void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); }
- int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; }
- IMGUI_API void FlattenIntoSingleLayer();
+ ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); }
};
//-----------------------------------------------------------------------------
@@ -800,9 +820,11 @@ enum ImGuiItemFlags_
ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable()
+ ImGuiItemFlags_AllowOverlap = 1 << 9, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
// Controlled by widget code
ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
+ ImGuiItemFlags_HasSelectionUserData = 1 << 11, // false // Set by SetNextItemSelectionUserData()
};
// Status flags for an already submitted item
@@ -818,8 +840,9 @@ enum ImGuiItemStatusFlags_
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_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing.
- ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon)
- ImGuiItemStatusFlags_Visible = 1 << 9, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).
+ ImGuiItemStatusFlags_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).
+ ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid.
+ ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().
// Additional status + semantic for ImGuiTestEngine
#ifdef IMGUI_ENABLE_TEST_ENGINE
@@ -831,6 +854,14 @@ enum ImGuiItemStatusFlags_
#endif
};
+// Extend ImGuiHoveredFlags_
+enum ImGuiHoveredFlagsPrivate_
+{
+ ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay,
+ ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary,
+ ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_,
+};
+
// Extend ImGuiInputTextFlags_
enum ImGuiInputTextFlagsPrivate_
{
@@ -838,6 +869,7 @@ enum ImGuiInputTextFlagsPrivate_
ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline()
ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data
ImGuiInputTextFlags_MergedItem = 1 << 28, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
+ ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 29, // For internal use by InputScalar() and TempInputScalar()
};
// Extend ImGuiButtonFlags_
@@ -851,13 +883,13 @@ enum ImGuiButtonFlagsPrivate_
ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)
ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat
ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping
- ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap()
+ ImGuiButtonFlags_AllowOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable.
ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED]
//ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled
ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held
ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)
- ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used everytime an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags)
+ ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags)
ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item
ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
@@ -875,7 +907,7 @@ enum ImGuiComboFlagsPrivate_
enum ImGuiSliderFlagsPrivate_
{
ImGuiSliderFlags_Vertical = 1 << 20, // Should this slider be orientated vertically?
- ImGuiSliderFlags_ReadOnly = 1 << 21,
+ ImGuiSliderFlags_ReadOnly = 1 << 21, // Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags_ReadOnly instead.
};
// Extend ImGuiSelectableFlags_
@@ -896,6 +928,7 @@ enum ImGuiSelectableFlagsPrivate_
enum ImGuiTreeNodeFlagsPrivate_
{
ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20,
+ ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 21,// (FIXME-WIP) Turn Down arrow into an Up arrow, but reversed trees (#6517)
};
enum ImGuiSeparatorFlags_
@@ -925,7 +958,7 @@ enum ImGuiTextFlags_
enum ImGuiTooltipFlags_
{
ImGuiTooltipFlags_None = 0,
- ImGuiTooltipFlags_OverridePreviousTooltip = 1 << 0, // Override will clear/ignore previously submitted tooltip (defaults to append)
+ ImGuiTooltipFlags_OverridePrevious = 1 << 1, // Clear/ignore previously submitted tooltip (defaults to append)
};
// FIXME: this is in development, not exposed/functional as a generic feature yet.
@@ -959,43 +992,6 @@ enum ImGuiPlotType
ImGuiPlotType_Histogram,
};
-enum ImGuiPopupPositionPolicy
-{
- ImGuiPopupPositionPolicy_Default,
- ImGuiPopupPositionPolicy_ComboBox,
- ImGuiPopupPositionPolicy_Tooltip,
-};
-
-struct ImGuiDataVarInfo
-{
- ImGuiDataType Type;
- ImU32 Count; // 1+
- ImU32 Offset; // Offset in parent structure
- void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }
-};
-
-struct ImGuiDataTypeTempStorage
-{
- ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT
-};
-
-// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
-struct ImGuiDataTypeInfo
-{
- size_t Size; // Size in bytes
- const char* Name; // Short descriptive name for the type, for debugging
- const char* PrintFmt; // Default printf format for the type
- const char* ScanFmt; // Default scanf format for the type
-};
-
-// Extend ImGuiDataType_
-enum ImGuiDataTypePrivate_
-{
- ImGuiDataType_String = ImGuiDataType_COUNT + 1,
- ImGuiDataType_Pointer,
- ImGuiDataType_ID,
-};
-
// Stacked color modifier, backup of modified data so we can restore it
struct ImGuiColorMod
{
@@ -1032,6 +1028,7 @@ struct IMGUI_API ImGuiGroupData
ImGuiID WindowID;
ImVec2 BackupCursorPos;
ImVec2 BackupCursorMaxPos;
+ ImVec2 BackupCursorPosPrevLine;
ImVec1 BackupIndent;
ImVec1 BackupGroupOffset;
ImVec2 BackupCurrLineSize;
@@ -1039,6 +1036,7 @@ struct IMGUI_API ImGuiGroupData
ImGuiID BackupActiveIdIsAlive;
bool BackupActiveIdPreviousFrameIsAlive;
bool BackupHoveredIdIsAlive;
+ bool BackupIsSameLine;
bool EmitItem;
};
@@ -1078,7 +1076,7 @@ struct IMGUI_API ImGuiInputTextState
int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not.
ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
ImVector TextA; // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity.
- ImVector InitialTextA; // backup of end-user buffer at the time of focus (in UTF-8, unaltered)
+ ImVector InitialTextA; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)
int BufCapacityA; // end-user buffer capacity
float ScrollX; // horizontal scrolling/offset
@@ -1088,12 +1086,15 @@ struct IMGUI_API ImGuiInputTextState
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
bool Edited; // edited this frame
ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
+ bool ReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version.
+ int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet.
+ int ReloadSelectionEnd;
ImGuiInputTextState() { memset(this, 0, sizeof(*this)); }
void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }
void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); }
int GetUndoAvailCount() const { return Stb.undostate.undo_point; }
- int GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; }
+ int GetRedoAvailCount() const { return IMSTB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; }
void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation
// Cursor & Selection
@@ -1105,21 +1106,25 @@ struct IMGUI_API ImGuiInputTextState
int GetSelectionStart() const { return Stb.select_start; }
int GetSelectionEnd() const { return Stb.select_end; }
void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; }
+
+ // Reload user buf (WIP #2890)
+ // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this)
+ // strcpy(my_buf, "hello");
+ // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item
+ // state->ReloadUserBufAndSelectAll();
+ void ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
+ void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb.select_start; ReloadSelectionEnd = Stb.select_end; }
+ void ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
+
};
-// Storage for current popup stack
-struct ImGuiPopupData
+enum ImGuiWindowRefreshFlags_
{
- ImGuiID PopupId; // Set on OpenPopup()
- ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
- ImGuiWindow* BackupNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close
- int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value
- int OpenFrameCount; // Set on OpenPopup()
- ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)
- ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)
- ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup
-
- ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; }
+ ImGuiWindowRefreshFlags_None = 0,
+ ImGuiWindowRefreshFlags_TryToAvoidRefresh = 1 << 0, // [EXPERIMENTAL] Try to keep existing contents, USER MUST NOT HONOR BEGIN() RETURNING FALSE AND NOT APPEND.
+ ImGuiWindowRefreshFlags_RefreshOnHover = 1 << 1, // [EXPERIMENTAL] Always refresh on hover
+ ImGuiWindowRefreshFlags_RefreshOnFocus = 1 << 2, // [EXPERIMENTAL] Always refresh on focus
+ // Refresh policy/frequency, Load Balancing etc.
};
enum ImGuiNextWindowDataFlags_
@@ -1133,6 +1138,8 @@ enum ImGuiNextWindowDataFlags_
ImGuiNextWindowDataFlags_HasFocus = 1 << 5,
ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6,
ImGuiNextWindowDataFlags_HasScroll = 1 << 7,
+ ImGuiNextWindowDataFlags_HasChildFlags = 1 << 8,
+ ImGuiNextWindowDataFlags_HasRefreshPolicy = 1 << 9,
};
// Storage for SetNexWindow** functions
@@ -1147,34 +1154,45 @@ struct ImGuiNextWindowData
ImVec2 SizeVal;
ImVec2 ContentSizeVal;
ImVec2 ScrollVal;
+ ImGuiChildFlags ChildFlags;
bool CollapsedVal;
ImRect SizeConstraintRect;
ImGuiSizeCallback SizeCallback;
void* SizeCallbackUserData;
float BgAlphaVal; // Override background alpha
ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?)
+ ImGuiWindowRefreshFlags RefreshFlagsVal;
ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); }
inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; }
};
+// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect()
+// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.)
+typedef ImS64 ImGuiSelectionUserData;
+
enum ImGuiNextItemDataFlags_
{
- ImGuiNextItemDataFlags_None = 0,
- ImGuiNextItemDataFlags_HasWidth = 1 << 0,
- ImGuiNextItemDataFlags_HasOpen = 1 << 1,
+ ImGuiNextItemDataFlags_None = 0,
+ ImGuiNextItemDataFlags_HasWidth = 1 << 0,
+ ImGuiNextItemDataFlags_HasOpen = 1 << 1,
+ ImGuiNextItemDataFlags_HasShortcut = 1 << 2,
};
struct ImGuiNextItemData
{
ImGuiNextItemDataFlags Flags;
- float Width; // Set by SetNextItemWidth()
- ImGuiID FocusScopeId; // Set by SetNextItemMultiSelectData() (!= 0 signify value has been set, so it's an alternate version of HasSelectionData, we don't use Flags for this because they are cleared too early. This is mostly used for debugging)
- ImGuiCond OpenCond;
- bool OpenVal; // Set by SetNextItemOpen()
+ ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap.
+ // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem()
+ ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values)
+ float Width; // Set by SetNextItemWidth()
+ ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut()
+ ImGuiInputFlags ShortcutFlags; // Set by SetNextItemShortcut()
+ bool OpenVal; // Set by SetNextItemOpen()
+ ImGuiCond OpenCond : 8;
- ImGuiNextItemData() { memset(this, 0, sizeof(*this)); }
- inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()!
+ ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; }
+ inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!
};
// Status storage for the last submitted item
@@ -1185,11 +1203,24 @@ struct ImGuiLastItemData
ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_
ImRect Rect; // Full rectangle
ImRect NavRect; // Navigation scoring rectangle (not displayed)
- ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set)
+ // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags ar set.
+ ImRect DisplayRect; // Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) is set.
+ ImRect ClipRect; // Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasClipRect) is set..
+ ImGuiKeyChord Shortcut; // Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasShortcut) is set..
ImGuiLastItemData() { memset(this, 0, sizeof(*this)); }
};
+// Store data emitted by TreeNode() for usage by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere.
+// This is the minimum amount of data that we need to perform the equivalent of NavApplyItemToResult() and which we can't infer in TreePop()
+// Only stored when the node is a potential candidate for landing on a Left arrow jump.
+struct ImGuiNavTreeNodeData
+{
+ ImGuiID ID;
+ ImGuiItemFlags InFlags;
+ ImRect NavRect;
+};
+
struct IMGUI_API ImGuiStackSizes
{
short SizeOfIDStack;
@@ -1210,9 +1241,9 @@ struct IMGUI_API ImGuiStackSizes
// Data saved for each window pushed into the stack
struct ImGuiWindowStackData
{
- ImGuiWindow* Window;
- ImGuiLastItemData ParentLastItemDataBackup;
- ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
+ ImGuiWindow* Window;
+ ImGuiLastItemData ParentLastItemDataBackup;
+ ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
};
struct ImGuiShrinkWidthItem
@@ -1231,6 +1262,66 @@ struct ImGuiPtrOrIndex
ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
};
+//-----------------------------------------------------------------------------
+// [SECTION] Data types support
+//-----------------------------------------------------------------------------
+
+struct ImGuiDataVarInfo
+{
+ ImGuiDataType Type;
+ ImU32 Count; // 1+
+ ImU32 Offset; // Offset in parent structure
+ void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }
+};
+
+struct ImGuiDataTypeTempStorage
+{
+ ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT
+};
+
+// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
+struct ImGuiDataTypeInfo
+{
+ size_t Size; // Size in bytes
+ const char* Name; // Short descriptive name for the type, for debugging
+ const char* PrintFmt; // Default printf format for the type
+ const char* ScanFmt; // Default scanf format for the type
+};
+
+// Extend ImGuiDataType_
+enum ImGuiDataTypePrivate_
+{
+ ImGuiDataType_String = ImGuiDataType_COUNT + 1,
+ ImGuiDataType_Pointer,
+ ImGuiDataType_ID,
+};
+
+//-----------------------------------------------------------------------------
+// [SECTION] Popup support
+//-----------------------------------------------------------------------------
+
+enum ImGuiPopupPositionPolicy
+{
+ ImGuiPopupPositionPolicy_Default,
+ ImGuiPopupPositionPolicy_ComboBox,
+ ImGuiPopupPositionPolicy_Tooltip,
+};
+
+// Storage for popup stacks (g.OpenPopupStack and g.BeginPopupStack)
+struct ImGuiPopupData
+{
+ ImGuiID PopupId; // Set on OpenPopup()
+ ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
+ ImGuiWindow* RestoreNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close
+ int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value
+ int OpenFrameCount; // Set on OpenPopup()
+ ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)
+ ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)
+ ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup
+
+ ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; }
+};
+
//-----------------------------------------------------------------------------
// [SECTION] Inputs support
//-----------------------------------------------------------------------------
@@ -1278,7 +1369,6 @@ enum ImGuiInputSource
ImGuiInputSource_Mouse, // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them.
ImGuiInputSource_Keyboard,
ImGuiInputSource_Gamepad,
- ImGuiInputSource_Clipboard, // Currently only used by InputText()
ImGuiInputSource_COUNT
};
@@ -1312,7 +1402,8 @@ struct ImGuiInputEvent
// Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior.
#define ImGuiKeyOwner_Any ((ImGuiID)0) // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
-#define ImGuiKeyOwner_None ((ImGuiID)-1) // Require key to have no owner.
+#define ImGuiKeyOwner_NoOwner ((ImGuiID)-1) // Require key to have no owner.
+//#define ImGuiKeyOwner_None ImGuiKeyOwner_NoOwner // We previously called this 'ImGuiKeyOwner_None' but it was inconsistent with our pattern that _None values == 0 and quite dangerous. Also using _NoOwner makes the IsKeyPressed() calls more explicit.
typedef ImS16 ImGuiKeyRoutingIndex;
@@ -1320,12 +1411,13 @@ typedef ImS16 ImGuiKeyRoutingIndex;
struct ImGuiKeyRoutingData
{
ImGuiKeyRoutingIndex NextEntryIndex;
- ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImGuiMod_Shortcut is already translated to Ctrl/Super.
+ ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits.
+ ImU8 RoutingCurrScore; // [DEBUG] For debug display
ImU8 RoutingNextScore; // Lower is better (0: perfect score)
ImGuiID RoutingCurr;
ImGuiID RoutingNext;
- ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; }
+ ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; }
};
// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.
@@ -1349,53 +1441,47 @@ struct ImGuiKeyOwnerData
bool LockThisFrame; // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame.
bool LockUntilRelease; // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well.
- ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_None; LockThisFrame = LockUntilRelease = false; }
+ ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_NoOwner; LockThisFrame = LockUntilRelease = false; }
};
+// Extend ImGuiInputFlags_
// Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner()
-// Don't mistake with ImGuiInputTextFlags! (for ImGui::InputText() function)
-enum ImGuiInputFlags_
+// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function)
+enum ImGuiInputFlagsPrivate_
{
- // Flags for IsKeyPressed(), IsMouseClicked(), Shortcut()
- ImGuiInputFlags_None = 0,
- ImGuiInputFlags_Repeat = 1 << 0, // Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.
- ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default)
- ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast
- ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster
- ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak,
-
- // Flags for SetItemKeyOwner()
- ImGuiInputFlags_CondHovered = 1 << 4, // Only set if item is hovered (default to both)
- ImGuiInputFlags_CondActive = 1 << 5, // Only set if item is active (default to both)
- ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
- ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
+ // Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut()
+ // - Repeat mode: Repeat rate selection
+ ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default)
+ ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast
+ ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster
+ // - Repeat mode: Specify when repeating key pressed can be interrupted.
+ // - In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in.
+ ImGuiInputFlags_RepeatUntilRelease = 1 << 4, // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior.
+ ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5, // Stop repeating when released OR if keyboard mods are changed (default for Shortcut)
+ ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6, // Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod.
+ ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7, // Stop repeating when released OR if any other keyboard key is pressed during the repeat
// Flags for SetKeyOwner(), SetItemKeyOwner()
- ImGuiInputFlags_LockThisFrame = 1 << 6, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.
- ImGuiInputFlags_LockUntilRelease = 1 << 7, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.
+ // - Locking key away from non-input aware code. Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary.
+ ImGuiInputFlags_LockThisFrame = 1 << 20, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame.
+ ImGuiInputFlags_LockUntilRelease = 1 << 21, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released.
- // Routing policies for Shortcut() + low-level SetShortcutRouting()
- // - The general idea is that several callers register interest in a shortcut, and only one owner gets it.
- // - When a policy (other than _RouteAlways) is set, Shortcut() will register itself with SetShortcutRouting(),
- // allowing the system to decide where to route the input among other route-aware calls.
- // - Shortcut() uses ImGuiInputFlags_RouteFocused by default: meaning that a simple Shortcut() poll
- // will register a route and only succeed when parent window is in the focus stack and if no-one
- // with a higher priority is claiming the shortcut.
- // - Using ImGuiInputFlags_RouteAlways is roughly equivalent to doing e.g. IsKeyPressed(key) + testing mods.
- // - Priorities: GlobalHigh > Focused (when owner is active item) > Global > Focused (when focused window) > GlobalLow.
- // - Can select only 1 policy among all available.
- ImGuiInputFlags_RouteFocused = 1 << 8, // (Default) Register focused route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.
- ImGuiInputFlags_RouteGlobalLow = 1 << 9, // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority.
- ImGuiInputFlags_RouteGlobal = 1 << 10, // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText).
- ImGuiInputFlags_RouteGlobalHigh = 1 << 11, // Register route globally (highest priority: unlikely you need to use that: will interfere with every active items)
- ImGuiInputFlags_RouteMask_ = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh, // _Always not part of this!
- ImGuiInputFlags_RouteAlways = 1 << 12, // Do not register route, poll keys directly.
- ImGuiInputFlags_RouteUnlessBgFocused= 1 << 13, // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.
- ImGuiInputFlags_RouteExtraMask_ = ImGuiInputFlags_RouteAlways | ImGuiInputFlags_RouteUnlessBgFocused,
+ // - Condition for SetItemKeyOwner()
+ ImGuiInputFlags_CondHovered = 1 << 22, // Only set if item is hovered (default to both)
+ ImGuiInputFlags_CondActive = 1 << 23, // Only set if item is active (default to both)
+ ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
// [Internal] Mask of which function support which flags
- ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_,
- ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteExtraMask_,
+ ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak,
+ ImGuiInputFlags_RepeatUntilMask_ = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress,
+ ImGuiInputFlags_RepeatMask_ = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_,
+ ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
+ ImGuiInputFlags_RouteTypeMask_ = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways,
+ ImGuiInputFlags_RouteOptionsMask_ = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow,
+ ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_RepeatMask_,
+ ImGuiInputFlags_SupportedByIsMouseClicked = ImGuiInputFlags_Repeat,
+ ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_,
+ ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip,
ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,
ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,
};
@@ -1440,6 +1526,8 @@ enum ImGuiActivateFlags_
ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key.
ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used.
ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)
+ ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request
+ ImGuiActivateFlags_FromShortcut = 1 << 4, // Activation requested by an item shortcut via SetNextItemShortcut() function.
};
// Early work-in-progress API for ScrollToItem()
@@ -1460,8 +1548,7 @@ enum ImGuiScrollFlags_
enum ImGuiNavHighlightFlags_
{
ImGuiNavHighlightFlags_None = 0,
- ImGuiNavHighlightFlags_TypeDefault = 1 << 0,
- ImGuiNavHighlightFlags_TypeThin = 1 << 1,
+ ImGuiNavHighlightFlags_Compact = 1 << 1, // Compact highlight, no padding
ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse.
ImGuiNavHighlightFlags_NoRounding = 1 << 3,
};
@@ -1479,10 +1566,13 @@ enum ImGuiNavMoveFlags_
ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary
ImGuiNavMoveFlags_Forwarded = 1 << 7,
ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result
- ImGuiNavMoveFlags_FocusApi = 1 << 9,
- ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight
- ImGuiNavMoveFlags_Activate = 1 << 11,
- ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12, // Do not alter the visible state of keyboard vs mouse nav highlight
+ ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details)
+ ImGuiNavMoveFlags_IsTabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight
+ ImGuiNavMoveFlags_IsPageMove = 1 << 11, // Identify a PageDown/PageUp request.
+ ImGuiNavMoveFlags_Activate = 1 << 12, // Activate/select target item.
+ ImGuiNavMoveFlags_NoSelect = 1 << 13, // Don't trigger selection by not setting g.NavJustMovedTo
+ ImGuiNavMoveFlags_NoSetNavHighlight = 1 << 14, // Do not alter the visible state of keyboard vs mouse nav highlight
+ ImGuiNavMoveFlags_NoClearActiveId = 1 << 15, // (Experimental) Do not clear active id when applying move result
};
enum ImGuiNavLayer
@@ -1502,16 +1592,60 @@ struct ImGuiNavItemData
float DistBox; // Move // Best candidate box distance to current NavId
float DistCenter; // Move // Best candidate center distance to current NavId
float DistAxial; // Move // Best candidate axial distance to current NavId
+ ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionData() value.
ImGuiNavItemData() { Clear(); }
- void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; }
+ void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }
+};
+
+struct ImGuiFocusScopeData
+{
+ ImGuiID ID;
+ ImGuiID WindowID;
+};
+
+//-----------------------------------------------------------------------------
+// [SECTION] Typing-select support
+//-----------------------------------------------------------------------------
+
+// Flags for GetTypingSelectRequest()
+enum ImGuiTypingSelectFlags_
+{
+ ImGuiTypingSelectFlags_None = 0,
+ ImGuiTypingSelectFlags_AllowBackspace = 1 << 0, // Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state)
+ ImGuiTypingSelectFlags_AllowSingleCharMode = 1 << 1, // Allow "single char" search mode which is activated when pressing the same character multiple times.
+};
+
+// Returned by GetTypingSelectRequest(), designed to eventually be public.
+struct IMGUI_API ImGuiTypingSelectRequest
+{
+ ImGuiTypingSelectFlags Flags; // Flags passed to GetTypingSelectRequest()
+ int SearchBufferLen;
+ const char* SearchBuffer; // Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize).
+ bool SelectRequest; // Set when buffer was modified this frame, requesting a selection.
+ bool SingleCharMode; // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication.
+ ImS8 SingleCharSize; // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input.
+};
+
+// Storage for GetTypingSelectRequest()
+struct IMGUI_API ImGuiTypingSelectState
+{
+ ImGuiTypingSelectRequest Request; // User-facing data
+ char SearchBuffer[64]; // Search buffer: no need to make dynamic as this search is very transient.
+ ImGuiID FocusScope;
+ int LastRequestFrame = 0;
+ float LastRequestTime = 0.0f;
+ bool SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing.
+
+ ImGuiTypingSelectState() { memset(this, 0, sizeof(*this)); }
+ void Clear() { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging
};
//-----------------------------------------------------------------------------
// [SECTION] Columns support
//-----------------------------------------------------------------------------
-// Flags for internal's BeginColumns(). Prefix using BeginTable() nowadays!
+// Flags for internal's BeginColumns(). This is an obsolete API. Prefer using BeginTable() nowadays!
enum ImGuiOldColumnFlags_
{
ImGuiOldColumnFlags_None = 0,
@@ -1519,16 +1653,16 @@ enum ImGuiOldColumnFlags_
ImGuiOldColumnFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers
ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns
ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window
- ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.
+ ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, // Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None,
- ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder,
- ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize,
- ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths,
- ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow,
- ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize,
+ //ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None,
+ //ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder,
+ //ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize,
+ //ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths,
+ //ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow,
+ //ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize,
#endif
};
@@ -1567,6 +1701,9 @@ struct ImGuiOldColumns
// [SECTION] Multi-select support
//-----------------------------------------------------------------------------
+// We always assume that -1 is an invalid value (which works for indices and pointers)
+#define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1)
+
#ifdef IMGUI_HAS_MULTI_SELECT
//
#endif // #ifdef IMGUI_HAS_MULTI_SELECT
@@ -1587,18 +1724,17 @@ struct ImGuiOldColumns
// Every instance of ImGuiViewport is in fact a ImGuiViewportP.
struct ImGuiViewportP : public ImGuiViewport
{
- int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used
- ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.
+ int BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used
+ ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.
ImDrawData DrawDataP;
- ImDrawDataBuilder DrawDataBuilder;
-
+ ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData
ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!)
ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height).
ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f.
ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f.
- ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; }
- ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); }
+ ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; }
+ ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); }
// Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); }
@@ -1624,6 +1760,7 @@ struct ImGuiWindowSettings
ImVec2ih Pos;
ImVec2ih Size;
bool Collapsed;
+ bool IsChild;
bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
bool WantDelete; // Set to invalidate/delete the settings entry
@@ -1653,6 +1790,7 @@ struct ImGuiSettingsHandler
// This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack.
enum ImGuiLocKey : int
{
+ ImGuiLocKey_VersionStr,
ImGuiLocKey_TableSizeOne,
ImGuiLocKey_TableSizeAllFit,
ImGuiLocKey_TableSizeAllDefault,
@@ -1677,30 +1815,53 @@ struct ImGuiLocEntry
enum ImGuiDebugLogFlags_
{
// Event types
- ImGuiDebugLogFlags_None = 0,
- ImGuiDebugLogFlags_EventActiveId = 1 << 0,
- ImGuiDebugLogFlags_EventFocus = 1 << 1,
- ImGuiDebugLogFlags_EventPopup = 1 << 2,
- ImGuiDebugLogFlags_EventNav = 1 << 3,
- ImGuiDebugLogFlags_EventClipper = 1 << 4,
- ImGuiDebugLogFlags_EventSelection = 1 << 5,
- ImGuiDebugLogFlags_EventIO = 1 << 6,
- ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO,
- ImGuiDebugLogFlags_OutputToTTY = 1 << 10, // Also send output to TTY
+ ImGuiDebugLogFlags_None = 0,
+ ImGuiDebugLogFlags_EventActiveId = 1 << 0,
+ ImGuiDebugLogFlags_EventFocus = 1 << 1,
+ ImGuiDebugLogFlags_EventPopup = 1 << 2,
+ ImGuiDebugLogFlags_EventNav = 1 << 3,
+ ImGuiDebugLogFlags_EventClipper = 1 << 4,
+ ImGuiDebugLogFlags_EventSelection = 1 << 5,
+ ImGuiDebugLogFlags_EventIO = 1 << 6,
+ ImGuiDebugLogFlags_EventInputRouting = 1 << 7,
+
+ ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting,
+ ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY
+ ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine
+};
+
+struct ImGuiDebugAllocEntry
+{
+ int FrameCount;
+ ImS16 AllocCount;
+ ImS16 FreeCount;
+};
+
+struct ImGuiDebugAllocInfo
+{
+ int TotalAllocCount; // Number of call to MemAlloc().
+ int TotalFreeCount;
+ ImS16 LastEntriesIdx; // Current index in buffer
+ ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations
+
+ ImGuiDebugAllocInfo() { memset(this, 0, sizeof(*this)); }
};
struct ImGuiMetricsConfig
{
bool ShowDebugLog = false;
- bool ShowStackTool = false;
+ bool ShowIDStackTool = false;
bool ShowWindowsRects = false;
bool ShowWindowsBeginOrder = false;
bool ShowTablesRects = false;
bool ShowDrawCmdMesh = true;
bool ShowDrawCmdBoundingBoxes = true;
+ bool ShowTextEncodingViewer = false;
bool ShowAtlasTintedWithTextColor = false;
int ShowWindowsRectsType = -1;
int ShowTablesRectsType = -1;
+ int HighlightMonitorIdx = -1;
+ ImGuiID HighlightViewportID = 0;
};
struct ImGuiStackLevelInfo
@@ -1714,8 +1875,8 @@ struct ImGuiStackLevelInfo
ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); }
};
-// State for Stack tool queries
-struct ImGuiStackTool
+// State for ID Stack tool queries
+struct ImGuiIDStackTool
{
int LastActiveFrame;
int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level
@@ -1724,7 +1885,7 @@ struct ImGuiStackTool
bool CopyToClipboardOnCtrlC;
float CopyToClipboardLastTime;
- ImGuiStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; }
+ ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; }
};
//-----------------------------------------------------------------------------
@@ -1758,6 +1919,7 @@ struct ImGuiContext
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height.
+ float CurrentDpiScale; // Current window/viewport DpiScale
ImDrawListSharedData DrawListSharedData;
double Time;
int FrameCount;
@@ -1783,7 +1945,8 @@ struct ImGuiContext
ImVector CurrentWindowStack;
ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow*
int WindowsActiveCount; // Number of unique windows submitted by frame
- ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING)
+ ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING).
+ ImGuiID DebugBreakInWindow; // Set to break in Begin() call.
ImGuiWindow* CurrentWindow; // Window being drawn into
ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs.
ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
@@ -1791,12 +1954,13 @@ struct ImGuiContext
ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
ImVec2 WheelingWindowRefMousePos;
int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL
+ int WheelingWindowScrolledFrame;
float WheelingWindowReleaseTimer;
ImVec2 WheelingWindowWheelRemainder;
ImVec2 WheelingAxisAvg;
// Item/widgets state and tracking information
- ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
+ ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
bool HoveredIdAllowOverlap;
@@ -1812,10 +1976,11 @@ struct ImGuiContext
bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state.
bool ActiveIdHasBeenEditedThisFrame;
+ bool ActiveIdFromShortcut;
+ int ActiveIdMouseButton : 8;
ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
ImGuiWindow* ActiveIdWindow;
ImGuiInputSource ActiveIdSource; // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad
- int ActiveIdMouseButton;
ImGuiID ActiveIdPreviousFrame;
bool ActiveIdPreviousFrameIsAlive;
bool ActiveIdPreviousFrameHasBeenEditedBefore;
@@ -1823,36 +1988,43 @@ struct ImGuiContext
ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation.
float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
- // [EXPERIMENTAL] Key/Input Ownership + Shortcut Routing system
+ // Key/Input Ownership + Shortcut Routing system
// - The idea is that instead of "eating" a given key, we can link to an owner.
- // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID.
+ // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID.
// - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame().
+ double LastKeyModsChangeTime; // Record the last time key mods changed (affect repeat delay when using shortcut logic)
+ double LastKeyModsChangeFromNoneTime; // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic)
+ double LastKeyboardKeyPressTime; // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed.
+ ImBitArrayForNamedKeys KeysMayBeCharInput; // Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput(). sizeof() = 20 bytes
ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT];
ImGuiKeyRoutingTable KeysRoutingTable;
ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)
+ ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls.
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'
#endif
// Next window/item data
- ImGuiID CurrentFocusScopeId; // == g.FocusScopeStack.back()
- ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back()
+ ImGuiID CurrentFocusScopeId; // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId.
+ ImGuiItemFlags CurrentItemFlags; // Value for currently appending items == g.ItemFlagsStack.back()
ImGuiID DebugLocateId; // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location
ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions
ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd)
ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions
+ bool DebugShowGroupRects;
// Shared stacks
- ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
- ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
- ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
- ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
- ImVectorItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
- ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
- ImVectorOpenPopupStack; // Which popups are open (persistent)
- ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame)
- int BeginMenuCount;
+ ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line)
+ ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
+ ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
+ ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin()
+ ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
+ ImVector ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
+ ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
+ ImVector OpenPopupStack; // Which popups are open (persistent)
+ ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame)
+ ImVector NavTreeNodeStack; // Stack for TreeNode() when a NavLeft requested is emitted.
// Viewports
ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData.
@@ -1860,11 +2032,14 @@ struct ImGuiContext
// Gamepad/keyboard Navigation
ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow'
ImGuiID NavId; // Focused item for navigation
- ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set)
+ ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope)
+ ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain.
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem()
ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0
ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)
ImGuiActivateFlags NavActivateFlags;
+ ImGuiID NavHighlightActivatedId;
+ float NavHighlightActivatedTimer;
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
ImGuiKeyChord NavJustMovedToKeyMods;
@@ -1872,6 +2047,7 @@ struct ImGuiContext
ImGuiActivateFlags NavNextActivateFlags;
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse
ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
+ ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid
bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)
bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover)
@@ -1902,20 +2078,20 @@ struct ImGuiContext
ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy
// Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
- ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab, for reconfiguration (see #4828)
- ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab
+ ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
+ ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)
ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.
ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents
float NavWindowingTimer;
float NavWindowingHighlightAlpha;
bool NavWindowingToggleLayer;
+ ImGuiKey NavWindowingToggleKey;
ImVec2 NavWindowingAccumDeltaPos;
ImVec2 NavWindowingAccumDeltaSize;
// Render
float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
- ImGuiMouseCursor MouseCursor;
// Drag and Drop
bool DragDropActive;
@@ -1926,6 +2102,7 @@ struct ImGuiContext
int DragDropMouseButton;
ImGuiPayload DragDropPayload;
ImRect DragDropTargetRect; // Store rectangle of current target candidate (we favor small targets when overlapping)
+ ImRect DragDropTargetClipRect; // Store ClipRect at the time of item's drawing
ImGuiID DragDropTargetId;
ImGuiDragDropFlags DragDropAcceptFlags;
float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface)
@@ -1942,6 +2119,7 @@ struct ImGuiContext
// Tables
ImGuiTable* CurrentTable;
+ ImGuiID DebugBreakInTable; // Set to break in BeginTable() call.
int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting)
ImPool Tables; // Persistent table data
@@ -1955,17 +2133,25 @@ struct ImGuiContext
ImVector ShrinkWidthBuffer;
// Hover Delay system
- ImGuiID HoverDelayId;
- ImGuiID HoverDelayIdPreviousFrame;
- float HoverDelayTimer; // Currently used IsItemHovered(), generally inferred from g.HoveredIdTimer but kept uncleared until clear timer elapse.
- float HoverDelayClearTimer; // Currently used IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared.
+ ImGuiID HoverItemDelayId;
+ ImGuiID HoverItemDelayIdPreviousFrame;
+ float HoverItemDelayTimer; // Currently used by IsItemHovered()
+ float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared.
+ ImGuiID HoverItemUnlockedStationaryId; // Mouse has once been stationary on this item. Only reset after departing the item.
+ ImGuiID HoverWindowUnlockedStationaryId; // Mouse has once been stationary on this window. Only reset after departing the window.
+
+ // Mouse state
+ ImGuiMouseCursor MouseCursor;
+ float MouseStationaryTimer; // Time the mouse has been stationary (with some loose heuristic)
+ ImVec2 MouseLastValidPos;
// Widget state
- ImVec2 MouseLastValidPos;
ImGuiInputTextState InputTextState;
ImGuiInputTextDeactivatedState InputTextDeactivatedState;
ImFont InputTextPasswordFont;
ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc.
+ int BeginMenuDepth;
+ int BeginComboDepth;
ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets
ImGuiID ColorEditCurrentID; // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others).
ImGuiID ColorEditSavedID; // ID we are saving/restoring HS for
@@ -1974,6 +2160,8 @@ struct ImGuiContext
ImU32 ColorEditSavedColor; // RGB value with alpha set to 0.
ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker.
ImGuiComboPreviewData ComboPreviewData;
+ ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving
+ bool WindowResizeRelativeMode;
float SliderGrabClickOffset;
float SliderCurrentAccum; // Accumulated slider delta when using navigation controls.
bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it?
@@ -1983,14 +2171,15 @@ struct ImGuiContext
float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage?
float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled()
short DisabledStackSize;
+ short LockMarkEdited;
short TooltipOverrideCount;
ImVector ClipboardHandlerData; // If no custom clipboard handler is defined
ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once
+ ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest()
// Platform support
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn
- char PlatformLocaleDecimalPoint; // '.' or *localeconv()->decimal_point
// Settings
bool SettingsLoaded;
@@ -2019,17 +2208,24 @@ struct ImGuiContext
int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
// Debug Tools
+ // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)
ImGuiDebugLogFlags DebugLogFlags;
ImGuiTextBuffer DebugLogBuf;
ImGuiTextIndex DebugLogIndex;
- ImU8 DebugLogClipperAutoDisableFrames;
+ ImGuiDebugLogFlags DebugLogAutoDisableFlags;
+ ImU8 DebugLogAutoDisableFrames;
ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
+ bool DebugBreakInLocateId; // Debug break in ItemAdd() call for g.DebugLocateId.
+ ImGuiKeyChord DebugBreakKeyChord; // = ImGuiKey_Pause
ImS8 DebugBeginReturnValueCullDepth; // Cycle between 0..9 then wrap around.
bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker())
ImU8 DebugItemPickerMouseButton;
ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID
+ float DebugFlashStyleColorTime;
+ ImVec4 DebugFlashStyleColorBackup;
ImGuiMetricsConfig DebugMetricsConfig;
- ImGuiStackTool DebugStackTool;
+ ImGuiIDStackTool DebugIDStackTool;
+ ImGuiDebugAllocInfo DebugAllocInfo;
// Misc
float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames..
@@ -2040,6 +2236,7 @@ struct ImGuiContext
int WantCaptureKeyboardNextFrame; // "
int WantTextInputNextFrame;
ImVector TempBuffer; // Temporary text buffer
+ char TempKeychordName[64];
ImGuiContext(ImFontAtlas* shared_font_atlas)
{
@@ -2049,7 +2246,7 @@ struct ImGuiContext
Initialized = false;
FontAtlasOwnedByContext = shared_font_atlas ? false : true;
Font = NULL;
- FontSize = FontBaseSize = 0.0f;
+ FontSize = FontBaseSize = CurrentDpiScale = 0.0f;
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
Time = 0.0f;
FrameCount = 0;
@@ -2068,7 +2265,7 @@ struct ImGuiContext
HoveredWindowUnderMovingWindow = NULL;
MovingWindow = NULL;
WheelingWindow = NULL;
- WheelingWindowStartFrame = -1;
+ WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
WheelingWindowReleaseTimer = 0.0f;
DebugHookIdInfo = 0;
@@ -2085,6 +2282,7 @@ struct ImGuiContext
ActiveIdHasBeenPressedBefore = false;
ActiveIdHasBeenEditedBefore = false;
ActiveIdHasBeenEditedThisFrame = false;
+ ActiveIdFromShortcut = false;
ActiveIdClickOffset = ImVec2(-1, -1);
ActiveIdWindow = NULL;
ActiveIdSource = ImGuiInputSource_None;
@@ -2096,6 +2294,8 @@ struct ImGuiContext
LastActiveId = 0;
LastActiveIdTimer = 0.0f;
+ LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
+
ActiveIdUsingNavDirMask = 0x00;
ActiveIdUsingAllKeyboardKeys = false;
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
@@ -2104,15 +2304,18 @@ struct ImGuiContext
CurrentFocusScopeId = 0;
CurrentItemFlags = ImGuiItemFlags_None;
- BeginMenuCount = 0;
+ DebugShowGroupRects = false;
NavWindow = NULL;
NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
+ NavHighlightActivatedId = 0;
+ NavHighlightActivatedTimer = 0.0f;
NavJustMovedToKeyMods = ImGuiMod_None;
NavInputSource = ImGuiInputSource_Keyboard;
NavLayer = ImGuiNavLayer_Main;
+ NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
NavIdIsAlive = false;
NavMousePosDirty = false;
NavDisableHighlight = true;
@@ -2131,14 +2334,16 @@ struct ImGuiContext
NavTabbingDir = 0;
NavTabbingCounter = 0;
- ConfigNavWindowingKeyNext = ImGuiMod_Ctrl | ImGuiKey_Tab;
- ConfigNavWindowingKeyPrev = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab;
+ // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
+ // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
+ ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
+ ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false;
+ NavWindowingToggleKey = ImGuiKey_None;
DimBgRatio = 0.0f;
- MouseCursor = ImGuiMouseCursor_Arrow;
DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
DragDropSourceFlags = ImGuiDragDropFlags_None;
@@ -2158,14 +2363,19 @@ struct ImGuiContext
TablesTempDataStacked = 0;
CurrentTabBar = NULL;
- HoverDelayId = HoverDelayIdPreviousFrame = 0;
- HoverDelayTimer = HoverDelayClearTimer = 0.0f;
+ HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
+ HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
+
+ MouseCursor = ImGuiMouseCursor_Arrow;
+ MouseStationaryTimer = 0.0f;
TempInputId = 0;
+ BeginMenuDepth = BeginComboDepth = 0;
ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
ColorEditCurrentID = ColorEditSavedID = 0;
ColorEditSavedHue = ColorEditSavedSat = 0.0f;
ColorEditSavedColor = 0;
+ WindowResizeRelativeMode = false;
SliderGrabClickOffset = 0.0f;
SliderCurrentAccum = 0.0f;
SliderCurrentAccumDirty = false;
@@ -2175,11 +2385,11 @@ struct ImGuiContext
ScrollbarClickDeltaToGrabCenter = 0.0f;
DisabledAlphaBackup = 0.0f;
DisabledStackSize = 0;
+ LockMarkEdited = 0;
TooltipOverrideCount = 0;
PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
- PlatformLocaleDecimalPoint = '.';
SettingsLoaded = false;
SettingsDirtyTimer = 0.0f;
@@ -2198,17 +2408,28 @@ struct ImGuiContext
DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
DebugLocateId = 0;
- DebugLogClipperAutoDisableFrames = 0;
+ DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
+ DebugLogAutoDisableFrames = 0;
DebugLocateFrames = 0;
DebugBeginReturnValueCullDepth = -1;
DebugItemPickerActive = false;
DebugItemPickerMouseButton = ImGuiMouseButton_Left;
DebugItemPickerBreakId = 0;
+ DebugFlashStyleColorTime = 0.0f;
+ DebugFlashStyleColorIdx = ImGuiCol_COUNT;
+
+ // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
+ DebugBreakInWindow = 0;
+ DebugBreakInTable = 0;
+ DebugBreakInLocateId = false;
+ DebugBreakKeyChord = ImGuiKey_Pause;
+ DebugBreakInShortcutRouting = ImGuiKey_None;
memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
FramerateSecPerFrameAccum = 0.0f;
WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
+ memset(TempKeychordName, 0, sizeof(TempKeychordName));
}
};
@@ -2258,6 +2479,7 @@ struct IMGUI_API ImGuiWindowTempData
int CurrentTableIdx; // Current table index (into g.Tables)
ImGuiLayoutType LayoutType;
ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin()
+ ImU32 ModalDimBgColor;
// Local parameters stacks
// We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.
@@ -2274,6 +2496,7 @@ struct IMGUI_API ImGuiWindow
char* Name; // Window name, owned by the window.
ImGuiID ID; // == ImHashStr(Name)
ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_
+ ImGuiChildFlags ChildFlags; // Set when window is a child window. See enum ImGuiChildFlags_
ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded.
ImVec2 Pos; // Position (always rounded-up to nearest pixel)
ImVec2 Size; // Current size (==SizeFull or collapsed title bar size)
@@ -2303,11 +2526,13 @@ struct IMGUI_API ImGuiWindow
bool Collapsed; // Set when collapsing window to become only title-bar
bool WantCollapseToggle;
bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed)
+ bool SkipRefresh; // [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false.
bool Appearing; // Set during the frame where the window is appearing (or re-appearing)
bool Hidden; // Do not display (== HiddenFrames*** > 0)
bool IsFallbackWindow; // Set on the "Debug##Default" window.
bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked()
bool HasCloseButton; // Set when the window has a close button (p_open != NULL)
+ signed char ResizeBorderHovered; // Current border being hovered for resize (-1: none, otherwise 0-3)
signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3)
short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
short BeginCountPreviousFrame; // Number of Begin() during the previous frame
@@ -2316,7 +2541,6 @@ struct IMGUI_API ImGuiWindow
short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused.
ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)
ImS8 AutoFitFramesX, AutoFitFramesY;
- ImS8 AutoFitChildAxises;
bool AutoFitOnlyGrows;
ImGuiDir AutoPosLastDirection;
ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames
@@ -2360,6 +2584,7 @@ struct IMGUI_API ImGuiWindow
ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.
ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag.
+ ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document)
ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1)
@@ -2450,6 +2675,8 @@ struct IMGUI_API ImGuiTabBar
float ScrollingSpeed;
float ScrollingRectMinX;
float ScrollingRectMaxX;
+ float SeparatorMinX;
+ float SeparatorMaxX;
ImGuiID ReorderRequestTabId;
ImS16 ReorderRequestOffset;
ImS8 BeginCount;
@@ -2539,26 +2766,39 @@ struct ImGuiTableColumn
};
// Transient cell data stored per row.
-// sizeof() ~ 6
+// sizeof() ~ 6 bytes
struct ImGuiTableCellData
{
ImU32 BgColor; // Actual color
ImGuiTableColumnIdx Column; // Column number
};
+// Parameters for TableAngledHeadersRowEx()
+// This may end up being refactored for more general purpose.
+// sizeof() ~ 12 bytes
+struct ImGuiTableHeaderData
+{
+ ImGuiTableColumnIdx Index; // Column index
+ ImU32 TextColor;
+ ImU32 BgColor0;
+ ImU32 BgColor1;
+};
+
// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?)
+// sizeof() ~ 24 bytes
struct ImGuiTableInstanceData
{
ImGuiID TableInstanceID;
float LastOuterHeight; // Outer height from last frame
- float LastFirstRowHeight; // Height of first row from last frame (FIXME: this is used as "header height" and may be reworked)
+ float LastTopHeadersRowHeight; // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set)
float LastFrozenHeight; // Height of frozen section from last frame
+ int HoveredRowLast; // Index of row which was hovered last frame.
+ int HoveredRowNext; // Index of row hovered this frame, set after encountering it.
- ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastFirstRowHeight = LastFrozenHeight = 0.0f; }
+ ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; }
};
-// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs, incoming RowData
-// sizeof() ~ 580 bytes + heap allocs described in TableBeginInitMemory()
+// sizeof() ~ 592 bytes + heap allocs described in TableBeginInitMemory()
struct IMGUI_API ImGuiTable
{
ImGuiID ID;
@@ -2582,6 +2822,7 @@ struct IMGUI_API ImGuiTable
float RowPosY1;
float RowPosY2;
float RowMinHeight; // Height submitted to TableNextRow()
+ float RowCellPaddingY; // Top and bottom padding. Reloaded during row change.
float RowTextBaseline;
float RowIndentOffsetX;
ImGuiTableRowFlags RowFlags : 16; // Current row flags, see ImGuiTableRowFlags_
@@ -2595,9 +2836,8 @@ struct IMGUI_API ImGuiTable
float HostIndentX;
float MinColumnWidth;
float OuterPaddingX;
- float CellPaddingX; // Padding from each borders
- float CellPaddingY;
- float CellSpacingX1; // Spacing between non-bordered cells
+ float CellPaddingX; // Padding from each borders. Locked in BeginTable()/Layout.
+ float CellSpacingX1; // Spacing between non-bordered cells. Locked in BeginTable()/Layout.
float CellSpacingX2;
float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details.
float ColumnsGivenWidth; // Sum of current column width
@@ -2606,6 +2846,8 @@ struct IMGUI_API ImGuiTable
float ResizedColumnNextWidth;
float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table.
float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
+ float AngledHeadersHeight; // Set by TableAngledHeadersRow(), used in TableUpdateLayout()
+ float AngledHeadersSlope; // Set by TableAngledHeadersRow(), used in TableUpdateLayout()
ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is
ImRect WorkRect;
@@ -2626,10 +2868,12 @@ struct IMGUI_API ImGuiTable
ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs()
ImGuiTableColumnIdx SortSpecsCount;
ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount)
- ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns (<= ColumnsCount)
+ ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns using fixed width (<= ColumnsCount)
ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn()
+ ImGuiTableColumnIdx AngledHeadersCount; // Count columns with angled headers
ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!
ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing).
+ ImGuiTableColumnIdx HighlightColumnHeader; // Index of column which should be highlighted.
ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit.
ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0.
ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame.
@@ -2655,6 +2899,7 @@ struct IMGUI_API ImGuiTable
bool IsSortSpecsDirty;
bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag.
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
+ bool DisableDefaultContextMenu; // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup()
bool IsSettingsRequestLoad;
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
@@ -2662,6 +2907,8 @@ struct IMGUI_API ImGuiTable
bool IsResetDisplayOrderRequest;
bool IsUnfrozenRows; // Set when we got past the frozen row.
bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable()
+ bool IsActiveIdAliveBeforeTable;
+ bool IsActiveIdInTable;
bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame.
bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous.
bool MemoryCompacted;
@@ -2674,11 +2921,14 @@ struct IMGUI_API ImGuiTable
// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).
// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.
// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.
-// sizeof() ~ 112 bytes.
+// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs.
+// sizeof() ~ 136 bytes.
struct IMGUI_API ImGuiTableTempData
{
int TableIndex; // Index in g.Tables.Buf[] pool
float LastTimeActive; // Last timestamp this structure was used
+ float AngledHeadersExtraWidth; // Used in EndTable()
+ ImVector AngledHeadersRequests; // Used in TableAngledHeadersRow()
ImVec2 UserOuterSize; // outer_size.x passed to BeginTable()
ImDrawListSplitter DrawSplitter;
@@ -2750,6 +3000,7 @@ namespace ImGui
IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id);
IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
+ IMGUI_API void UpdateWindowSkipRefresh(ImGuiWindow* window);
IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy);
IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
@@ -2759,7 +3010,8 @@ namespace ImGui
IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0);
IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0);
IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size);
- IMGUI_API void SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window);
+ IMGUI_API void SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window);
+ inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; }
inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }
inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }
inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); }
@@ -2774,12 +3026,16 @@ namespace ImGui
IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window);
IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window);
+ // Windows: Idle, Refresh Policies [EXPERIMENTAL]
+ IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
+
// Fonts, drawing
IMGUI_API void SetCurrentFont(ImFont* font);
inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
+ IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list);
// Init
IMGUI_API void Initialize();
@@ -2788,6 +3044,7 @@ namespace ImGui
// NewFrame
IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs);
IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
+ IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window);
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
IMGUI_API void UpdateMouseMovingWindowNewFrame();
IMGUI_API void UpdateMouseMovingWindowEndFrame();
@@ -2852,7 +3109,7 @@ namespace ImGui
IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min.
IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);
- IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id);
+ IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags);
IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0);
IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id);
IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);
@@ -2875,7 +3132,7 @@ namespace ImGui
IMGUI_API void LogSetNextTextDecoration(const char* prefix, const char* suffix);
// Popups, Modals, Tooltips
- IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
+ IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags);
IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None);
IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
@@ -2883,6 +3140,7 @@ namespace ImGui
IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags);
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);
+ IMGUI_API bool BeginTooltipHidden();
IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window);
IMGUI_API ImGuiWindow* GetTopMostPopupModal();
IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal();
@@ -2907,52 +3165,61 @@ namespace ImGui
IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);
+ IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data);
IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
+ IMGUI_API void NavHighlightActivated(ImGuiID id);
IMGUI_API void NavClearPreferredPosForAxis(ImGuiAxis axis);
+ IMGUI_API void NavRestoreHighlightAfterMove();
IMGUI_API void NavUpdateCurrentWindowIsScrollPushableX();
- IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again.
IMGUI_API void SetNavWindow(ImGuiWindow* window);
IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
+ IMGUI_API void SetNavFocusScope(ImGuiID focus_scope_id);
+
+ // Focus/Activation
+ // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are
+ // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones.
+ IMGUI_API void FocusItem(); // Focus last item (no selection/activation).
+ IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again.
// Inputs
// FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
- inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }
- inline bool IsNamedKeyOrModKey(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super || key == ImGuiMod_Shortcut; }
- inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }
- inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; }
- inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }
- inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; }
- inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }
- inline ImGuiKeyChord ConvertShortcutMod(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; IM_ASSERT_PARANOID(key_chord & ImGuiMod_Shortcut); return (key_chord & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); }
- inline ImGuiKey ConvertSingleModFlagToKey(ImGuiContext* ctx, ImGuiKey key)
+ inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }
+ inline bool IsNamedKeyOrMod(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; }
+ inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }
+ inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; }
+ inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }
+ inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; }
+ inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }
+ inline bool IsModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; }
+ ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord);
+ inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key)
{
- ImGuiContext& g = *ctx;
if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl;
if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift;
if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt;
if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper;
- if (key == ImGuiMod_Shortcut) return (g.IO.ConfigMacOSXBehaviors ? ImGuiKey_ReservedForModSuper : ImGuiKey_ReservedForModCtrl);
return key;
}
IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key);
inline ImGuiKeyData* GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); }
- IMGUI_API void GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size);
+ IMGUI_API const char* GetKeyChordName(ImGuiKeyChord key_chord);
inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); }
IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
IMGUI_API ImVec2 GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down);
IMGUI_API float GetNavTweakPressedAmount(ImGuiAxis axis);
IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
IMGUI_API void GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate);
+ IMGUI_API void TeleportMousePos(const ImVec2& pos);
IMGUI_API void SetActiveIdUsingAllKeyboardKeys();
inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
// [EXPERIMENTAL] Low-Level: Key/Input Ownership
// - The idea is that instead of "eating" a given input, we can link to an owner id.
// - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead).
- // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID.
+ // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID.
// - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0).
// - Input ownership is automatically released on the frame after a key is released. Therefore:
// - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case).
@@ -2960,12 +3227,12 @@ namespace ImGui
// - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state.
// - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step.
// Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved.
- IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key);
- IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
- IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
- IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
- IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id'
- inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(ctx, key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }
+ IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key);
+ IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
+ IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
+ IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
+ IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id'
+ inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }
// [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership
// - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag.
@@ -2973,26 +3240,32 @@ namespace ImGui
// - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'.
// Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
// - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API.
- IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id);
- IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat.
- IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id);
- IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id);
- IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
- IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id);
+ IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id);
+ IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat.
+ IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id);
+ IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0);
+ IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id);
+ IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
+ IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id);
+ IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id);
- // [EXPERIMENTAL] Shortcut Routing
- // - ImGuiKeyChord = a ImGuiKey optionally OR-red with ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
- // ImGuiKey_C (accepted by functions taking ImGuiKey or ImGuiKeyChord)
- // ImGuiKey_C | ImGuiMod_Ctrl (accepted by functions taking ImGuiKeyChord)
- // ONLY ImGuiMod_XXX values are legal to 'OR' with an ImGuiKey. You CANNOT 'OR' two ImGuiKey values.
- // - When using one of the routing flags (e.g. ImGuiInputFlags_RouteFocused): routes requested ahead of time given a chord (key + modifiers) and a routing policy.
- // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.
- // - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route.
- // - Multiple read sites may use the same owner id and will all get the granted route.
- // - For routing: when owner_id is 0 we use the current Focus Scope ID as a default owner in order to identify our location.
- IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
- IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
- IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id);
+ // Shortcut Testing & Routing
+ // - Set Shortcut() and SetNextItemShortcut() in imgui.h
+ // - When a policy (except for ImGuiInputFlags_RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(),
+ // allowing the system to decide where to route the input among other route-aware calls.
+ // (* using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key) and bypassing route registration and check)
+ // - When using one of the routing option:
+ // - The default route is ImGuiInputFlags_RouteFocused (accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.)
+ // - Routes are requested given a chord (key + modifiers) and a routing policy.
+ // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.
+ // - Each route may be granted to a single owner. When multiple requests are made we have policies to select the winning route (e.g. deep most window).
+ // - Multiple read sites may use the same owner id can all access the granted route.
+ // - When owner_id is 0 we use the current Focus Scope ID as a owner ID in order to identify our location.
+ // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute()
+ // e.g. if you have a tool window associated to a document, and you want document shortcuts to run when the tool is focused.
+ IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id);
+ IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); // owner_id needs to be explicit and cannot be 0
+ IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id);
IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord);
// [EXPERIMENTAL] Focus Scope
@@ -3012,7 +3285,13 @@ namespace ImGui
IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
IMGUI_API void ClearDragDrop();
IMGUI_API bool IsDragDropPayloadBeingAccepted();
- IMGUI_API void RenderDragDropTargetRect(const ImRect& bb);
+ IMGUI_API void RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect);
+
+ // Typing-Select API
+ IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None);
+ IMGUI_API int TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);
+ IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);
+ IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data);
// Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)
IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect);
@@ -3030,10 +3309,13 @@ namespace ImGui
IMGUI_API void TableOpenContextMenu(int column_n = -1);
IMGUI_API void TableSetColumnWidth(int column_n, float width);
IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
- IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
+ IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
+ IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.
IMGUI_API float TableGetHeaderRowHeight();
+ IMGUI_API float TableGetHeaderAngledMaxLabelWidth();
IMGUI_API void TablePushBackgroundChannel();
IMGUI_API void TablePopBackgroundChannel();
+ IMGUI_API void TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);
// Tables: Internals
inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; }
@@ -3046,7 +3328,7 @@ namespace ImGui
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table);
IMGUI_API void TableDrawBorders(ImGuiTable* table);
- IMGUI_API void TableDrawContextMenu(ImGuiTable* table);
+ IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display);
IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table);
IMGUI_API void TableMergeDrawChannels(ImGuiTable* table);
inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; }
@@ -3111,7 +3393,7 @@ namespace ImGui
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
- IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight
+ IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_None); // Navigation highlight
IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.
IMGUI_API void RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow);
@@ -3127,7 +3409,7 @@ namespace ImGui
IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
- IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
+ IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);
IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);
@@ -3152,6 +3434,7 @@ namespace ImGui
IMGUI_API void TreePushOverrideID(ImGuiID id);
IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open);
IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging.
+ IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
// Template functions are instantiated in imgui_widgets.cpp for a finite number of types.
// To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).
@@ -3190,6 +3473,7 @@ namespace ImGui
// Shade functions (write over already created vertices)
IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);
IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp);
+ IMGUI_API void ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out);
// Garbage collection
IMGUI_API void GcCompactTransientMiscBuffers();
@@ -3199,20 +3483,25 @@ namespace ImGui
// Debug Log
IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1);
IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1);
+ IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
// Debug Tools
IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
+ IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255));
+ IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255));
+ IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255));
IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time!
IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time!
IMGUI_API void DebugLocateItemResolveWithLastItem();
- inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); }
- inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }
+ IMGUI_API void DebugBreakClearData();
+ IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location);
+ IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location);
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);
IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns);
- IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label);
+ IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);
IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
IMGUI_API void DebugNodeFont(ImFont* font);
IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);
@@ -3221,6 +3510,7 @@ namespace ImGui
IMGUI_API void DebugNodeTable(ImGuiTable* table);
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings);
IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state);
+ IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state);
IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label);
IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings);
IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label);
@@ -3234,16 +3524,14 @@ namespace ImGui
inline void SetItemUsingMouseWheel() { SetItemKeyOwner(ImGuiKey_MouseWheelY); } // Changed in 1.89
inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89
- // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets that used FocusableItemRegister():
+ //inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
+
+ // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
// (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)'
- // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0'
- // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))' (WIP)
- // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText()
- inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()
- inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
-#endif
-#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
- inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
+ // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0'
+ // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))'
+ //inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()
+ //inline void FocusableItemUnregister(ImGuiWindow* window) // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
#endif
} // namespace ImGui
@@ -3263,6 +3551,7 @@ struct ImFontBuilderIO
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype();
#endif
+IMGUI_API void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
diff --git a/extern/src/imgui/imgui_tables.cpp b/extern/src/imgui/imgui_tables.cpp
index 8850094d..10daf40f 100644
--- a/extern/src/imgui/imgui_tables.cpp
+++ b/extern/src/imgui/imgui_tables.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.89.6
+// dear imgui, v1.90.7
// (tables and columns code)
/*
@@ -24,8 +24,9 @@ Index of this file:
*/
// Navigating this file:
-// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
-// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
+// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
+// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
+// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
//-----------------------------------------------------------------------------
// [SECTION] Commentary
@@ -48,7 +49,8 @@ Index of this file:
// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().
// | TableSetupDrawChannels() - setup ImDrawList channels
// | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
-// | TableDrawContextMenu() - draw right-click context menu
+// | TableBeginContextMenuPopup()
+// | - TableDrawDefaultContextMenu() - draw right-click context menu contents
//-----------------------------------------------------------------------------
// - TableHeadersRow() or TableHeader() user submit a headers row (optional)
// | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction
@@ -82,7 +84,7 @@ Index of this file:
// Y with ScrollX/ScrollY disabled: we output table directly in current window
// - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll.
// - outer_size.y = 0.0f -> No minimum height (but will auto extend, unless _NoHostExtendY is set)
-// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtenY is set)
+// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtendY is set)
// Y with ScrollX/ScrollY enabled: using a child window for scrolling
// - outer_size.y < 0.0f -> Bottom-align. Not meaningful if parent window can vertically scroll.
// - outer_size.y = 0.0f -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window.
@@ -198,11 +200,7 @@ Index of this file:
#include "imgui_internal.h"
// System includes
-#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
-#include // intptr_t
-#else
#include // intptr_t
-#endif
// Visual Studio warnings
#ifdef _MSC_VER
@@ -230,6 +228,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
@@ -321,19 +320,30 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
IM_ASSERT(inner_width >= 0.0f);
// If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve.
+ // FIXME: coarse clipping because access to table data causes two issues:
+ // - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine.
+ // - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers.
+ // The side-effects of accessing table data on coarse clip would be:
+ // - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations.
+ // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
const ImVec2 avail_size = GetContentRegionAvail();
- ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
- ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
- if (use_child_window && IsClippedEx(outer_rect, 0))
+ const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
+ const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
+ const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
+ if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
{
ItemSize(outer_rect);
+ ItemAdd(outer_rect, id);
return false;
}
+ // [DEBUG] Debug break requested by user
+ if (g.DebugBreakInTable == id)
+ IM_DEBUG_BREAK();
+
// Acquire storage for the table
ImGuiTable* table = g.Tables.GetOrAddByKey(id);
- const ImGuiTableFlags table_last_flags = table->Flags;
// Acquire temporary buffers
const int table_idx = g.Tables.GetIndex(table);
@@ -349,7 +359,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
flags = TableFixFlags(flags, outer_window);
// Initialize
- const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
+ const int previous_frame_active = table->LastFrameActive;
+ const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
+ const ImGuiTableFlags previous_flags = table->Flags;
table->ID = id;
table->Flags = flags;
table->LastFrameActive = g.FrameCount;
@@ -396,7 +408,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f));
// Reset scroll if we are reactivating it
- if ((table_last_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
+ if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
// Create scrolling region (without border and zero window padding)
@@ -408,13 +420,17 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->InnerRect = table->InnerWindow->InnerRect;
IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f);
+ // Allow submitting when host is measuring
+ if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)
+ table->InnerWindow->SkipItems = false;
+
// When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)
if (instance_no == 0)
{
table->HasScrollbarYPrev = table->HasScrollbarYCurr;
table->HasScrollbarYCurr = false;
}
- table->HasScrollbarYCurr |= (table->InnerWindow->ScrollMax.y > 0.0f);
+ table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY;
}
else
{
@@ -443,6 +459,18 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
+ // Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)
+ // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
+ // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
+ // problem only affect scrolling tables in this case we can get away with doing it without extra cost).
+ if (inner_window != outer_window)
+ {
+ if (flags & ImGuiTableFlags_BordersOuterV)
+ table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
+ if (flags & ImGuiTableFlags_BordersOuterH)
+ table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
+ }
+
// Padding and Spacing
// - None ........Content..... Pad .....Content........
// - PadOuter | Pad ..Content..... Pad .....Content.. Pad |
@@ -456,7 +484,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border;
table->CellSpacingX2 = inner_spacing_explicit;
table->CellPaddingX = inner_padding_explicit;
- table->CellPaddingY = g.Style.CellPadding.y;
const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f;
@@ -473,10 +500,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
+ table->RowCellPaddingY = 0.0f;
table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any
table->FreezeColumnsRequest = table->FreezeColumnsCount = 0;
table->IsUnfrozenRows = true;
- table->DeclColumnsCount = 0;
+ table->DeclColumnsCount = table->AngledHeadersCount = 0;
+ if (previous_frame_active + 1 < g.FrameCount)
+ table->IsActiveIdInTable = false;
+ table->AngledHeadersHeight = 0.0f;
+ temp_data->AngledHeadersExtraWidth = 0.0f;
// Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()
table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong);
@@ -489,7 +521,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
inner_window->DC.CurrentTableIdx = table_idx;
- if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)
+ if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)
table->IsResetDisplayOrderRequest = true;
// Mark as used to avoid GC
@@ -850,8 +882,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx;
IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0);
- // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible
- // to avoid the column fitting having to wait until the first visible frame of the child container (may or not be a good thing).
+ // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid
+ // the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510.
// FIXME-TABLE: for always auto-resizing columns may not want to do that all the time.
if (has_auto_fit_request && table->OuterWindow != table->InnerWindow)
table->InnerWindow->SkipItems = false;
@@ -936,7 +968,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (column->Flags & ImGuiTableColumnFlags_WidthStretch)
{
float weight_ratio = column->StretchWeight / stretch_sum_weights;
- column->WidthRequest = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f);
+ column->WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f);
width_remaining_for_stretched_columns -= column->WidthRequest;
}
@@ -946,7 +978,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->Flags |= ImGuiTableColumnFlags_NoDirectResize_;
// Assign final width, record width in case we will need to shrink
- column->WidthGiven = ImFloor(ImMax(column->WidthRequest, table->MinColumnWidth));
+ column->WidthGiven = ImTrunc(ImMax(column->WidthRequest, table->MinColumnWidth));
table->ColumnsGivenWidth += column->WidthGiven;
}
@@ -971,17 +1003,25 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).
// - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
- table->HoveredColumnBody = -1;
- table->HoveredColumnBorder = -1;
+ table_instance->HoveredRowLast = table_instance->HoveredRowNext;
+ table_instance->HoveredRowNext = -1;
+ table->HoveredColumnBody = table->HoveredColumnBorder = -1;
const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
const ImGuiID backup_active_id = g.ActiveId;
g.ActiveId = 0;
- const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
+ const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None);
g.ActiveId = backup_active_id;
+ // Determine skewed MousePos.x to support angled headers.
+ float mouse_skewed_x = g.IO.MousePos.x;
+ if (table->AngledHeadersHeight > 0.0f)
+ if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight)
+ mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope);
+
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
// Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
int visible_n = 0;
+ bool has_at_least_one_column_requesting_output = false;
bool offset_x_frozen = (table->FreezeColumnsCount > 0);
float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;
ImRect host_clip_rect = table->InnerClipRect;
@@ -1019,7 +1059,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
}
// Detect hovered column
- if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x)
+ if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
// Lock start position
@@ -1036,9 +1076,10 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.
// - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.
// - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.
+ const float previous_instance_work_min_x = column->WorkMinX;
column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;
column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max
- column->ItemWidth = ImFloor(column->WidthGiven * 0.65f);
+ column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f);
column->ClipRect.Min.x = column->MinX;
column->ClipRect.Min.y = work_rect.Min.y;
column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX;
@@ -1062,9 +1103,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;
// Mark column as SkipItems (ignoring all items/layout)
+ // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2)
column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;
if (column->IsSkipItems)
IM_ASSERT(!is_visible);
+ if (column->IsRequestOutput && !column->IsSkipItems)
+ has_at_least_one_column_requesting_output = true;
// Update status flags
column->Flags |= ImGuiTableColumnFlags_IsEnabled;
@@ -1085,8 +1129,22 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
// Reset content width variables
- column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX;
- column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX;
+ if (table->InstanceCurrent == 0)
+ {
+ column->ContentMaxXFrozen = column->WorkMinX;
+ column->ContentMaxXUnfrozen = column->WorkMinX;
+ column->ContentMaxXHeadersUsed = column->WorkMinX;
+ column->ContentMaxXHeadersIdeal = column->WorkMinX;
+ }
+ else
+ {
+ // As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation.
+ const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x;
+ column->ContentMaxXFrozen += offset_from_previous_instance;
+ column->ContentMaxXUnfrozen += offset_from_previous_instance;
+ column->ContentMaxXHeadersUsed += offset_from_previous_instance;
+ column->ContentMaxXHeadersIdeal += offset_from_previous_instance;
+ }
// Don't decrement auto-fit counters until container window got a chance to submit its items
if (table->HostSkipItems == false)
@@ -1102,18 +1160,26 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
visible_n++;
}
+ // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.
+ // Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar.
+ if (has_at_least_one_column_requesting_output == false)
+ {
+ table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true;
+ table->Columns[table->LeftMostEnabledColumn].IsSkipItems = false;
+ }
+
// [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
// Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either
// because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.
const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);
if (is_hovering_table && table->HoveredColumnBody == -1)
- {
- if (g.IO.MousePos.x >= unused_x1)
+ if (mouse_skewed_x >= unused_x1)
table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount;
- }
if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable))
table->Flags &= ~ImGuiTableFlags_Resizable;
+ table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0);
+
// [Part 8] Lock actual OuterRect/WorkRect right-most position.
// This is done late to handle the case of fixed-columns tables not claiming more widths that they need.
// Because of this we are careful with uses of WorkRect and InnerClipRect before this point.
@@ -1125,8 +1191,16 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1);
}
table->InnerWindow->ParentWorkRect = table->WorkRect;
- table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f);
- table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f);
+ table->BorderX1 = table->InnerClipRect.Min.x;
+ table->BorderX2 = table->InnerClipRect.Max.x;
+
+ // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call.
+ float window_content_max_y;
+ if (table->Flags & ImGuiTableFlags_NoHostExtendY)
+ window_content_max_y = table->OuterRect.Max.y;
+ else
+ window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y);
+ table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y);
// [Part 9] Allocate draw channels and setup background cliprect
TableSetupDrawChannels(table);
@@ -1134,14 +1208,26 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 10] Hit testing on borders
if (table->Flags & ImGuiTableFlags_Resizable)
TableUpdateBorders(table);
- table_instance->LastFirstRowHeight = 0.0f;
+ table_instance->LastTopHeadersRowHeight = 0.0f;
table->IsLayoutLocked = true;
table->IsUsingHeaders = false;
- // [Part 11] Context menu
- if (TableBeginContextMenuPopup(table))
+ // Highlight header
+ table->HighlightColumnHeader = -1;
+ if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent)
+ table->HighlightColumnHeader = table->ContextPopupColumn;
+ else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1)
+ if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive))
+ table->HighlightColumnHeader = table->HoveredColumnBody;
+
+ // [Part 11] Default context menu
+ // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
+ // - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().
+ // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
+ // e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
+ if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
{
- TableDrawContextMenu(table);
+ TableDrawDefaultContextMenu(table, table->Flags);
EndPopup();
}
@@ -1167,8 +1253,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
// - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise.
-// - Submit ahead of table contents and header, use ImGuiButtonFlags_AllowItemOverlap to prioritize
-// widgets overlapping the same area.
void ImGui::TableUpdateBorders(ImGuiTable* table)
{
ImGuiContext& g = *GImGui;
@@ -1179,10 +1263,10 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
// really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
// Actual columns highlight/render will be performed in EndTable() and not be affected.
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
- const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS;
- const float hit_y1 = table->OuterRect.Min.y;
- const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight);
- const float hit_y2_head = hit_y1 + table_instance->LastFirstRowHeight;
+ const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale);
+ const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;
+ const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight);
+ const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
@@ -1208,12 +1292,12 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
//GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));
bool hovered = false, held = false;
- bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);
+ bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);
if (pressed && IsMouseDoubleClicked(0))
{
TableSetColumnWidthAutoSingle(table, column_n);
ClearActiveID();
- held = hovered = false;
+ held = false;
}
if (held)
{
@@ -1285,7 +1369,7 @@ void ImGui::EndTable()
max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border);
if (table->ResizedColumn != -1)
max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2);
- table->InnerWindow->DC.CursorMaxPos.x = max_pos_x;
+ table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth;
}
// Pop clipping rect
@@ -1356,11 +1440,13 @@ void ImGui::EndTable()
if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted)
{
ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];
- const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS);
- const float new_width = ImFloor(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);
+ const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale));
+ const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);
table->ResizedColumnNextWidth = new_width;
}
+ table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false);
+
// Pop from id stack
IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!");
IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!");
@@ -1383,7 +1469,10 @@ void ImGui::EndTable()
// CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)
if (inner_window != outer_window)
{
+ short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
+ inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
EndChild();
+ inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
}
else
{
@@ -1401,7 +1490,7 @@ void ImGui::EndTable()
}
else if (temp_data->UserOuterSize.x <= 0.0f)
{
- const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f;
+ const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f);
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x);
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth));
}
@@ -1440,7 +1529,7 @@ void ImGui::EndTable()
NavUpdateCurrentWindowIsScrollPushableX();
}
-// See "COLUMN SIZING POLICIES" comments at the top of this file
+// See "COLUMNS SIZING POLICIES" comments at the top of this file
// If (init_width_or_weight <= 0.0f) it is ignored
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
{
@@ -1468,6 +1557,11 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
flags |= ImGuiTableColumnFlags_WidthFixed;
+ if (flags & ImGuiTableColumnFlags_AngledHeader)
+ {
+ flags |= ImGuiTableColumnFlags_NoHeaderLabel;
+ table->AngledHeadersCount++;
+ }
TableSetupColumnFlags(table, column, flags);
column->UserID = user_id;
@@ -1501,6 +1595,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
}
// Store name (append with zero-terminator in contiguous buffer)
+ // FIXME: If we recorded the number of \n in names we could compute header row height
column->NameOffset = -1;
if (label != NULL && label[0] != 0)
{
@@ -1549,6 +1644,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
// - TableGetCellBgRect() [Internal]
// - TableGetColumnResizeID() [Internal]
// - TableGetHoveredColumn() [Internal]
+// - TableGetHoveredRow() [Internal]
// - TableSetBgColor()
//-----------------------------------------------------------------------------
@@ -1653,6 +1749,19 @@ int ImGui::TableGetHoveredColumn()
return (int)table->HoveredColumnBody;
}
+// Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row.
+// *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value.
+// This difference with is the reason why this is not public yet.
+int ImGui::TableGetHoveredRow()
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiTable* table = g.CurrentTable;
+ if (!table)
+ return -1;
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
+ return (int)table_instance->HoveredRowLast;
+}
+
void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n)
{
ImGuiContext& g = *GImGui;
@@ -1727,19 +1836,20 @@ void ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height)
table->LastRowFlags = table->RowFlags;
table->RowFlags = row_flags;
+ table->RowCellPaddingY = g.Style.CellPadding.y;
table->RowMinHeight = row_min_height;
TableBeginRow(table);
// We honor min_row_height requested by user, but cannot guarantee per-row maximum height,
// because that would essentially require a unique clipping rectangle per-cell.
- table->RowPosY2 += table->CellPaddingY * 2.0f;
+ table->RowPosY2 += table->RowCellPaddingY * 2.0f;
table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height);
// Disable output until user calls TableNextColumn()
table->InnerWindow->SkipItems = true;
}
-// [Internal] Called by TableNextRow()
+// [Internal] Only called by TableNextRow()
void ImGui::TableBeginRow(ImGuiTable* table)
{
ImGuiWindow* window = table->InnerWindow;
@@ -1760,8 +1870,10 @@ void ImGui::TableBeginRow(ImGuiTable* table)
table->RowPosY1 = table->RowPosY2 = next_y1;
table->RowTextBaseline = 0.0f;
table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent
+
window->DC.PrevLineTextBaseOffset = 0.0f;
- window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
+ window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + table->RowCellPaddingY); // This allows users to call SameLine() to share LineSize between columns.
+ window->DC.PrevLineSize = window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // This allows users to call SameLine() to share LineSize between columns, and to call it from first column too.
window->DC.IsSameLine = window->DC.IsSetPos = false;
window->DC.CursorMaxPos.y = next_y1;
@@ -1798,12 +1910,17 @@ void ImGui::TableEndRow(ImGuiTable* table)
const float bg_y2 = table->RowPosY2;
const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount);
const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest);
- if (table->CurrentRow == 0)
- TableGetInstanceData(table, table->InstanceCurrent)->LastFirstRowHeight = bg_y2 - bg_y1;
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
+ if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers)))
+ table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1;
const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y);
if (is_visible)
{
+ // Update data for TableGetHoveredRow()
+ if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0)
+ table_instance->HoveredRowNext = table->CurrentRow;
+
// Decide of background color for the row
ImU32 bg_col0 = 0;
ImU32 bg_col1 = 0;
@@ -1815,15 +1932,14 @@ void ImGui::TableEndRow(ImGuiTable* table)
bg_col1 = table->RowBgColor[1];
// Decide of top border color
- ImU32 border_col = 0;
+ ImU32 top_border_col = 0;
const float border_size = TABLE_BORDER_SIZE;
- if (table->CurrentRow > 0 || table->InnerWindow == table->OuterWindow)
- if (table->Flags & ImGuiTableFlags_BordersInnerH)
- border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight;
+ if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH))
+ top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight;
const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0;
const bool draw_strong_bottom_border = unfreeze_rows_actual;
- if ((bg_col0 | bg_col1 | border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color)
+ if ((bg_col0 | bg_col1 | top_border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color)
{
// In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is
// always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
@@ -1857,13 +1973,14 @@ void ImGui::TableEndRow(ImGuiTable* table)
cell_bg_rect.ClipWith(table->BgClipRect);
cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling
cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);
- window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
+ if (cell_bg_rect.Min.y < cell_bg_rect.Max.y)
+ window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
}
}
// Draw top border
- if (border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y)
- window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), border_col, border_size);
+ if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y)
+ window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size);
// Draw bottom border at the row unfreezing mark (always strong)
if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y)
@@ -1881,7 +1998,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
IM_ASSERT(table->IsUnfrozenRows == false);
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table->IsUnfrozenRows = true;
- TableGetInstanceData(table, table->InstanceCurrent)->LastFrozenHeight = y0 - table->OuterRect.Min.y;
+ table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
@@ -1991,12 +2108,14 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row.
window->DC.CursorPos.x = start_x;
- window->DC.CursorPos.y = table->RowPosY1 + table->CellPaddingY;
+ window->DC.CursorPos.y = table->RowPosY1 + table->RowCellPaddingY;
window->DC.CursorMaxPos.x = window->DC.CursorPos.x;
window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT
+ window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x; // PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns.
window->DC.CurrLineTextBaseOffset = table->RowTextBaseline;
window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent;
+ // Note how WorkRect.Max.y is only set once during layout
window->WorkRect.Min.y = window->DC.CursorPos.y;
window->WorkRect.Min.x = column->WorkMinX;
window->WorkRect.Max.x = column->WorkMaxX;
@@ -2047,7 +2166,7 @@ void ImGui::TableEndCell(ImGuiTable* table)
p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen;
*p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x);
if (column->IsEnabled)
- table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->CellPaddingY);
+ table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->RowCellPaddingY);
column->ItemWidth = window->DC.ItemWidth;
// Propagate text baseline for the entire row
@@ -2065,6 +2184,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
// - TableSetColumnWidthAutoAll() [Internal]
// - TableUpdateColumnsWeightFromWidth() [Internal]
//-------------------------------------------------------------------------
+// Note that actual columns widths are computed in TableUpdateLayout().
+//-------------------------------------------------------------------------
// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
@@ -2257,6 +2378,7 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table)
// - TablePopBackgroundChannel() [Internal]
// - TableSetupDrawChannels() [Internal]
// - TableMergeDrawChannels() [Internal]
+// - TableGetColumnBorderCol() [Internal]
// - TableDrawBorders() [Internal]
//-------------------------------------------------------------------------
@@ -2540,6 +2662,18 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
}
}
+static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n)
+{
+ const bool is_hovered = (table->HoveredColumnBorder == column_n);
+ const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);
+ const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);
+ if (is_resized || is_hovered)
+ return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
+ if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)))
+ return table->BorderColorStrong;
+ return table->BorderColorLight;
+}
+
// FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow)
void ImGui::TableDrawBorders(ImGuiTable* table)
{
@@ -2554,9 +2688,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
// Draw inner border and resizing feedback
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
const float border_size = TABLE_BORDER_SIZE;
- const float draw_y1 = table->InnerRect.Min.y;
+ const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f);
const float draw_y2_body = table->InnerRect.Max.y;
- const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastFirstRowHeight) : draw_y1;
+ const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1;
if (table->Flags & ImGuiTableFlags_BordersInnerV)
{
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
@@ -2582,21 +2716,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
// Draw in outer window so right-most column won't be clipped
// Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling.
- ImU32 col;
- float draw_y2;
- if (is_hovered || is_resized || is_frozen_separator)
- {
- draw_y2 = draw_y2_body;
- col = is_resized ? GetColorU32(ImGuiCol_SeparatorActive) : is_hovered ? GetColorU32(ImGuiCol_SeparatorHovered) : table->BorderColorStrong;
- }
- else
- {
- draw_y2 = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? draw_y2_head : draw_y2_body;
- col = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? table->BorderColorStrong : table->BorderColorLight;
- }
-
+ float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head;
if (draw_y2 > draw_y1)
- inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), col, border_size);
+ inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);
}
}
@@ -2613,7 +2735,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
const ImU32 outer_col = table->BorderColorStrong;
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
{
- inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
+ inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);
}
else if (table->Flags & ImGuiTableFlags_BordersOuterV)
{
@@ -2628,7 +2750,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
}
if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y)
{
- // Draw bottom-most row border
+ // Draw bottom-most row border between it is above outer border.
const float border_y = table->RowPosY2;
if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y)
inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size);
@@ -2649,8 +2771,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
//-------------------------------------------------------------------------
// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set)
-// You can sort your data again when 'SpecsChanged == true'. It will be true with sorting specs have changed since
-// last call, or the first time.
+// When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have
+// changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,
+// else you may wastefully sort your data every frame!
// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()!
ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
{
@@ -2813,6 +2936,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
}
// Write output
+ // May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable()
ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;
if (dirty && sort_specs != NULL)
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
@@ -2836,8 +2960,11 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
// [SECTION] Tables: Headers
//-------------------------------------------------------------------------
// - TableGetHeaderRowHeight() [Internal]
+// - TableGetHeaderAngledMaxLabelWidth() [Internal]
// - TableHeadersRow()
// - TableHeader()
+// - TableAngledHeadersRow()
+// - TableAngledHeadersRowEx() [Internal]
//-------------------------------------------------------------------------
float ImGui::TableGetHeaderRowHeight()
@@ -2846,21 +2973,31 @@ float ImGui::TableGetHeaderRowHeight()
// Calculate row height, for the unlikely case that some labels may be taller than others.
// If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height.
// In your custom header row you may omit this all together and just call TableNextRow() without a height...
- float row_height = GetTextLineHeight();
- int columns_count = TableGetColumnCount();
- for (int column_n = 0; column_n < columns_count; column_n++)
- {
- ImGuiTableColumnFlags flags = TableGetColumnFlags(column_n);
- if ((flags & ImGuiTableColumnFlags_IsEnabled) && !(flags & ImGuiTableColumnFlags_NoHeaderLabel))
- row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
- }
- row_height += GetStyle().CellPadding.y * 2.0f;
- return row_height;
+ ImGuiContext& g = *GImGui;
+ ImGuiTable* table = g.CurrentTable;
+ float row_height = g.FontSize;
+ for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
+ if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
+ if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0)
+ row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y);
+ return row_height + g.Style.CellPadding.y * 2.0f;
+}
+
+float ImGui::TableGetHeaderAngledMaxLabelWidth()
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiTable* table = g.CurrentTable;
+ float width = 0.0f;
+ for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
+ if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
+ if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader)
+ width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x);
+ return width + g.Style.CellPadding.y * 2.0f; // Swap padding
}
// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
// The intent is that advanced users willing to create customized headers would not need to use this helper
-// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
+// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
@@ -2875,9 +3012,9 @@ void ImGui::TableHeadersRow()
TableUpdateLayout(table);
// Open row
- const float row_y1 = GetCursorScreenPos().y;
const float row_height = TableGetHeaderRowHeight();
TableNextRow(ImGuiTableRowFlags_Headers, row_height);
+ const float row_y1 = GetCursorScreenPos().y;
if (table->HostSkipItems) // Merely an optimization, you may skip in your own code.
return;
@@ -2899,7 +3036,7 @@ void ImGui::TableHeadersRow()
ImVec2 mouse_pos = ImGui::GetMousePos();
if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)
if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height)
- TableOpenContextMenu(-1); // Will open a non-column-specific popup.
+ TableOpenContextMenu(columns_count); // Will open a non-column-specific popup.
}
// Emit a column header (text + optional sort order)
@@ -2928,16 +3065,19 @@ void ImGui::TableHeader(const char* label)
// If we already got a row height, there's use that.
// FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?
ImRect cell_r = TableGetCellBgRect(table, column_n);
- float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f);
+ float label_height = ImMax(label_size.y, table->RowMinHeight - table->RowCellPaddingY * 2.0f);
// Calculate ideal size for sort order arrow
float w_arrow = 0.0f;
float w_sort_text = 0.0f;
+ bool sort_arrow = false;
char sort_order_suf[4] = "";
const float ARROW_SCALE = 0.65f;
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
{
- w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);
+ w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);
+ if (column->SortOrder != -1)
+ sort_arrow = true;
if (column->SortOrder > 0)
{
ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
@@ -2945,13 +3085,12 @@ void ImGui::TableHeader(const char* label)
}
}
- // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
+ // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging.
float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
- column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX);
+ column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, sort_arrow ? cell_r.Max.x : ImMin(max_pos_x, cell_r.Max.x));
column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
// Keep header highlighted when context menu is open.
- const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent);
ImGuiID id = window->GetID(label);
ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
@@ -2961,12 +3100,11 @@ void ImGui::TableHeader(const char* label)
//GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
//GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
- // Using AllowItemOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items.
+ // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items.
+ const bool highlight = (table->HighlightColumnHeader == column_n);
bool hovered, held;
- bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowItemOverlap);
- if (g.ActiveId != id)
- SetItemAllowOverlap();
- if (held || hovered || selected)
+ bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap);
+ if (held || hovered || highlight)
{
const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
//RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
@@ -2978,7 +3116,7 @@ void ImGui::TableHeader(const char* label)
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
}
- RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
+ RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
if (held)
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
@@ -3036,19 +3174,190 @@ void ImGui::TableHeader(const char* label)
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
- if (text_clipped && hovered && g.ActiveId == 0 && IsItemHovered(ImGuiHoveredFlags_DelayNormal))
- SetTooltip("%.*s", (int)(label_end - label), label);
+ if (text_clipped && hovered && g.ActiveId == 0)
+ SetItemTooltip("%.*s", (int)(label_end - label), label);
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
if (IsMouseReleased(1) && IsItemHovered())
TableOpenContextMenu(column_n);
}
+// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
+// FIXME: No hit-testing/button on the angled header.
+void ImGui::TableAngledHeadersRow()
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiTable* table = g.CurrentTable;
+ ImGuiTableTempData* temp_data = table->TempData;
+ temp_data->AngledHeadersRequests.resize(0);
+ temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount);
+
+ // Which column needs highlight?
+ const ImGuiID row_id = GetID("##AngledHeaders");
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
+ int highlight_column_n = table->HighlightColumnHeader;
+ if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
+ if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
+ highlight_column_n = table->HoveredColumnBody;
+
+ // Build up request
+ ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg);
+ ImU32 col_text = GetColorU32(ImGuiCol_Text);
+ for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
+ if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
+ {
+ const int column_n = table->DisplayOrderToIndex[order_n];
+ ImGuiTableColumn* column = &table->Columns[column_n];
+ if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
+ continue;
+ ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 };
+ temp_data->AngledHeadersRequests.push_back(request);
+ }
+
+ // Render row
+ TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size);
+}
+
+// Important: data must be fed left to right
+void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiTable* table = g.CurrentTable;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImDrawList* draw_list = window->DrawList;
+ IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
+ IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
+
+ if (max_label_width == 0.0f)
+ max_label_width = TableGetHeaderAngledMaxLabelWidth();
+
+ // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user.
+ const bool flip_label = (angle < 0.0f);
+ angle -= IM_PI * 0.5f;
+ const float cos_a = ImCos(angle);
+ const float sin_a = ImSin(angle);
+ const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a;
+ const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a;
+ const ImVec2 unit_right = ImVec2(cos_a, sin_a);
+
+ // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()
+ // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.
+ const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f;
+ const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y));
+ table->AngledHeadersHeight = row_height;
+ table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;
+ const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right
+
+ // Declare row, override and draw our own background
+ TableNextRow(ImGuiTableRowFlags_Headers, row_height);
+ TableNextColumn();
+ const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, table->RowPosY2);
+ table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0);
+ float clip_rect_min_x = table->BgClipRect.Min.x;
+ if (table->FreezeColumnsCount > 0)
+ clip_rect_min_x = ImMax(clip_rect_min_x, table->Columns[table->FreezeColumnsCount - 1].MaxX);
+ TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel
+ PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns
+ draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.
+ PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns
+
+ ButtonBehavior(row_r, row_id, NULL, NULL);
+ KeepAliveID(row_id);
+
+ const float ascent_scaled = g.Font->Ascent * (g.FontSize / g.Font->FontSize); // FIXME: Standardize those scaling factors better
+ const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
+ const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
+ const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
+
+ // Draw background and labels in first pass, then all borders.
+ float max_x = 0.0f;
+ for (int pass = 0; pass < 2; pass++)
+ for (int order_n = 0; order_n < data_count; order_n++)
+ {
+ const ImGuiTableHeaderData* request = &data[order_n];
+ const int column_n = request->Index;
+ ImGuiTableColumn* column = &table->Columns[column_n];
+
+ ImVec2 bg_shape[4];
+ bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);
+ bg_shape[1] = ImVec2(column->MinX, row_r.Max.y);
+ bg_shape[2] = bg_shape[1] + header_angled_vector;
+ bg_shape[3] = bg_shape[0] + header_angled_vector;
+ if (pass == 0)
+ {
+ // Draw shape
+ draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0);
+ draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight
+ max_x = ImMax(max_x, bg_shape[3].x);
+
+ // Draw label
+ // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset.
+ // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
+ const char* label_name = TableGetColumnName(table, column_n);
+ const char* label_name_end = FindRenderedTextEnd(label_name);
+ const float line_off_step_x = (g.FontSize / -sin_a);
+ const int label_lines = ImTextCountLines(label_name, label_name_end);
+
+ // Left<>Right alignment
+ float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
+ float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x;
+ line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
+
+ // Register header width
+ column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);
+
+ while (label_name < label_name_end)
+ {
+ const char* label_name_eol = strchr(label_name, '\n');
+ if (label_name_eol == NULL)
+ label_name_eol = label_name_end;
+
+ // FIXME: Individual line clipping for right-most column is broken for negative angles.
+ ImVec2 label_size = CalcTextSize(label_name, label_name_eol);
+ float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.
+ float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);
+ ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
+ int vtx_idx_begin = draw_list->_VtxCurrentIdx;
+ PushStyleColor(ImGuiCol_Text, request->TextColor);
+ RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
+ PopStyleColor();
+ int vtx_idx_end = draw_list->_VtxCurrentIdx;
+
+ // Up<>Down alignment
+ const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f);
+ const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f);
+
+ // Rotate and offset label
+ ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y);
+ ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y);
+ line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x;
+ pivot_out += unit_right * padding.y;
+ if (flip_label)
+ pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x));
+ pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x;
+ ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset
+ //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }
+
+ label_name = label_name_eol + 1;
+ }
+ }
+ if (pass == 1)
+ {
+ // Draw border
+ draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n));
+ }
+ }
+ PopClipRect();
+ PopClipRect();
+ table->TempData->AngledHeadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX);
+}
+
//-------------------------------------------------------------------------
// [SECTION] Tables: Context Menu
//-------------------------------------------------------------------------
// - TableOpenContextMenu() [Internal]
-// - TableDrawContextMenu() [Internal]
+// - TableBeginContextMenuPopup() [Internal]
+// - TableDrawDefaultContextMenu() [Internal]
//-------------------------------------------------------------------------
// Use -1 to open menu not specific to a given column.
@@ -3084,7 +3393,13 @@ bool ImGui::TableBeginContextMenuPopup(ImGuiTable* table)
// Output context menu into current window (generally a popup)
// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?
-void ImGui::TableDrawContextMenu(ImGuiTable* table)
+// Sections to display are pulled from 'flags_for_section_to_display', which is typically == table->Flags.
+// - ImGuiTableFlags_Resizable -> display Sizing menu items
+// - ImGuiTableFlags_Reorderable -> display "Reset Order"
+////- ImGuiTableFlags_Sortable -> display sorting options (disabled)
+// - ImGuiTableFlags_Hideable -> display columns visibility menu items
+// It means if you have a custom context menus you can call this section and omit some sections, and add your own.
+void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
@@ -3096,7 +3411,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table)
ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL;
// Sizing
- if (table->Flags & ImGuiTableFlags_Resizable)
+ if (flags_for_section_to_display & ImGuiTableFlags_Resizable)
{
if (column != NULL)
{
@@ -3116,7 +3431,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table)
}
// Ordering
- if (table->Flags & ImGuiTableFlags_Reorderable)
+ if (flags_for_section_to_display & ImGuiTableFlags_Reorderable)
{
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder))
table->IsResetDisplayOrderRequest = true;
@@ -3130,7 +3445,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table)
// Sorting
// (modify TableOpenContextMenu() to add _Sortable flag if enabling this)
#if 0
- if ((table->Flags & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0)
+ if ((flags_for_section_to_display & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0)
{
if (want_separator)
Separator();
@@ -3145,7 +3460,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table)
#endif
// Hiding / Visibility
- if (table->Flags & ImGuiTableFlags_Hideable)
+ if (flags_for_section_to_display & ImGuiTableFlags_Hideable)
{
if (want_separator)
Separator();
@@ -3581,7 +3896,8 @@ static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_poli
void ImGui::DebugNodeTable(ImGuiTable* table)
{
- const bool is_active = (table->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.
+ ImGuiContext& g = *GImGui;
+ const bool is_active = (table->LastFrameActive >= g.FrameCount - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.
if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*");
if (!is_active) { PopStyleColor(); }
@@ -3593,12 +3909,24 @@ void ImGui::DebugNodeTable(ImGuiTable* table)
return;
if (table->InstanceCurrent > 0)
Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1);
+ if (g.IO.ConfigDebugIsDebuggerPresent)
+ {
+ if (DebugBreakButton("**DebugBreak**", "in BeginTable()"))
+ g.DebugBreakInTable = table->ID;
+ SameLine();
+ }
+
bool clear_settings = SmallButton("Clear settings");
BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags));
BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : "");
BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX);
BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder);
BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn);
+ for (int n = 0; n < table->InstanceCurrent + 1; n++)
+ {
+ ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n);
+ BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight);
+ }
//BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen);
float sum_weights = 0.0f;
for (int n = 0; n < table->ColumnsCount; n++)
@@ -3724,7 +4052,7 @@ float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offse
return offset / (columns->OffMaxX - columns->OffMinX);
}
-static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f;
+static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f;
static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
{
@@ -3735,7 +4063,7 @@ static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
- float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x;
+ float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x;
x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))
x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
@@ -3908,7 +4236,7 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFl
// Set state for first column
// We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect
const float column_padding = g.Style.ItemSpacing.x;
- const float half_clip_extend_x = ImFloor(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize));
+ const float half_clip_extend_x = ImTrunc(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize));
const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f);
const float max_2 = window->WorkRect.Max.x + half_clip_extend_x;
columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f);
@@ -3955,8 +4283,9 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFl
float width = offset_1 - offset_0;
PushItemWidth(width * 0.65f);
window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);
- window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
+ window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding;
+ window->WorkRect.Max.y = window->ContentRegionRect.Max.y;
}
void ImGui::NextColumn()
@@ -3970,7 +4299,7 @@ void ImGui::NextColumn()
if (columns->Count == 1)
{
- window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
+ window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
IM_ASSERT(columns->Current == 0);
return;
}
@@ -4002,7 +4331,7 @@ void ImGui::NextColumn()
window->DC.IsSameLine = false;
columns->LineMinY = columns->LineMaxY;
}
- window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
+ window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
window->DC.CursorPos.y = columns->LineMinY;
window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
window->DC.CurrLineTextBaseOffset = 0.0f;
@@ -4049,7 +4378,7 @@ void ImGui::EndColumns()
ImGuiOldColumnData* column = &columns->Columns[n];
float x = window->Pos.x + GetColumnOffset(n);
const ImGuiID column_id = columns->ID + ImGuiID(n);
- const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;
+ const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale);
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
continue;
@@ -4066,7 +4395,7 @@ void ImGui::EndColumns()
// Draw column
const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
- const float xi = IM_FLOOR(x);
+ const float xi = IM_TRUNC(x);
window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);
}
@@ -4087,7 +4416,7 @@ void ImGui::EndColumns()
window->ParentWorkRect = columns->HostBackupParentWorkRect;
window->DC.CurrentColumns = NULL;
window->DC.ColumnsOffset.x = 0.0f;
- window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
+ window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
NavUpdateCurrentWindowIsScrollPushableX();
}
diff --git a/extern/src/imgui/imgui_widgets.cpp b/extern/src/imgui/imgui_widgets.cpp
index 83f27111..2881dd7a 100644
--- a/extern/src/imgui/imgui_widgets.cpp
+++ b/extern/src/imgui/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.89.6
+// dear imgui, v1.90.7
// (widgets code)
/*
@@ -18,6 +18,8 @@ Index of this file:
// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
// [SECTION] Widgets: Selectable
+// [SECTION] Widgets: Typing-Select support
+// [SECTION] Widgets: Multi-Select support
// [SECTION] Widgets: ListBox
// [SECTION] Widgets: PlotLines, PlotHistogram
// [SECTION] Widgets: Value helpers
@@ -41,11 +43,7 @@ Index of this file:
#include "imgui_internal.h"
// System includes
-#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
-#include // intptr_t
-#else
#include // intptr_t
-#endif
//-------------------------------------------------------------------------
// Warnings
@@ -77,6 +75,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
+#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
@@ -124,9 +123,9 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
//-------------------------------------------------------------------------
// For InputTextEx()
-static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source);
-static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
-static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
+static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
+static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
+static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
//-------------------------------------------------------------------------
// [SECTION] Widgets: Text, etc.
@@ -479,6 +478,9 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
// Frame N + RepeatDelay + RepeatRate*N true true - true
//-------------------------------------------------------------------------------------------------------------------------------------------------
+// FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc.
+// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
+// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
@@ -492,6 +494,14 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0)
flags |= ImGuiButtonFlags_PressedOnDefault_;
+ // Default behavior inherited from item flags
+ // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.
+ ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
+ if (flags & ImGuiButtonFlags_AllowOverlap)
+ item_flags |= ImGuiItemFlags_AllowOverlap;
+ if (flags & ImGuiButtonFlags_Repeat)
+ item_flags |= ImGuiItemFlags_ButtonRepeat;
+
ImGuiWindow* backup_hovered_window = g.HoveredWindow;
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;
if (flatten_hovered_children)
@@ -499,16 +509,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
#ifdef IMGUI_ENABLE_TEST_ENGINE
// Alternate registration spot, for when caller didn't use ItemAdd()
- if (id != 0 && g.LastItemData.ID != id)
+ if (g.LastItemData.ID != id)
IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL);
#endif
bool pressed = false;
- bool hovered = ItemHoverable(bb, id);
-
- // Drag source doesn't report as hovered
- if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
- hovered = false;
+ bool hovered = ItemHoverable(bb, id, item_flags);
// Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
@@ -527,14 +533,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
if (flatten_hovered_children)
g.HoveredWindow = backup_hovered_window;
- // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
- if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
- hovered = false;
-
// Mouse handling
const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id;
if (hovered)
{
+ IM_ASSERT(id != 0); // Lazily check inside rare path.
+
// Poll mouse buttons
// - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId.
// - Technically we only need some values in one code path, but since this is gated by hovered test this is fine.
@@ -543,7 +547,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
for (int button = 0; button < 3; button++)
if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here.
{
- if (IsMouseClicked(button, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; }
+ if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; }
if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; }
}
@@ -579,7 +583,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
{
if (mouse_button_released != -1)
{
- const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior
+ const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior
if (!has_repeated_at_least_once)
pressed = true;
if (!(flags & ImGuiButtonFlags_NoNavFocus))
@@ -590,8 +594,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
// 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
// Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
- if (g.ActiveId == id && (flags & ImGuiButtonFlags_Repeat))
- if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat))
+ if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat))
+ if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id))
pressed = true;
}
@@ -599,16 +603,16 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
g.NavDisableHighlight = true;
}
- // Gamepad/Keyboard navigation
- // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse.
- if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId))
+ // Gamepad/Keyboard handling
+ // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse.
+ if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover)
if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))
hovered = true;
if (g.NavActivateDownId == id)
{
bool nav_activated_by_code = (g.NavActivateId == id);
bool nav_activated_by_inputs = (g.NavActivatePressedId == id);
- if (!nav_activated_by_inputs && (flags & ImGuiButtonFlags_Repeat))
+ if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat))
{
// Avoid pressing multiple keys from triggering excessive amount of repeat events
const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space);
@@ -623,8 +627,10 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
pressed = true;
SetActiveID(id, window);
g.ActiveIdSource = g.NavInputSource;
- if (!(flags & ImGuiButtonFlags_NoNavFocus))
+ if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut))
SetFocusID(id, window);
+ if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)
+ g.ActiveIdFromShortcut = true;
}
}
@@ -655,7 +661,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
{
// Report as pressed when releasing the mouse (this is the most common path)
bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2;
- bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps
+ bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps
bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id);
if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned)
pressed = true;
@@ -668,13 +674,19 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
{
// When activated using Nav, we hold on the ActiveID until activation button is released
- if (g.NavActivateDownId != id)
+ if (g.NavActivateDownId == id)
+ held = true; // hovered == true not true as we are already likely hovered on direct activation.
+ else
ClearActiveID();
}
if (pressed)
g.ActiveIdHasBeenPressedBefore = true;
}
+ // Activation highlight (this may be a remote activation)
+ if (g.NavHighlightActivatedId == id)
+ hovered = true;
+
if (out_hovered) *out_hovered = hovered;
if (out_held) *out_held = held;
@@ -702,9 +714,6 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
if (!ItemAdd(bb, id))
return false;
- if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat)
- flags |= ImGuiButtonFlags_Repeat;
-
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
@@ -781,9 +790,6 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu
if (!ItemAdd(bb, id))
return false;
- if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat)
- flags |= ImGuiButtonFlags_Repeat;
-
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
@@ -812,14 +818,14 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
// Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825)
// This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible?
- const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f);
+ const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));
ImRect bb_interact = bb;
const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea();
if (area_to_visible_ratio < 1.5f)
- bb_interact.Expand(ImFloor(bb_interact.GetSize() * -0.25f));
+ bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f));
// Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window.
- // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible).
+ // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer).
bool is_clipped = !ItemAdd(bb_interact, id);
bool hovered, held;
@@ -848,17 +854,19 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f);
- ItemAdd(bb, id);
+ ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));
+ bool is_clipped = !ItemAdd(bb, id);
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
+ if (is_clipped)
+ return pressed;
// Render
ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImU32 text_col = GetColorU32(ImGuiCol_Text);
if (hovered || held)
- window->DrawList->AddCircleFilled(bb.GetCenter()/*+ ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, bg_col);
- RenderArrow(window->DrawList, bb.Min + g.Style.FramePadding, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
+ window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, bg_col);
+ RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
// Switch to moving the window after mouse is moved beyond the initial drag threshold
if (IsItemActive() && IsMouseDragging(0))
@@ -881,9 +889,9 @@ ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
IM_ASSERT(scrollbar_size > 0.0f);
if (axis == ImGuiAxis_X)
- return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x, outer_rect.Max.y);
+ return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size);
else
- return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x, inner_rect.Max.y);
+ return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size);
}
void ImGui::Scrollbar(ImGuiAxis axis)
@@ -944,7 +952,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
const bool allow_interaction = (alpha >= 1.0f);
ImRect bb = bb_frame;
- bb.Expand(ImVec2(-ImClamp(IM_FLOOR((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_FLOOR((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
+ bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
@@ -972,7 +980,6 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
// Click position in scrollbar normalized space (0.0f->1.0f)
const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
- SetHoveredID(id);
bool seek_absolute = false;
if (g.ActiveIdIsJustActivated)
@@ -994,7 +1001,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max);
grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
- // Update distance to grab now that we have seeked and saturated
+ // Update distance to grab now that we have seek'ed and saturated
if (seek_absolute)
g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
}
@@ -1013,33 +1020,30 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
return held;
}
-void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
+// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
+// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
+void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
- ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
- if (border_col.w > 0.0f)
- bb.Max += ImVec2(2, 2);
+ const float border_size = (border_col.w > 0.0f) ? 1.0f : 0.0f;
+ const ImVec2 padding(border_size, border_size);
+ const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
ItemSize(bb);
if (!ItemAdd(bb, 0))
return;
- if (border_col.w > 0.0f)
- {
- window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);
- window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col));
- }
- else
- {
- window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));
- }
+ // Render
+ if (border_size > 0.0f)
+ window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f, ImDrawFlags_None, border_size);
+ window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
}
// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390)
// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API.
-bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
+bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@@ -1047,7 +1051,7 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size
return false;
const ImVec2 padding = g.Style.FramePadding;
- const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2.0f);
+ const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
ItemSize(bb);
if (!ItemAdd(bb, id))
return false;
@@ -1066,14 +1070,15 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size
return pressed;
}
-bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
+// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
+bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return false;
- return ImageButtonEx(window->GetID(str_id), user_texture_id, size, uv0, uv1, bg_col, tint_col);
+ return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col);
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -1140,12 +1145,12 @@ bool ImGui::Checkbox(const char* label, bool* v)
{
// Undocumented tristate/mixed/indeterminate checkbox (#2644)
// This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox)
- ImVec2 pad(ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)), ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)));
+ ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)));
window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding);
}
else if (*v)
{
- const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
+ const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);
}
@@ -1168,10 +1173,8 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value)
if (!all_on && any_on)
{
ImGuiContext& g = *GImGui;
- ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
- g.CurrentItemFlags |= ImGuiItemFlags_MixedValue;
+ g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue;
pressed = Checkbox(label, &all_on);
- g.CurrentItemFlags = backup_item_flags;
}
else
{
@@ -1242,7 +1245,7 @@ bool ImGui::RadioButton(const char* label, bool active)
window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);
if (active)
{
- const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
+ const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark));
}
@@ -1288,24 +1291,47 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over
if (!ItemAdd(bb, 0))
return;
- // Render
- fraction = ImSaturate(fraction);
- RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
- bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
- const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
- RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
+ // Fraction < 0.0f will display an indeterminate progress bar animation
+ // The value must be animated along with time, so e.g. passing '-1.0f * ImGui::GetTime()' as fraction works.
+ const bool is_indeterminate = (fraction < 0.0f);
+ if (!is_indeterminate)
+ fraction = ImSaturate(fraction);
- // Default displaying the fraction as percentage string, but user can override it
- char overlay_buf[32];
- if (!overlay)
+ // Out of courtesy we accept a NaN fraction without crashing
+ float fill_n0 = 0.0f;
+ float fill_n1 = (fraction == fraction) ? fraction : 0.0f;
+
+ if (is_indeterminate)
{
- ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f);
- overlay = overlay_buf;
+ const float fill_width_n = 0.2f;
+ fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n;
+ fill_n1 = ImSaturate(fill_n0 + fill_width_n);
+ fill_n0 = ImSaturate(fill_n0);
}
- ImVec2 overlay_size = CalcTextSize(overlay, NULL);
- if (overlay_size.x > 0.0f)
- RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb);
+ // Render
+ RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
+ bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
+ RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding);
+
+ // Default displaying the fraction as percentage string, but user can override it
+ // Don't display text for indeterminate bars by default
+ char overlay_buf[32];
+ if (!is_indeterminate || overlay != NULL)
+ {
+ if (!overlay)
+ {
+ ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f);
+ overlay = overlay_buf;
+ }
+
+ ImVec2 overlay_size = CalcTextSize(overlay, NULL);
+ if (overlay_size.x > 0.0f)
+ {
+ float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x;
+ RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb);
+ }
+ }
}
void ImGui::Bullet()
@@ -1422,26 +1448,19 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness)
else if (flags & ImGuiSeparatorFlags_Horizontal)
{
// Horizontal Separator
- float x1 = window->Pos.x;
- float x2 = window->Pos.x + window->Size.x;
-
- // FIXME-WORKRECT: old hack (#205) until we decide of consistent behavior with WorkRect/Indent and Separator
- if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID)
- x1 += window->DC.Indent.x;
-
- // FIXME-WORKRECT: In theory we should simply be using WorkRect.Min.x/Max.x everywhere but it isn't aesthetically what we want,
- // need to introduce a variant of WorkRect for that purpose. (#4787)
- if (ImGuiTable* table = g.CurrentTable)
- {
- x1 = table->Columns[table->CurrentColumn].MinX;
- x2 = table->Columns[table->CurrentColumn].MaxX;
- }
+ float x1 = window->DC.CursorPos.x;
+ float x2 = window->WorkRect.Max.x;
+ // Preserve legacy behavior inside Columns()
// Before Tables API happened, we relied on Separator() to span all columns of a Columns() set.
// We currently don't need to provide the same feature for tables because tables naturally have border features.
ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL;
if (columns)
+ {
+ x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03
+ x2 = window->Pos.x + window->Size.x;
PushColumnsBackground();
+ }
// We don't provide our width to the layout so that it doesn't get feed back into AutoFit
// FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell)
@@ -1475,7 +1494,11 @@ void ImGui::Separator()
// Those flags should eventually be configurable by the user
// FIXME: We cannot g.Style.SeparatorTextBorderSize for thickness as it relates to SeparatorText() which is a decorated separator, not defaulting to 1.0f.
ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
- flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot.
+
+ // Only applies to legacy Columns() api as they relied on Separator() a lot.
+ if (window->DC.CurrentColumns)
+ flags |= ImGuiSeparatorFlags_SpanAllColumns;
+
SeparatorEx(flags, 1.0f);
}
@@ -1492,14 +1515,14 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
const float separator_thickness = style.SeparatorTextBorderSize;
const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness));
const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y));
- const float text_baseline_y = ImFloor((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f));
+ const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f));
ItemSize(min_size, text_baseline_y);
if (!ItemAdd(bb, id))
return;
const float sep1_x1 = pos.x;
const float sep2_x2 = bb.Max.x;
- const float seps_y = ImFloor((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);
+ const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);
const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f);
const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN
@@ -1553,14 +1576,20 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float
if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav))
return false;
+ // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is
+ // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item.
+ // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item.
+ ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren;
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ button_flags |= ImGuiButtonFlags_AllowOverlap;
+#endif
+
bool hovered, held;
ImRect bb_interact = bb;
bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
- ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
+ ButtonBehavior(bb_interact, id, &hovered, &held, button_flags);
if (hovered)
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb
- if (g.ActiveId != id)
- SetItemAllowOverlap();
if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay))
SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
@@ -1568,8 +1597,7 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float
ImRect bb_render = bb;
if (held)
{
- ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
- float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
+ float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis];
// Minimum pane size
float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);
@@ -1582,12 +1610,8 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float
// Apply resize
if (mouse_delta != 0.0f)
{
- if (mouse_delta < 0.0f)
- IM_ASSERT(*size1 + mouse_delta >= min_size1);
- if (mouse_delta > 0.0f)
- IM_ASSERT(*size2 - mouse_delta >= min_size2);
- *size1 += mouse_delta;
- *size2 -= mouse_delta;
+ *size1 = ImMax(*size1 + mouse_delta, min_size1);
+ *size2 = ImMax(*size2 - mouse_delta, min_size2);
bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
MarkItemEdited(id);
}
@@ -1641,7 +1665,7 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc
width_excess = 0.0f;
for (int n = 0; n < count; n++)
{
- float width_rounded = ImFloor(items[n].Width);
+ float width_rounded = ImTrunc(items[n].Width);
width_excess += items[n].Width - width_rounded;
items[n].Width = width_rounded;
}
@@ -1687,10 +1711,13 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together
+ if (flags & ImGuiComboFlags_WidthFitPreview)
+ IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0);
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
- const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth();
+ const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f;
+ const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
ItemSize(total_bb, style.FramePadding.y);
@@ -1783,7 +1810,7 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags
// This is essentially a specialized version of BeginPopupEx()
char name[16];
- ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth
// Set position given a custom constraint (peak into expected window size so we can position it)
// FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function?
@@ -1810,12 +1837,15 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags
IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
return false;
}
+ g.BeginComboDepth++;
return true;
}
void ImGui::EndCombo()
{
+ ImGuiContext& g = *GImGui;
EndPopup();
+ g.BeginComboDepth--;
}
// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements
@@ -1829,7 +1859,7 @@ bool ImGui::BeginComboPreview()
if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible))
return false;
IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag?
- if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional)
+ if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional)
return false;
// FIXME: This could be contained in a PushWorkRect() api
@@ -1872,18 +1902,15 @@ void ImGui::EndComboPreview()
}
// Getter for the old Combo() API: const char*[]
-static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
+static const char* Items_ArrayGetter(void* data, int idx)
{
const char* const* items = (const char* const*)data;
- if (out_text)
- *out_text = items[idx];
- return true;
+ return items[idx];
}
// Getter for the old Combo() API: "item1\0item2\0item3\0"
-static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
+static const char* Items_SingleStringGetter(void* data, int idx)
{
- // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
const char* items_separated_by_zeros = (const char*)data;
int items_count = 0;
const char* p = items_separated_by_zeros;
@@ -1894,22 +1921,18 @@ static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
p += strlen(p) + 1;
items_count++;
}
- if (!*p)
- return false;
- if (out_text)
- *out_text = p;
- return true;
+ return *p ? p : NULL;
}
// Old API, prefer using BeginCombo() nowadays if you can.
-bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items)
+bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items)
{
ImGuiContext& g = *GImGui;
// Call the getter to obtain the preview string which is a parameter to BeginCombo()
const char* preview_value = NULL;
if (*current_item >= 0 && *current_item < items_count)
- items_getter(data, *current_item, &preview_value);
+ preview_value = getter(user_data, *current_item);
// The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint))
@@ -1923,12 +1946,13 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
bool value_changed = false;
for (int i = 0; i < items_count; i++)
{
+ const char* item_text = getter(user_data, i);
+ if (item_text == NULL)
+ item_text = "*Unknown item*";
+
PushID(i);
const bool item_selected = (i == *current_item);
- const char* item_text;
- if (!items_getter(data, i, &item_text))
- item_text = "*Unknown item*";
- if (Selectable(item_text, item_selected))
+ if (Selectable(item_text, item_selected) && *current_item != i)
{
value_changed = true;
*current_item = i;
@@ -1967,13 +1991,37 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
return value_changed;
}
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+
+struct ImGuiGetNameFromIndexOldToNewCallbackData { void* UserData; bool (*OldCallback)(void*, int, const char**); };
+static const char* ImGuiGetNameFromIndexOldToNewCallback(void* user_data, int idx)
+{
+ ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data;
+ const char* s = NULL;
+ data->OldCallback(data->UserData, idx, &s);
+ return s;
+}
+
+bool ImGui::ListBox(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int height_in_items)
+{
+ ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
+ return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items);
+}
+bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int popup_max_height_in_items)
+{
+ ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
+ return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items);
+}
+
+#endif
+
//-------------------------------------------------------------------------
// [SECTION] Data Type and Data Formatting Helpers [Internal]
//-------------------------------------------------------------------------
// - DataTypeGetInfo()
// - DataTypeFormatString()
// - DataTypeApplyOp()
-// - DataTypeApplyOpFromText()
+// - DataTypeApplyFromText()
// - DataTypeCompare()
// - DataTypeClamp()
// - GetMinimumStepAtDecimalPrecision
@@ -2261,7 +2309,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;
const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);
- const float tweak_factor = tweak_slow ? 1.0f / 1.0f : tweak_fast ? 10.0f : 1.0f;
+ const float tweak_factor = tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;
adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor;
v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
}
@@ -2415,19 +2463,18 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt;
- const bool hovered = ItemHoverable(frame_bb, id);
+ const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active)
{
// Tabbing or CTRL-clicking on Drag turns it into an InputText
- const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
- const bool clicked = hovered && IsMouseClicked(0, id);
+ const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id));
- const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id);
+ const bool make_active = (clicked || double_clicked || g.NavActivateId == id);
if (make_active && (clicked || double_clicked))
SetKeyOwner(ImGuiKey_MouseLeft, id);
if (make_active && temp_input_allowed)
- if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
+ if ((clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
temp_input_is_active = true;
// (Optional) simple click (without moving) turns Drag into an InputText
@@ -2778,14 +2825,14 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;
const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
- const SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
+ const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it.
// Calculate bounds
const float grab_padding = 2.0f; // FIXME: Should be part of style.
const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
float grab_sz = style.GrabMinSize;
- if (!is_floating_point && v_range >= 0) // v_range < 0 may happen on integer overflows
- grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit
+ if (!is_floating_point && v_range_f >= 0.0f) // v_range_f < 0 may happen on integer overflows
+ grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit
grab_sz = ImMin(grab_sz, slider_sz);
const float slider_usable_sz = slider_sz - grab_sz;
const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;
@@ -2854,8 +2901,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
}
else
{
- if ((v_range >= -100.0f && v_range <= 100.0f) || tweak_slow)
- input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps
+ if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow)
+ input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Gamepad/keyboard tweak speeds in integer steps
else
input_delta /= 100.0f;
}
@@ -2902,6 +2949,10 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
}
}
+ if (set_new_value)
+ if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
+ set_new_value = false;
+
if (set_new_value)
{
TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
@@ -2947,11 +2998,6 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type
// Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
- // Those are the things we can do easily outside the SliderBehaviorT<> template, saves code generation.
- ImGuiContext& g = *GImGui;
- if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
- return false;
-
switch (data_type)
{
case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }
@@ -3008,18 +3054,17 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt;
- const bool hovered = ItemHoverable(frame_bb, id);
+ const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active)
{
// Tabbing or CTRL-clicking on Slider turns it into an input box
- const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
- const bool clicked = hovered && IsMouseClicked(0, id);
- const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id);
+ const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
+ const bool make_active = (clicked || g.NavActivateId == id);
if (make_active && clicked)
SetKeyOwner(ImGuiKey_MouseLeft, id);
if (make_active && temp_input_allowed)
- if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
+ if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
temp_input_is_active = true;
if (make_active && !temp_input_is_active)
@@ -3175,8 +3220,8 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt;
- const bool hovered = ItemHoverable(frame_bb, id);
- const bool clicked = hovered && IsMouseClicked(0, id);
+ const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
+ const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
if (clicked || g.NavActivateId == id)
{
if (clicked)
@@ -3391,14 +3436,6 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
return value_changed;
}
-static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(ImGuiDataType data_type, const char* format)
-{
- if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
- return ImGuiInputTextFlags_CharsScientific;
- const char format_last_char = format[0] ? format[strlen(format) - 1] : 0;
- return (format_last_char == 'x' || format_last_char == 'X') ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal;
-}
-
// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set!
// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility.
// However this may not be ideal for all uses, as some user code may break on out of bound values.
@@ -3415,8 +3452,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format);
ImStrTrimBlanks(data_buf);
- ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited;
- flags |= InputScalar_DefaultCharsFilter(data_type, format);
+ ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
bool value_changed = false;
if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))
@@ -3460,10 +3496,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
char buf[64];
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
- // Testing ActiveId as a minor optimization as filtering is not needed until active
- if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
- flags |= InputScalar_DefaultCharsFilter(data_type, format);
- flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
+ flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
+ flags |= (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
bool value_changed = false;
if (p_step == NULL)
@@ -3557,7 +3591,6 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_dat
bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)
{
- flags |= ImGuiInputTextFlags_CharsScientific;
return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags);
}
@@ -3600,7 +3633,6 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags)
bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)
{
- flags |= ImGuiInputTextFlags_CharsScientific;
return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags);
}
@@ -3698,8 +3730,8 @@ namespace ImStb
{
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; }
-static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; }
-static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); }
+static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->CurLenW); return obj->TextW[idx]; }
+static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); }
static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; }
static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
@@ -3817,12 +3849,13 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const Im
#define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page
#define STB_TEXTEDIT_K_SHIFT 0x400000
-#define STB_TEXTEDIT_IMPLEMENTATION
+#define IMSTB_TEXTEDIT_IMPLEMENTATION
+#define IMSTB_TEXTEDIT_memmove memmove
#include "imstb_textedit.h"
// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling
// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
-static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len)
+static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len);
ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW);
@@ -3875,6 +3908,10 @@ void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
{
+ // Accept null ranges
+ if (new_text == new_text_end)
+ return;
+
const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);
if (new_text_len + BufTextLen >= BufSize)
@@ -3906,9 +3943,8 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons
}
// Return false to discard a character.
-static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source)
+static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard)
{
- IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard);
unsigned int c = *p_char;
// Filter non-printable (NB: isprint is unreliable! see #2467)
@@ -3916,14 +3952,14 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
if (c < 0x20)
{
bool pass = false;
- pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
- pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput));
+ pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
+ pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0;
if (!pass)
return false;
apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted.
}
- if (input_source != ImGuiInputSource_Clipboard)
+ if (input_source_is_clipboard == false)
{
// We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817)
if (c == 127)
@@ -3939,16 +3975,19 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
return false;
// Generic named filters
- if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)))
+ if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)))
{
// The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'.
// The standard mandate that programs starts in the "C" locale where the decimal point is '.'.
// We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point.
// Change the default decimal_point with:
- // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point;
+ // ImGui::GetIO()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point;
// Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions.
- ImGuiContext& g = *GImGui;
- const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint;
+ ImGuiContext& g = *ctx;
+ const unsigned c_decimal_point = (unsigned int)g.IO.PlatformLocaleDecimalPoint;
+ if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))
+ if (c == '.' || c == ',')
+ c = c_decimal_point;
// Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
// While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may
@@ -4034,7 +4073,7 @@ static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* st
const int insert_len = new_last_diff - first_diff + 1;
const int delete_len = old_last_diff - first_diff + 1;
if (insert_len > 0 || delete_len > 0)
- if (STB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len))
+ if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len))
for (int i = 0; i < delete_len; i++)
p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i);
}
@@ -4050,8 +4089,16 @@ void ImGui::InputTextDeactivateHook(ImGuiID id)
if (id == 0 || state->ID != id)
return;
g.InputTextDeactivatedState.ID = state->ID;
- g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1);
- memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data ? state->TextA.Data : "", state->CurLenA + 1);
+ if (state->Flags & ImGuiInputTextFlags_ReadOnly)
+ {
+ g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat.
+ }
+ else
+ {
+ IM_ASSERT(state->TextA.Data != 0);
+ g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1);
+ memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->CurLenA + 1);
+ }
}
// Edit a string of text
@@ -4078,12 +4125,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const bool RENDER_SELECTION_WHEN_INACTIVE = false;
const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
- const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0;
- const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
- const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
- const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
- if (is_resizable)
- IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar)
BeginGroup();
@@ -4097,7 +4138,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImGuiWindow* draw_window = window;
ImVec2 inner_size = frame_size;
- ImGuiItemStatusFlags item_status_flags = 0;
ImGuiLastItemData item_data_backup;
if (is_multiline)
{
@@ -4108,17 +4148,25 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
EndGroup();
return false;
}
- item_status_flags = g.LastItemData.StatusFlags;
item_data_backup = g.LastItemData;
window->DC.CursorPos = backup_pos;
+ // Prevent NavActivation from Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping.
+ if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && (flags & ImGuiInputTextFlags_AllowTabInput))
+ g.NavActivateId = 0;
+
+ // Prevent NavActivate reactivating in BeginChild() when we are already active.
+ const ImGuiID backup_activate_id = g.NavActivateId;
+ if (g.ActiveId == id) // Prevent reactivation
+ g.NavActivateId = 0;
+
// We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug.
- // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre.
PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges
bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove);
+ g.NavActivateId = backup_activate_id;
PopStyleVar(3);
PopStyleColor();
if (!child_visible)
@@ -4139,16 +4187,23 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (!(flags & ImGuiInputTextFlags_MergedItem))
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))
return false;
- item_status_flags = g.LastItemData.StatusFlags;
}
- const bool hovered = ItemHoverable(frame_bb, id);
+ const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
if (hovered)
g.MouseCursor = ImGuiMouseCursor_TextInput;
// We are only allowed to access the state if we are already the active widget.
ImGuiInputTextState* state = GetInputTextState(id);
- const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
+ if (g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly)
+ flags |= ImGuiInputTextFlags_ReadOnly;
+ const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0;
+ const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
+ const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
+ const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
+ if (is_resizable)
+ IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
+
const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard)));
const bool user_clicked = hovered && io.MouseClicked[0];
@@ -4159,27 +4214,32 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
+ const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf);
const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); // state != NULL means its our state.
- const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing);
+ const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
const bool init_state = (init_make_active || user_scroll_active);
- if ((init_state && g.ActiveId != id) || init_changed_specs)
+ if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf)
{
// Access state even if we don't own it yet.
state = &g.InputTextState;
state->CursorAnimReset();
+ state->ReloadUserBuf = false;
// Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714)
InputTextDeactivateHook(state->ID);
- // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
- // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
+ // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode)
const int buf_len = (int)strlen(buf);
- state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
- memcpy(state->InitialTextA.Data, buf, buf_len + 1);
+ if (!init_reload_from_user_buf)
+ {
+ // Take a copy of the initial buffer value.
+ state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
+ memcpy(state->InitialTextA.Data, buf, buf_len + 1);
+ }
// Preserve cursor position and undo/redo stack if we come back to same widget
- // FIXME: Since we reworked this on 2022/06, may want to differenciate recycle_cursor vs recycle_undostate?
- bool recycle_state = (state->ID == id && !init_changed_specs);
+ // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
+ bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf);
if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0)))
recycle_state = false;
@@ -4204,13 +4264,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
stb_textedit_initialize_state(&state->Stb, !is_multiline);
}
- if (!is_multiline)
+ if (init_reload_from_user_buf)
+ {
+ state->Stb.select_start = state->ReloadSelectionStart;
+ state->Stb.cursor = state->Stb.select_end = state->ReloadSelectionEnd;
+ state->CursorClamp();
+ }
+ else if (!is_multiline)
{
if (flags & ImGuiInputTextFlags_AutoSelectAll)
select_all = true;
if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState)))
select_all = true;
- if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl))
+ if (user_clicked && io.KeyCtrl)
select_all = true;
}
@@ -4234,6 +4300,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
+ SetKeyOwner(ImGuiKey_Enter, id);
+ SetKeyOwner(ImGuiKey_KeypadEnter, id);
SetKeyOwner(ImGuiKey_Home, id);
SetKeyOwner(ImGuiKey_End, id);
if (is_multiline)
@@ -4241,10 +4309,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
SetKeyOwner(ImGuiKey_PageUp, id);
SetKeyOwner(ImGuiKey_PageDown, id);
}
+ // FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu
if (is_osx)
SetKeyOwner(ImGuiMod_Alt, id);
- if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
- SetShortcutRouting(ImGuiKey_Tab, id);
}
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
@@ -4373,16 +4440,25 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)
// (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)
- if ((flags & ImGuiInputTextFlags_AllowTabInput) && Shortcut(ImGuiKey_Tab, id) && !is_readonly)
+ if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly)
{
- unsigned int c = '\t'; // Insert TAB
- if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
- state->OnKeyPressed((int)c);
+ if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id))
+ {
+ unsigned int c = '\t'; // Insert TAB
+ if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
+ state->OnKeyPressed((int)c);
+ }
+ // FIXME: Implement Shift+Tab
+ /*
+ if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, ImGuiInputFlags_Repeat, id))
+ {
+ }
+ */
}
// Process regular text input (before we check for Return because using some IME will effectively send a Return?)
// We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
- const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
+ const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl);
if (io.InputQueueCharacters.Size > 0)
{
if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)
@@ -4392,7 +4468,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
unsigned int c = (unsigned int)io.InputQueueCharacters[n];
if (c == '\t') // Skip Tab, see above.
continue;
- if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
+ if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
state->OnKeyPressed((int)c);
}
@@ -4412,25 +4488,26 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
- const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
+ const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
// Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText)
// Otherwise we could simply assume that we own the keys as we are active.
const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat;
- const bool is_cut = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_X, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, id, f_repeat)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
- const bool is_copy = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_C, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, id)) && !is_password && (!is_multiline || state->HasSelection());
- const bool is_paste = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_V, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, id, f_repeat)) && !is_readonly;
- const bool is_undo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Z, id, f_repeat)) && !is_readonly && is_undoable;
- const bool is_redo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Y, id, f_repeat) || (is_osx && Shortcut(ImGuiMod_Shortcut | ImGuiMod_Shift | ImGuiKey_Z, id, f_repeat))) && !is_readonly && is_undoable;
- const bool is_select_all = Shortcut(ImGuiMod_Shortcut | ImGuiKey_A, id);
+ const bool is_cut = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
+ const bool is_copy = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, 0, id)) && !is_password && (!is_multiline || state->HasSelection());
+ const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly;
+ const bool is_undo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable;
+ const bool is_redo = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || (is_osx && Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id))) && !is_readonly && is_undoable;
+ const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id);
// We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true);
const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false));
- const bool is_cancel = Shortcut(ImGuiKey_Escape, id, f_repeat) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, id, f_repeat));
+ const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id));
// FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of.
+ // FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line.
if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
@@ -4455,7 +4532,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
if (is_wordmove_key_down)
state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT);
- else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl)
+ else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper)
state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT);
}
state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
@@ -4475,7 +4552,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
else if (!is_readonly)
{
unsigned int c = '\n'; // Insert new line
- if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))
+ if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
state->OnKeyPressed((int)c);
}
}
@@ -4483,7 +4560,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
if (flags & ImGuiInputTextFlags_EscapeClearsAll)
{
- if (state->CurLenA > 0)
+ if (buf[0] != 0)
{
revert_edit = true;
}
@@ -4542,7 +4619,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
unsigned int c;
s += ImTextCharFromUtf8(&c, s, NULL);
- if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard))
+ if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
continue;
clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;
}
@@ -4571,9 +4648,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (flags & ImGuiInputTextFlags_EscapeClearsAll)
{
// Clear input
+ IM_ASSERT(buf[0] != 0);
apply_new_text = "";
apply_new_text_length = 0;
- STB_TEXTEDIT_CHARTYPE empty_string;
+ value_changed = true;
+ IMSTB_TEXTEDIT_CHARTYPE empty_string;
stb_textedit_replace(state, &state->Stb, &empty_string, 0);
}
else if (strcmp(buf, state->InitialTextA.Data) != 0)
@@ -4601,9 +4680,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL);
}
- // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
+ // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer
+ // before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.
// If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail.
- // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize).
+ // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage
+ // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object
+ // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize).
const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
if (apply_edit_back_to_user_buffer)
{
@@ -4620,7 +4702,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
ImGuiInputTextFlags event_flag = 0;
ImGuiKey event_key = ImGuiKey_None;
- if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, id))
+ if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0, id))
{
event_flag = ImGuiInputTextFlags_CallbackCompletion;
event_key = ImGuiKey_Tab;
@@ -4679,7 +4761,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); }
if (buf_dirty)
{
- IM_ASSERT((flags & ImGuiInputTextFlags_ReadOnly) == 0);
+ IM_ASSERT(!is_readonly);
IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?
if (callback_data.BufTextLen > backup_current_text_length && is_resizable)
@@ -4704,11 +4786,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details)
if (g.InputTextDeactivatedState.ID == id)
{
- if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly)
+ if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0)
{
apply_new_text = g.InputTextDeactivatedState.TextA.Data;
apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1;
- value_changed |= (strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0);
+ value_changed = true;
//IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text);
}
g.InputTextDeactivatedState.ID = 0;
@@ -4850,9 +4932,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const float scroll_increment_x = inner_size.x * 0.25f;
const float visible_width = inner_size.x - style.FramePadding.x;
if (cursor_offset.x < state->ScrollX)
- state->ScrollX = IM_FLOOR(ImMax(0.0f, cursor_offset.x - scroll_increment_x));
+ state->ScrollX = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x));
else if (cursor_offset.x - visible_width >= state->ScrollX)
- state->ScrollX = IM_FLOOR(cursor_offset.x - visible_width + scroll_increment_x);
+ state->ScrollX = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x);
}
else
{
@@ -4902,7 +4984,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
else
{
ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true);
- if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
+ if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
@@ -4925,7 +5007,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
state->CursorAnim += io.DeltaTime;
bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
- ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll);
+ ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
@@ -4963,11 +5045,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
{
// For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)...
Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y));
- ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
- g.CurrentItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop;
+ g.NextItemData.ItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop;
EndChild();
item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow);
- g.CurrentItemFlags = backup_item_flags;
// ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active...
// FIXME: This quite messy/tricky, should attempt to get rid of the child window.
@@ -5011,10 +5091,10 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenW, state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);
Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
- if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), true)) // Visualize undo state
+ if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) // Visualize undo state
{
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
- for (int n = 0; n < STB_TEXTEDIT_UNDOSTATECOUNT; n++)
+ for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++)
{
ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n];
const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';
@@ -5096,10 +5176,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float square_sz = GetFrameHeight();
- const float w_full = CalcItemWidth();
- const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
- const float w_inputs = w_full - w_button;
const char* label_display_end = FindRenderedTextEnd(label);
+ float w_full = CalcItemWidth();
g.NextItemData.ClearFlags();
BeginGroup();
@@ -5133,6 +5211,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
const int components = alpha ? 4 : 3;
+ const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
+ const float w_inputs = ImMax(w_full - w_button, 1.0f);
+ w_full = w_inputs + w_button;
// Convert to the formats we need
float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
@@ -5156,10 +5237,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
{
// RGB/HSV 0..255 Sliders
- const float w_item_one = ImMax(1.0f, IM_FLOOR((w_inputs - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
- const float w_item_last = ImMax(1.0f, IM_FLOOR(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
+ const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1);
- const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
+ const bool hide_prefix = (IM_TRUNC(w_items / components) <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
static const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
static const char* fmt_table_int[3][4] =
{
@@ -5175,11 +5255,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
};
const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1;
+ float prev_split = 0.0f;
for (int n = 0; n < components; n++)
{
if (n > 0)
SameLine(0, style.ItemInnerSpacing.x);
- SetNextItemWidth((n + 1 < components) ? w_item_one : w_item_last);
+ float next_split = IM_TRUNC(w_items * (n + 1) / components);
+ SetNextItemWidth(ImMax(next_split - prev_split, 1.0f));
+ prev_split = next_split;
// FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0.
if (flags & ImGuiColorEditFlags_Float)
@@ -5204,7 +5287,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
else
ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255));
SetNextItemWidth(w_inputs);
- if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
+ if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase))
{
value_changed = true;
char* p = buf;
@@ -5302,7 +5385,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
// Drag and Drop Target
// NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
- if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
+ if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
{
bool accepted_drag_drop = false;
if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
@@ -5367,6 +5450,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
ImGuiIO& io = g.IO;
const float width = CalcItemWidth();
+ const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0;
g.NextItemData.ClearFlags();
PushID(label);
@@ -5401,7 +5485,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
- float bars_triangles_half_sz = IM_FLOOR(bars_width * 0.20f);
+ float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f);
float backup_initial_col[4];
memcpy(backup_initial_col, col, components * sizeof(float));
@@ -5437,7 +5521,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
{
// Hue wheel + SV triangle logic
InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
- if (IsItemActive())
+ if (IsItemActive() && !is_readonly)
{
ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
ImVec2 current_off = g.IO.MousePos - wheel_center;
@@ -5472,7 +5556,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
{
// SV rectangle logic
InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
- if (IsItemActive())
+ if (IsItemActive() && !is_readonly)
{
S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));
V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
@@ -5485,7 +5569,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
// Hue bar logic
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
- if (IsItemActive())
+ if (IsItemActive() && !is_readonly)
{
H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
value_changed = value_changed_h = true;
@@ -5569,7 +5653,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
{
PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
- ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
+ ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB))
@@ -5691,7 +5775,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
}
// Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
- float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
+ float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f;
int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others.
draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments);
draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments);
@@ -5801,7 +5885,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
}
// Tooltip
- if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
+ if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip))
ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
return pressed;
@@ -5831,7 +5915,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags
{
ImGuiContext& g = *GImGui;
- if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None))
+ if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
return;
const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
if (text_end > text)
@@ -5869,6 +5953,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
return;
ImGuiContext& g = *GImGui;
+ g.LockMarkEdited++;
ImGuiColorEditFlags opts = g.ColorEditOptions;
if (allow_opt_inputs)
{
@@ -5911,6 +5996,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
g.ColorEditOptions = opts;
EndPopup();
+ g.LockMarkEdited--;
}
void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)
@@ -5920,6 +6006,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context"))
return;
ImGuiContext& g = *GImGui;
+ g.LockMarkEdited++;
if (allow_opt_picker)
{
ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
@@ -5949,6 +6036,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
}
EndPopup();
+ g.LockMarkEdited--;
}
//-------------------------------------------------------------------------
@@ -6119,44 +6207,72 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
label_end = FindRenderedTextEnd(label);
const ImVec2 label_size = CalcTextSize(label, label_end, false);
+ const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing
+ const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
+ const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow
+
// We vertically grow up to current line height up the typical widget height.
const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2);
+ const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL);
ImRect frame_bb;
- frame_bb.Min.x = (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
+ frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
frame_bb.Min.y = window->DC.CursorPos.y;
- frame_bb.Max.x = window->WorkRect.Max.x;
+ frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanTextWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x;
frame_bb.Max.y = window->DC.CursorPos.y + frame_height;
if (display_frame)
{
// Framed header expand a little outside the default padding, to the edge of InnerClipRect
// (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f)
- frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f);
- frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f);
+ frame_bb.Min.x -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f);
+ frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f);
}
- const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapser arrow width + Spacing
- const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
- const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapser
ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);
ItemSize(ImVec2(text_width, frame_height), padding.y);
// For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
ImRect interact_bb = frame_bb;
- if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth)) == 0)
- interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f;
+ if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0)
+ interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f);
- // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child.
- // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
- // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero.
- const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
+ // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
+ const float backup_clip_rect_min_x = window->ClipRect.Min.x;
+ const float backup_clip_rect_max_x = window->ClipRect.Max.x;
+ if (span_all_columns)
+ {
+ window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
+ window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
+ }
+
+ // Compute open and multi-select states before ItemAdd() as it clear NextItem data.
bool is_open = TreeNodeUpdateNextOpen(id, flags);
- if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
- window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth);
-
bool item_add = ItemAdd(interact_bb, id);
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
g.LastItemData.DisplayRect = frame_bb;
+ if (span_all_columns)
+ {
+ window->ClipRect.Min.x = backup_clip_rect_min_x;
+ window->ClipRect.Max.x = backup_clip_rect_max_x;
+ }
+
+ // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled:
+ // Store data for the current depth to allow returning to this node from any child item.
+ // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
+ // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle.
+ // Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase.
+ if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
+ if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
+ {
+ g.NavTreeNodeStack.resize(g.NavTreeNodeStack.Size + 1);
+ ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back();
+ nav_tree_node_data->ID = id;
+ nav_tree_node_data->InFlags = g.LastItemData.InFlags;
+ nav_tree_node_data->NavRect = g.LastItemData.NavRect;
+ window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth);
+ }
+
+ const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
if (!item_add)
{
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
@@ -6165,9 +6281,16 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
return is_open;
}
+ if (span_all_columns)
+ {
+ TablePushBackgroundChannel();
+ g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;
+ g.LastItemData.ClipRect = window->ClipRect;
+ }
+
ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;
- if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
- button_flags |= ImGuiButtonFlags_AllowItemOverlap;
+ if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap))
+ button_flags |= ImGuiButtonFlags_AllowOverlap;
if (!is_leaf)
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
@@ -6240,8 +6363,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen;
}
}
- if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
- SetItemAllowOverlap();
// In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger.
if (selected != was_selected) //-V547
@@ -6249,7 +6370,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
// Render
const ImU32 text_col = GetColorU32(ImGuiCol_Text);
- ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin;
+ ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact;
if (display_frame)
{
// Framed type
@@ -6259,15 +6380,14 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
- RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
+ RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f);
else // Leaf without bullet, left-adjusted text
- text_pos.x -= text_offset_x;
+ text_pos.x -= text_offset_x -padding.x;
if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)
frame_bb.Max.x -= g.FontSize + style.FramePadding.x;
if (g.LogEnabled)
LogSetNextTextDecoration("###", "###");
- RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
}
else
{
@@ -6281,12 +6401,20 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
- RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
+ RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);
if (g.LogEnabled)
LogSetNextTextDecoration(">", NULL);
- RenderText(text_pos, label, label_end, false);
}
+ if (span_all_columns)
+ TablePopBackgroundChannel();
+
+ // Label
+ if (display_frame)
+ RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
+ else
+ RenderText(text_pos, label, label_end, false);
+
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushOverrideID(id);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
@@ -6328,12 +6456,14 @@ void ImGui::TreePop()
ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);
// Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled)
- if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
- if (g.NavIdIsAlive && (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask))
- {
- SetNavID(window->IDStack.back(), g.NavLayer, 0, ImRect());
- NavMoveRequestCancel();
- }
+ if (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask) // Only set during request
+ {
+ ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back();
+ IM_ASSERT(nav_tree_node_data->ID == window->IDStack.back());
+ if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
+ NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, nav_tree_node_data);
+ g.NavTreeNodeStack.pop_back();
+ }
window->DC.TreeJumpToParentOnPopMask &= tree_depth_mask - 1;
IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
@@ -6385,7 +6515,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl
ImGuiID id = window->GetID(label);
flags |= ImGuiTreeNodeFlags_CollapsingHeader;
if (p_visible)
- flags |= ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton;
+ flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton;
bool is_open = TreeNodeBehavior(id, flags, label);
if (p_visible != NULL)
{
@@ -6395,8 +6525,8 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl
ImGuiContext& g = *GImGui;
ImGuiLastItemData last_item_backup = g.LastItemData;
float button_size = g.FontSize;
- float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x * 2.0f - button_size);
- float button_y = g.LastItemData.Rect.Min.y;
+ float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size);
+ float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y;
ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id);
if (CloseButton(close_button_id, ImVec2(button_x, button_y)))
*p_visible = false;
@@ -6414,7 +6544,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl
// Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image.
// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
-// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowItemOverlap are also frequently used flags.
+// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags.
// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
{
@@ -6451,8 +6581,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
{
const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
const float spacing_y = style.ItemSpacing.y;
- const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
- const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
+ const float spacing_L = IM_TRUNC(spacing_x * 0.50f);
+ const float spacing_U = IM_TRUNC(spacing_y * 0.50f);
bb.Min.x -= spacing_L;
bb.Min.y -= spacing_U;
bb.Max.x += (spacing_x - spacing_L);
@@ -6460,7 +6590,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
}
//if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
- // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable..
+ // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
const float backup_clip_rect_min_x = window->ClipRect.Min.x;
const float backup_clip_rect_max_x = window->ClipRect.Max.x;
if (span_all_columns)
@@ -6486,10 +6616,15 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
// FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
// which would be advantageous since most selectable are not selected.
- if (span_all_columns && window->DC.CurrentColumns)
- PushColumnsBackground();
- else if (span_all_columns && g.CurrentTable)
- TablePushBackgroundChannel();
+ if (span_all_columns)
+ {
+ if (g.CurrentTable)
+ TablePushBackgroundChannel();
+ else if (window->DC.CurrentColumns)
+ PushColumnsBackground();
+ g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;
+ g.LastItemData.ClipRect = window->ClipRect;
+ }
// We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
ImGuiButtonFlags button_flags = 0;
@@ -6498,7 +6633,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
- if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }
+ if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; }
const bool was_selected = selected;
bool hovered, held;
@@ -6527,9 +6662,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
if (pressed)
MarkItemEdited(id);
- if (flags & ImGuiSelectableFlags_AllowItemOverlap)
- SetItemAllowOverlap();
-
// In this branch, Selectable() cannot toggle the selection so this will never trigger.
if (selected != was_selected) //-V547
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
@@ -6540,12 +6672,16 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
}
- RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
+ if (g.NavId == id)
+ RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
- if (span_all_columns && window->DC.CurrentColumns)
- PopColumnsBackground();
- else if (span_all_columns && g.CurrentTable)
- TablePopBackgroundChannel();
+ if (span_all_columns)
+ {
+ if (g.CurrentTable)
+ TablePopBackgroundChannel();
+ else if (window->DC.CurrentColumns)
+ PopColumnsBackground();
+ }
RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
@@ -6570,6 +6706,212 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
return false;
}
+
+//-------------------------------------------------------------------------
+// [SECTION] Widgets: Typing-Select support
+//-------------------------------------------------------------------------
+
+// [Experimental] Currently not exposed in public API.
+// Consume character inputs and return search request, if any.
+// This would typically only be called on the focused window or location you want to grab inputs for, e.g.
+// if (ImGui::IsWindowFocused(...))
+// if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest())
+// focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1);
+// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer).
+ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiTypingSelectState* data = &g.TypingSelectState;
+ ImGuiTypingSelectRequest* out_request = &data->Request;
+
+ // Clear buffer
+ const float TYPING_SELECT_RESET_TIMER = 1.80f; // FIXME: Potentially move to IO config.
+ const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times
+ if (data->SearchBuffer[0] != 0)
+ {
+ bool clear_buffer = false;
+ clear_buffer |= (g.NavFocusScopeId != data->FocusScope);
+ clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time);
+ clear_buffer |= g.NavAnyRequest;
+ clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere
+ clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter);
+ clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0;
+ //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); }
+ if (clear_buffer)
+ data->Clear();
+ }
+
+ // Append to buffer
+ const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;
+ int buffer_len = (int)strlen(data->SearchBuffer);
+ bool select_request = false;
+ for (ImWchar w : g.IO.InputQueueCharacters)
+ {
+ const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1);
+ if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks
+ continue;
+ char w_buf[5];
+ ImTextCharToUtf8(w_buf, (unsigned int)w);
+ if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0)
+ {
+ select_request = true; // Same character: don't need to append to buffer.
+ continue;
+ }
+ if (data->SingleCharModeLock)
+ {
+ data->Clear(); // Different character: clear
+ buffer_len = 0;
+ }
+ memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append
+ buffer_len += w_len;
+ select_request = true;
+ }
+ g.IO.InputQueueCharacters.resize(0);
+
+ // Handle backspace
+ if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat))
+ {
+ char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len);
+ *p = 0;
+ buffer_len = (int)(p - data->SearchBuffer);
+ }
+
+ // Return request if any
+ if (buffer_len == 0)
+ return NULL;
+ if (select_request)
+ {
+ data->FocusScope = g.NavFocusScopeId;
+ data->LastRequestFrame = g.FrameCount;
+ data->LastRequestTime = (float)g.Time;
+ }
+ out_request->Flags = flags;
+ out_request->SearchBufferLen = buffer_len;
+ out_request->SearchBuffer = data->SearchBuffer;
+ out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount);
+ out_request->SingleCharMode = false;
+ out_request->SingleCharSize = 0;
+
+ // Calculate if buffer contains the same character repeated.
+ // - This can be used to implement a special search mode on first character.
+ // - Performed on UTF-8 codepoint for correctness.
+ // - SingleCharMode is always set for first input character, because it usually leads to a "next".
+ if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode)
+ {
+ const char* buf_begin = out_request->SearchBuffer;
+ const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen;
+ const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end);
+ const char* p = buf_begin + c0_len;
+ for (; p < buf_end; p += c0_len)
+ if (memcmp(buf_begin, p, (size_t)c0_len) != 0)
+ break;
+ const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0;
+ out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock);
+ out_request->SingleCharSize = (ImS8)c0_len;
+ data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode.
+ }
+
+ return out_request;
+}
+
+static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2)
+{
+ int match_len = 0;
+ while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++))
+ match_len++;
+ return match_len;
+}
+
+// Default handler for finding a result for typing-select. You may implement your own.
+// You might want to display a tooltip to visualize the current request SearchBuffer
+// When SingleCharMode is set:
+// - it is better to NOT display a tooltip of other on-screen display indicator.
+// - the index of the currently focused item is required.
+// if your SetNextItemSelectionData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.
+int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)
+{
+ if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot.
+ return -1;
+ int idx = -1;
+ if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode))
+ idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx);
+ else
+ idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data);
+ if (idx != -1)
+ NavRestoreHighlightAfterMove();
+ return idx;
+}
+
+// Special handling when a single character is repeated: perform search on a single letter and goes to next.
+int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)
+{
+ // FIXME: Assume selection user data is index. Would be extremely practical.
+ //if (nav_item_idx == -1)
+ // nav_item_idx = (int)g.NavLastValidSelectionUserData;
+
+ int first_match_idx = -1;
+ bool return_next_match = false;
+ for (int idx = 0; idx < items_count; idx++)
+ {
+ const char* item_name = get_item_name_func(user_data, idx);
+ if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize)
+ continue;
+ if (return_next_match) // Return next matching item after current item.
+ return idx;
+ if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value.
+ return idx;
+ if (first_match_idx == -1) // Record first match for wrapping.
+ first_match_idx = idx;
+ if (nav_item_idx == idx) // Record that we encountering nav_item so we can return next match.
+ return_next_match = true;
+ }
+ return first_match_idx; // First result
+}
+
+int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data)
+{
+ int longest_match_idx = -1;
+ int longest_match_len = 0;
+ for (int idx = 0; idx < items_count; idx++)
+ {
+ const char* item_name = get_item_name_func(user_data, idx);
+ const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name);
+ if (match_len <= longest_match_len)
+ continue;
+ longest_match_idx = idx;
+ longest_match_len = match_len;
+ if (match_len == req->SearchBufferLen)
+ break;
+ }
+ return longest_match_idx;
+}
+
+void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)
+{
+#ifndef IMGUI_DISABLE_DEBUG_TOOLS
+ Text("SearchBuffer = \"%s\"", data->SearchBuffer);
+ Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock);
+ Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame);
+#else
+ IM_UNUSED(data);
+#endif
+}
+
+
+//-------------------------------------------------------------------------
+// [SECTION] Widgets: Multi-Select support
+//-------------------------------------------------------------------------
+
+void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)
+{
+ // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code!
+ // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api.
+ ImGuiContext& g = *GImGui;
+ g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;
+ g.NextItemData.SelectionUserData = selection_user_data;
+}
+
+
//-------------------------------------------------------------------------
// [SECTION] Widgets: ListBox
//-------------------------------------------------------------------------
@@ -6578,6 +6920,7 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
// - ListBox()
//-------------------------------------------------------------------------
+// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height).
bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
@@ -6593,7 +6936,7 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
// Size default to hold ~7.25 items.
// Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
- ImVec2 size = ImFloor(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));
+ ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));
ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
@@ -6603,19 +6946,21 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
{
ItemSize(bb.GetSize(), style.FramePadding.y);
ItemAdd(bb, 0, &frame_bb);
+ g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
return false;
}
- // FIXME-OPT: We could omit the BeginGroup() if label_size.x but would need to omit the EndGroup() as well.
+ // FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well.
BeginGroup();
if (label_size.x > 0.0f)
{
ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y);
RenderText(label_pos, label);
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size);
+ AlignTextToFramePadding();
}
- BeginChildFrame(id, frame_bb.GetSize());
+ BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle);
return true;
}
@@ -6626,7 +6971,7 @@ void ImGui::EndListBox()
IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?");
IM_UNUSED(window);
- EndChildFrame();
+ EndChild();
EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label
}
@@ -6638,7 +6983,7 @@ bool ImGui::ListBox(const char* label, int* current_item, const char* const item
// This is merely a helper around BeginListBox(), EndListBox().
// Considering using those directly to submit custom data or store selection differently.
-bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
+bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items)
{
ImGuiContext& g = *GImGui;
@@ -6646,7 +6991,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
if (height_in_items < 0)
height_in_items = ImMin(items_count, 7);
float height_in_items_f = height_in_items + 0.25f;
- ImVec2 size(0.0f, ImFloor(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));
+ ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));
if (!BeginListBox(label, size))
return false;
@@ -6659,8 +7004,8 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
while (clipper.Step())
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
- const char* item_text;
- if (!items_getter(data, i, &item_text))
+ const char* item_text = getter(user_data, i);
+ if (item_text == NULL)
item_text = "*Unknown item*";
PushID(i);
@@ -6714,7 +7059,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, 0, &frame_bb))
return -1;
- const bool hovered = ItemHoverable(frame_bb, id);
+ const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
// Determine scale from values if not specified
if (scale_min == FLT_MAX || scale_max == FLT_MAX)
@@ -7007,12 +7352,18 @@ void ImGui::EndMenuBar()
PopClipRect();
PopID();
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
- g.GroupStack.back().EmitItem = false;
- EndGroup(); // Restore position on layer 0
+
+ // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here.
+ ImGuiGroupData& group_data = g.GroupStack.back();
+ group_data.EmitItem = false;
+ ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos;
+ window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent.
+ EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore
window->DC.LayoutType = ImGuiLayoutType_Vertical;
window->DC.IsSameLine = false;
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
window->DC.MenuBarAppending = false;
+ window->DC.CursorMaxPos = restore_cursor_max_pos;
}
// Important: calling order matters!
@@ -7168,15 +7519,15 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
// Menu inside an horizontal menu bar
// Selectable extend their highlight by half ItemSpacing in each direction.
// For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
- popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
- window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
+ popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
+ window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
float w = label_size.x;
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y));
RenderText(text_pos, label);
PopStyleVar();
- window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
+ window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
}
else
{
@@ -7185,7 +7536,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
- float checkmark_w = IM_FLOOR(g.FontSize * 1.20f);
+ float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
@@ -7203,6 +7554,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
PopItemFlag();
bool want_open = false;
+ bool want_open_nav_init = false;
bool want_close = false;
if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
{
@@ -7213,18 +7565,18 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL;
if (g.HoveredWindow == window && child_menu_window != NULL)
{
- float ref_unit = g.FontSize; // FIXME-DPI
- float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f;
- ImRect next_window_rect = child_menu_window->Rect();
+ const float ref_unit = g.FontSize; // FIXME-DPI
+ const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f;
+ const ImRect next_window_rect = child_menu_window->Rect();
ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta);
ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR();
ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR();
- float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack.
+ const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // Add a bit of extra slack.
ta.x += child_dir * -0.5f;
tb.x += child_dir * ref_unit;
tc.x += child_dir * ref_unit;
- tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle has maximum height to limit the slope and the bias toward large sub-menus
- tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f);
+ tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f); // Triangle has maximum height to limit the slope and the bias toward large sub-menus
+ tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f);
moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
//GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
}
@@ -7232,18 +7584,22 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
// The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)
// But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.
// (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)
- if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover)
+ if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover && g.ActiveId == 0)
want_close = true;
// Open
+ // (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test)
if (!menu_is_open && pressed) // Click/activate to open
want_open = true;
else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open
want_open = true;
+ else if (!menu_is_open && hovered && g.HoveredIdTimer >= 0.30f && g.MouseStationaryTimer >= 0.30f) // Hover to open (timer fallback)
+ want_open = true;
if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
{
- want_open = true;
+ want_open = want_open_nav_init = true;
NavMoveRequestCancel();
+ NavRestoreHighlightAfterMove();
}
}
else
@@ -7275,13 +7631,13 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
{
- // Don't reopen/recycle same menu level in the same frame, first close the other menu and yield for a frame.
+ // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame.
OpenPopup(label);
}
else if (want_open)
{
menu_is_open = true;
- OpenPopup(label);
+ OpenPopup(label, ImGuiPopupFlags_NoReopen);// | (want_open_nav_init ? ImGuiPopupFlags_NoReopenAlwaysNavInit : 0));
}
if (menu_is_open)
@@ -7293,6 +7649,14 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
PopStyleVar();
if (menu_is_open)
{
+ // Implement what ImGuiPopupFlags_NoReopenAlwaysNavInit would do:
+ // Perform an init request in the case the popup was already open (via a previous mouse hover)
+ if (want_open && want_open_nav_init && !g.NavInitRequest)
+ {
+ FocusWindow(g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal);
+ NavInitWindow(g.CurrentWindow, false);
+ }
+
// Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu()
// (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck)
g.LastItemData = last_item_in_parent;
@@ -7362,14 +7726,14 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
float w = label_size.x;
- window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
+ window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f));
PopStyleVar();
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
RenderText(text_pos, label);
- window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
+ window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
}
else
{
@@ -7378,7 +7742,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f;
- float checkmark_w = IM_FLOOR(g.FontSize * 1.20f);
+ float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame
float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
@@ -7523,6 +7887,8 @@ bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);
ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2);
tab_bar->ID = id;
+ tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f);
+ tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f);
return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused);
}
@@ -7533,6 +7899,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
if (window->SkipItems)
return false;
+ IM_ASSERT(tab_bar->ID != 0);
if ((flags & ImGuiTabBarFlags_DockNode) == 0)
PushOverrideID(tab_bar->ID);
@@ -7575,12 +7942,12 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);
// Draw separator
+ // (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable)
const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive);
- const float y = tab_bar->BarRect.Max.y - 1.0f;
+ if (g.Style.TabBarBorderSize > 0.0f)
{
- const float separator_min_x = tab_bar->BarRect.Min.x - IM_FLOOR(window->WindowPadding.x * 0.5f);
- const float separator_max_x = tab_bar->BarRect.Max.x + IM_FLOOR(window->WindowPadding.x * 0.5f);
- window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f);
+ const float y = tab_bar->BarRect.Max.y;
+ window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col);
}
return true;
}
@@ -7787,7 +8154,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];
- float shrinked_width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width);
+ float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width);
if (shrinked_width < 0.0f)
continue;
@@ -7947,7 +8314,7 @@ void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
if (tab->Flags & ImGuiTabItemFlags_Button)
return; // A button appended with TabItemButton().
- if (!(tab->Flags & ImGuiTabItemFlags_UnsavedDocument))
+ if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0)
{
// This will remove a frame of lag for selecting another tab on closure.
// However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure
@@ -8374,7 +8741,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
size.x = tab->Width;
if (is_central_section)
- window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
+ window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
else
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);
ImVec2 pos = window->DC.CursorPos;
@@ -8398,7 +8765,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
}
// Click to Select a tab
- ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowItemOverlap);
+ // Allow the close button to overlap
+ ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap);
if (g.DragDropActive)
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
bool hovered, held;
@@ -8406,10 +8774,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
if (pressed && !is_tab_button)
TabBarQueueFocus(tab_bar, tab);
- // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered)
- if (g.ActiveId != id)
- SetItemAllowOverlap();
-
// Drag and drop: re-order tabs
if (held && !tab_appearing && IsMouseDragging(0))
{
@@ -8431,7 +8795,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth)
{
// Enlarge tab display when hovering
- bb.Max.x = bb.Min.x + IM_FLOOR(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));
+ bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));
display_draw_list = GetForegroundDrawList(window);
TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));
}
@@ -8474,8 +8838,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
// FIXME: We may want disabled tab to still display the tooltip?
if (text_clipped && g.HoveredId == id && !held)
if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))
- if (IsItemHovered(ImGuiHoveredFlags_DelayNormal))
- SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
+ SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected
if (is_tab_button)
@@ -8526,7 +8889,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
IM_ASSERT(width > 0.0f);
const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));
const float y1 = bb.Min.y + 1.0f;
- const float y2 = bb.Max.y - 1.0f;
+ const float y2 = bb.Max.y - g.Style.TabBarBorderSize;
draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
@@ -8577,7 +8940,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
}
const float button_sz = g.FontSize;
- const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x * 2.0f - button_sz), bb.Min.y);
+ const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y);
// Close Button & Unsaved Marker
// We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap()
@@ -8595,10 +8958,8 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
if (close_button_visible)
{
ImGuiLastItemData last_item_backup = g.LastItemData;
- PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding);
if (CloseButton(close_button_id, button_pos))
close_button_pressed = true;
- PopStyleVar();
g.LastItemData = last_item_backup;
// Close with middle mouse button
@@ -8607,7 +8968,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
}
else if (unsaved_marker_visible)
{
- const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f);
+ const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz));
RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text));
}
diff --git a/extern/src/imgui/imstb_textedit.h b/extern/src/imgui/imstb_textedit.h
index a8a82311..783054ab 100644
--- a/extern/src/imgui/imstb_textedit.h
+++ b/extern/src/imgui/imstb_textedit.h
@@ -2,8 +2,9 @@
// This is a slightly modified version of stb_textedit.h 1.14.
// Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
-// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000)
+// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// Grep for [DEAR IMGUI] to find the changes.
+// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
// stb_textedit.h - v1.14 - public domain - Sean Barrett
// Development of this library was sponsored by RAD Game Tools
@@ -30,7 +31,7 @@
// DEPENDENCIES
//
// Uses the C runtime function 'memmove', which you can override
-// by defining STB_TEXTEDIT_memmove before the implementation.
+// by defining IMSTB_TEXTEDIT_memmove before the implementation.
// Uses no other functions. Performs no runtime allocations.
//
//
@@ -40,7 +41,7 @@
// 1.13 (2019-02-07) fix bug in undo size management
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
-// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
+// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
// 1.9 (2016-08-27) customizable move-by-word
// 1.8 (2016-04-02) better keyboard handling when mouse button is down
// 1.7 (2015-09-13) change y range handling in case baseline is non-0
@@ -274,8 +275,8 @@
////
////
-#ifndef INCLUDE_STB_TEXTEDIT_H
-#define INCLUDE_STB_TEXTEDIT_H
+#ifndef INCLUDE_IMSTB_TEXTEDIT_H
+#define INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////
//
@@ -286,33 +287,33 @@
// and undo state.
//
-#ifndef STB_TEXTEDIT_UNDOSTATECOUNT
-#define STB_TEXTEDIT_UNDOSTATECOUNT 99
+#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
+#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
#endif
-#ifndef STB_TEXTEDIT_UNDOCHARCOUNT
-#define STB_TEXTEDIT_UNDOCHARCOUNT 999
+#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
+#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
#endif
-#ifndef STB_TEXTEDIT_CHARTYPE
-#define STB_TEXTEDIT_CHARTYPE int
+#ifndef IMSTB_TEXTEDIT_CHARTYPE
+#define IMSTB_TEXTEDIT_CHARTYPE int
#endif
-#ifndef STB_TEXTEDIT_POSITIONTYPE
-#define STB_TEXTEDIT_POSITIONTYPE int
+#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
+#define IMSTB_TEXTEDIT_POSITIONTYPE int
#endif
typedef struct
{
// private data
- STB_TEXTEDIT_POSITIONTYPE where;
- STB_TEXTEDIT_POSITIONTYPE insert_length;
- STB_TEXTEDIT_POSITIONTYPE delete_length;
+ IMSTB_TEXTEDIT_POSITIONTYPE where;
+ IMSTB_TEXTEDIT_POSITIONTYPE insert_length;
+ IMSTB_TEXTEDIT_POSITIONTYPE delete_length;
int char_storage;
} StbUndoRecord;
typedef struct
{
// private data
- StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
- STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
+ StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
+ IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
short undo_point, redo_point;
int undo_char_point, redo_char_point;
} StbUndoState;
@@ -371,7 +372,7 @@ typedef struct
float ymin,ymax; // height of row above and below baseline
int num_chars;
} StbTexteditRow;
-#endif //INCLUDE_STB_TEXTEDIT_H
+#endif //INCLUDE_IMSTB_TEXTEDIT_H
////////////////////////////////////////////////////////////////////////////
@@ -384,11 +385,11 @@ typedef struct
// implementation isn't include-guarded, since it might have indirectly
// included just the "header" portion
-#ifdef STB_TEXTEDIT_IMPLEMENTATION
+#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
-#ifndef STB_TEXTEDIT_memmove
+#ifndef IMSTB_TEXTEDIT_memmove
#include
-#define STB_TEXTEDIT_memmove memmove
+#define IMSTB_TEXTEDIT_memmove memmove
#endif
@@ -398,7 +399,7 @@ typedef struct
//
// traverse the layout to locate the nearest character to a display position
-static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
+static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
{
StbTexteditRow r;
int n = STB_TEXTEDIT_STRINGLEN(str);
@@ -458,7 +459,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)
}
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
-static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
+static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
// goes off the top or bottom of the text
@@ -476,7 +477,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
-static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
+static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
{
int p = 0;
@@ -502,11 +503,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
//
// forward declarations
-static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
-static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
-static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
+static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
+static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
+static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
-static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
+static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
typedef struct
{
@@ -518,7 +519,7 @@ typedef struct
// find the x/y location of a character, and remember info about the previous row in
// case we get a move-up event (for page up, we'll have to rescan)
-static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)
+static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
{
StbTexteditRow r;
int prev_start = 0;
@@ -549,7 +550,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
i += r.num_chars;
find->y += r.baseline_y_delta;
if (i == z) // [DEAR IMGUI]
+ {
+ r.num_chars = 0; // [DEAR IMGUI]
break; // [DEAR IMGUI]
+ }
}
find->first_char = first = i;
@@ -566,7 +570,7 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
// make the selection/cursor state valid if client altered the string
-static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
+static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
int n = STB_TEXTEDIT_STRINGLEN(str);
if (STB_TEXT_HAS_SELECTION(state)) {
@@ -580,7 +584,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
}
// delete characters while updating undo
-static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
+static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
{
stb_text_makeundo_delete(str, state, where, len);
STB_TEXTEDIT_DELETECHARS(str, where, len);
@@ -588,7 +592,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta
}
// delete the section
-static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
+static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
stb_textedit_clamp(str, state);
if (STB_TEXT_HAS_SELECTION(state)) {
@@ -625,7 +629,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state)
}
// move cursor to last character of selection
-static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
+static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_sortselection(state);
@@ -637,13 +641,13 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
}
#ifdef STB_TEXTEDIT_IS_SPACE
-static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
+static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{
return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
}
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
-static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
+static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
@@ -658,7 +662,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
#endif
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
-static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
+static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{
const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character
@@ -685,7 +689,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
}
// API cut: delete selection
-static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
+static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
if (STB_TEXT_HAS_SELECTION(state)) {
stb_textedit_delete_selection(str,state); // implicitly clamps
@@ -696,7 +700,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
}
// API paste: replace existing selection with passed-in text
-static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
+static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
{
// if there's a selection, the paste should delete it
stb_textedit_clamp(str, state);
@@ -717,14 +721,14 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
#endif
// API key: process a keyboard input
-static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
+static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{
retry:
switch (key) {
default: {
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
- STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
+ IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
// can't add newline in single-line mode
if (c == '\n' && state->single_line)
@@ -889,8 +893,8 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
- #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
- if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
+ #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
+ if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
@@ -951,8 +955,8 @@ retry:
x = row.x0;
for (i=0; i < row.num_chars; ++i) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
- #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
- if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
+ #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
+ if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
#endif
x += dx;
@@ -1109,8 +1113,8 @@ retry:
static void stb_textedit_flush_redo(StbUndoState *state)
{
- state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
- state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
+ state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
+ state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
}
// discard the oldest entry in the undo list
@@ -1122,13 +1126,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
int n = state->undo_rec[0].insert_length, i;
// delete n characters from all other records
state->undo_char_point -= n;
- STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
+ IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
for (i=0; i < state->undo_point; ++i)
if (state->undo_rec[i].char_storage >= 0)
state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
}
--state->undo_point;
- STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
+ IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
}
}
@@ -1138,7 +1142,7 @@ static void stb_textedit_discard_undo(StbUndoState *state)
// fill up even though the undo buffer didn't
static void stb_textedit_discard_redo(StbUndoState *state)
{
- int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
+ int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
if (state->redo_point <= k) {
// if the k'th undo state has characters, clean those up
@@ -1146,7 +1150,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
int n = state->undo_rec[k].insert_length, i;
// move the remaining redo character data to the end of the buffer
state->redo_char_point += n;
- STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
+ IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
// adjust the position of all the other records to account for above memmove
for (i=state->redo_point; i < k; ++i)
if (state->undo_rec[i].char_storage >= 0)
@@ -1154,12 +1158,12 @@ static void stb_textedit_discard_redo(StbUndoState *state)
}
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
// [DEAR IMGUI]
- size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
+ size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
- STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
+ IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
// now move redo_point to point to the new one
++state->redo_point;
@@ -1173,32 +1177,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch
// if we have no free records, we have to make room, by sliding the
// existing records down
- if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
+ if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
stb_textedit_discard_undo(state);
// if the characters to store won't possibly fit in the buffer, we can't undo
- if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {
+ if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
state->undo_point = 0;
state->undo_char_point = 0;
return NULL;
}
// if we don't have enough free characters in the buffer, we have to make room
- while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)
+ while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
stb_textedit_discard_undo(state);
return &state->undo_rec[state->undo_point++];
}
-static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
+static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
{
StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
if (r == NULL)
return NULL;
r->where = pos;
- r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
- r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
+ r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
+ r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
if (insert_len == 0) {
r->char_storage = -1;
@@ -1210,7 +1214,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
}
}
-static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
+static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord u, *r;
@@ -1237,7 +1241,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// characters stored for *undoing* don't leave room for redo
// if the last is true, we have to bail
- if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {
+ if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
// the undo records take up too much character space; there's no space to store the redo characters
r->insert_length = 0;
} else {
@@ -1246,7 +1250,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// there's definitely room to store the characters eventually
while (s->undo_char_point + u.delete_length > s->redo_char_point) {
// should never happen:
- if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
+ if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// there's currently not enough room, so discard a redo record
stb_textedit_discard_redo(s);
@@ -1278,11 +1282,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
s->redo_point--;
}
-static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
+static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
{
StbUndoState *s = &state->undostate;
StbUndoRecord *u, r;
- if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
+ if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
return;
// we need to do two things: apply the redo record, and create an undo record
@@ -1334,20 +1338,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le
stb_text_createundo(&state->undostate, where, 0, length);
}
-static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
+static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
{
int i;
- STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
+ IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
if (p) {
for (i=0; i < length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
}
}
-static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
+static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
{
int i;
- STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
+ IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
if (p) {
for (i=0; i < old_length; ++i)
p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
@@ -1359,8 +1363,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
{
state->undostate.undo_point = 0;
state->undostate.undo_char_point = 0;
- state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;
- state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;
+ state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
+ state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
state->select_end = state->select_start = 0;
state->cursor = 0;
state->has_preferred_x = 0;
@@ -1383,16 +1387,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
-static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
+static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
{
- return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
+ return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
}
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
-#endif//STB_TEXTEDIT_IMPLEMENTATION
+#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
/*
------------------------------------------------------------------------------
diff --git a/extern/src/imgui/imstb_truetype.h b/extern/src/imgui/imstb_truetype.h
index 35c827e6..976f09cb 100644
--- a/extern/src/imgui/imstb_truetype.h
+++ b/extern/src/imgui/imstb_truetype.h
@@ -656,7 +656,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
// If skip != 0, this tells stb_truetype to skip any codepoints for which
// there is no corresponding glyph. If skip=0, which is the default, then
-// codepoints without a glyph recived the font's "missing character" glyph,
+// codepoints without a glyph received the font's "missing character" glyph,
// typically an empty box by convention.
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
diff --git a/extern/src/libdeflate.h b/extern/src/libdeflate.h
index 0a274f0f..05dac048 100644
--- a/extern/src/libdeflate.h
+++ b/extern/src/libdeflate.h
@@ -13,8 +13,8 @@ extern "C" {
#endif
#define LIBDEFLATE_VERSION_MAJOR 1
-#define LIBDEFLATE_VERSION_MINOR 19
-#define LIBDEFLATE_VERSION_STRING "1.19"
+#define LIBDEFLATE_VERSION_MINOR 20
+#define LIBDEFLATE_VERSION_STRING "1.20"
/*
* Users of libdeflate.dll on Windows can define LIBDEFLATE_DLL to cause
diff --git a/src/ReDIVA/imgui_helper.cpp b/src/ReDIVA/imgui_helper.cpp
index 087c9296..fa3c066a 100644
--- a/src/ReDIVA/imgui_helper.cpp
+++ b/src/ReDIVA/imgui_helper.cpp
@@ -1936,16 +1936,16 @@ namespace ImGui {
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;
const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
- const SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
+ const float_t v_range_f = (float_t)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it.
// Calculate bounds
const float_t grab_padding = 2.0f; // FIXME: Should be part of style.
const float_t slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
float_t grab_sz = style.GrabMinSize * 1.5f;
- // v_range < 0 may happen on integer overflows
- if (!is_floating_point && v_range >= 0)
+ // v_range_f < 0 may happen on integer overflows
+ if (!is_floating_point && v_range_f >= 0.0f)
// For integer sliders: if possible have the grab size represent 1 unit
- grab_sz = ImMax((float_t)(slider_sz / (v_range + 1)), style.GrabMinSize * 1.5f);
+ grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize);
grab_sz = ImMin(grab_sz, slider_sz);
const float_t slider_usable_sz = slider_sz - grab_sz;
const float_t slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;
@@ -1987,9 +1987,8 @@ namespace ImGui {
}
ImRect out_grab_bb;
- if (slider_sz < 1.0f) {
+ if (slider_sz < 1.0f)
out_grab_bb = ImRect(bb.Min, bb.Min);
- }
else {
float_t grab_t = ScaleRatioFromValueT(data_type,
*v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
@@ -2033,9 +2032,9 @@ namespace ImGui {
if (tweak_slow)
input_delta /= 10.0f;
}
- else if ((v_range >= -100.0f && v_range <= 100.0f) || tweak_slow)
+ else if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow)
// Gamepad/keyboard tweak speeds in integer steps
- input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / (float_t)v_range;
+ input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f;
else
input_delta /= 100.0f;
@@ -2082,6 +2081,10 @@ namespace ImGui {
}
}
+ if (set_new_value)
+ if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
+ set_new_value = false;
+
if (set_new_value) {
TYPE v_new = ScaleValueFromRatioT(data_type,
clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
@@ -2135,13 +2138,9 @@ namespace ImGui {
const void* p_min, const void* p_max, const void* p_step, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb) {
// Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0)
- && "Invalid ImGuiSliderFlags flag! Has the 'float_t power' argument been"
+ && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been"
" mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
- ImGuiContext& g = *GImGui;
- if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
- return false;
-
switch (data_type) {
case ImGuiDataType_S8: {
int32_t v32 = (int32_t) * (int8_t*)p_v;
@@ -2273,17 +2272,16 @@ namespace ImGui {
if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt;
- const bool hovered = ItemHoverable(frame_bb, id);
+ const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active) {
// Tabbing or CTRL-clicking on Slider turns it into an input box
- const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;
- const bool clicked = hovered && IsMouseClicked(0, id);
- const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id);
+ const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
+ const bool make_active = (clicked || g.NavActivateId == id);
if (make_active && clicked)
SetKeyOwner(ImGuiKey_MouseLeft, id);
if (make_active && temp_input_allowed)
- if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
+ if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
temp_input_is_active = true;
if (make_active && !temp_input_is_active) {
@@ -2302,14 +2300,14 @@ namespace ImGui {
}
// Draw frame
- const uint32_t frame_col = GetColorU32(g.ActiveId == id
+ const ImU32 frame_col = GetColorU32(g.ActiveId == id
? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
RenderNavHighlight(frame_bb, id);
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
// Slider behavior
ImRect grab_bb;
- bool value_changed = SliderBehaviorStep(frame_bb, id, data_type,
+ const bool value_changed = SliderBehaviorStep(frame_bb, id, data_type,
p_data, p_min, p_max, p_step, format, flags, &grab_bb);
if (value_changed)
MarkItemEdited(id);
@@ -2334,7 +2332,8 @@ namespace ImGui {
io.KeyRepeatDelay = key_repeat_delay;
io.KeyRepeatRate = key_repeat_rate;
- IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags
+ | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
return value_changed;
}
}
\ No newline at end of file
diff --git a/thirdparty.txt b/thirdparty.txt
index 17574d33..908ec45d 100644
--- a/thirdparty.txt
+++ b/thirdparty.txt
@@ -119,32 +119,6 @@ The EGL Specification and various headers:
-------------------------------------------------------------------------------
-cimgui
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Stephan Dilly
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
--------------------------------------------------------------------------------
-
imgui
The MIT License (MIT)