From f969e68c10ad59c9685fa2c4b9af564610f114ac Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Aug 2021 20:30:51 +0200 Subject: [PATCH 001/332] Fix BeginDisabled(false), (#211, #4452) --- imgui.cpp | 7 ++----- imgui.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 354df7e6..78030ef0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6627,12 +6627,9 @@ void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + g.DisabledAlphaBackup = g.Style.Alpha; if (!was_disabled && disabled) - { - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); - g.DisabledAlphaBackup = g.Style.Alpha; - g.Style.Alpha *= g.Style.DisabledAlpha; - } + g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); //PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled); g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); diff --git a/imgui.h b/imgui.h index ff91abde..1df673a3 100644 --- a/imgui.h +++ b/imgui.h @@ -61,7 +61,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.84" -#define IMGUI_VERSION_NUM 18400 +#define IMGUI_VERSION_NUM 18401 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 32d4f6c5d9088dd1599d342a08f8376c00565900 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Aug 2021 23:59:46 +0200 Subject: [PATCH 002/332] Fix BeginDisabled(false), again, (#211, #4452, #4453) Version 1.84.1 (forced pushed since our earlier versioning didn't sort correctly in github web) --- docs/CHANGELOG.txt | 11 +++++++++++ imgui.cpp | 6 +++--- imgui.h | 5 +++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 237a112f..f4d4a447 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -31,6 +31,17 @@ HOW TO UPDATE? - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.84.1 (Released 2021-08-20) +----------------------------------------------------------------------- + +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.1 + +Other Changes: + +- Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) + + ----------------------------------------------------------------------- VERSION 1.84 (Released 2021-08-20) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 78030ef0..9139afee 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6621,7 +6621,7 @@ void ImGui::PopItemFlag() // - Those can be nested but this cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep things disabled) // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 -// - BeginDisabled(false) essentially does nothing but is provided to facilitate use of boolean expressions +// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() void ImGui::BeginDisabled(bool disabled) { @@ -6630,8 +6630,8 @@ void ImGui::BeginDisabled(bool disabled) g.DisabledAlphaBackup = g.Style.Alpha; if (!was_disabled && disabled) g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); - //PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled); - g.CurrentItemFlags |= ImGuiItemFlags_Disabled; + if (was_disabled || disabled) + g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); } diff --git a/imgui.h b/imgui.h index 1df673a3..4b7c973b 100644 --- a/imgui.h +++ b/imgui.h @@ -60,8 +60,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.84" -#define IMGUI_VERSION_NUM 18401 +#define IMGUI_VERSION "1.84.1" +#define IMGUI_VERSION_NUM 18403 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -801,6 +801,7 @@ namespace ImGui // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) + // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); From e6ffc0429107b5356f215fb450561b19ca2569a1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Aug 2021 11:40:08 +0200 Subject: [PATCH 003/332] Moved ImDrawIdx declaration higher up in imgui.h (to ease work for dear_bindings) Shuffled low-level data types declarations and tweaked comments --- imgui.h | 63 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/imgui.h b/imgui.h index 4b7c973b..c6132abe 100644 --- a/imgui.h +++ b/imgui.h @@ -61,7 +61,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.84.1" -#define IMGUI_VERSION_NUM 18403 +#define IMGUI_VERSION_NUM 18404 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -160,7 +160,7 @@ struct ImGuiTextBuffer; // Helper to hold and append into a text buf struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor -// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) +// Enums/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) // - 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.NavigateTo") 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. @@ -200,27 +200,22 @@ typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: f typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() -// Other types -#ifndef ImTextureID // ImTextureID [configurable type: override in imconfig.h with '#define ImTextureID xxx'] -typedef void* ImTextureID; // User data for rendering backend to identify a texture. This is whatever to you want it to be! read the FAQ about ImTextureID for details. +// ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type] +// - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file. +// - This can be whatever to you want it to be! read the FAQ about ImTextureID for details. +#ifndef ImTextureID +typedef void* ImTextureID; // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that) #endif -typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string. -typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() -typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() -typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() -// 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. -#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 -typedef ImWchar16 ImWchar; +// ImDrawIdx: vertex index. [Compile-time configurable type] +// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended). +// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file. +#ifndef ImDrawIdx +typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends) #endif -// Basic scalar data types +// Scalar data types +typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) typedef signed char ImS8; // 8-bit signed integer typedef unsigned char ImU8; // 8-bit unsigned integer typedef signed short ImS16; // 16-bit signed integer @@ -239,7 +234,24 @@ typedef signed long long ImS64; // 64-bit signed integer (post C++11) typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11) #endif -// 2D vector (often used to store positions or sizes) +// 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. +#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 +typedef ImWchar16 ImWchar; +#endif + +// Callback and functions types +typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() +typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() +typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() +typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() + +// 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. IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { @@ -253,7 +265,7 @@ struct ImVec2 #endif }; -// 4D vector (often used to store floating-point colors) +// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type] struct ImVec4 { float x, y, z, w; @@ -618,7 +630,7 @@ namespace ImGui 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); // Widgets: Data Plotting - // - Consider using ImPlot (https://github.com/epezent/implot) + // - 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)); @@ -2276,13 +2288,6 @@ struct ImDrawCmd inline ImTextureID GetTexID() const { return TextureId; } }; -// Vertex index, default to 16-bit -// To allow large meshes with 16-bit indices: set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset in the renderer backend (recommended). -// To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in imconfig.h. -#ifndef ImDrawIdx -typedef unsigned short ImDrawIdx; -#endif - // Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert From d79ca9b0b682681698ac12132d2c87673b986552 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Aug 2021 14:57:23 +0200 Subject: [PATCH 004/332] Fixed nested BeginDisabled()/EndDisabled() calls. (#211, #4452, #4453, #4462) [Legulysse] --- docs/CHANGELOG.txt | 13 ++++++++++--- imgui.cpp | 6 ++++-- imgui.h | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f4d4a447..951f3f64 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -31,15 +31,22 @@ HOW TO UPDATE? - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.84.2 (Released 2021-08-23) +----------------------------------------------------------------------- + +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.2 + +- Disabled: Fixed nested BeginDisabled()/EndDisabled() calls. (#211, #4452, #4453, #4462) [@Legulysse] + + ----------------------------------------------------------------------- VERSION 1.84.1 (Released 2021-08-20) ----------------------------------------------------------------------- Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.1 -Other Changes: - -- Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) +- Disabled: Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 9139afee..ab27f130 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6627,9 +6627,11 @@ void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; - g.DisabledAlphaBackup = g.Style.Alpha; if (!was_disabled && disabled) + { + g.DisabledAlphaBackup = g.Style.Alpha; g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); + } if (was_disabled || disabled) g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); @@ -6641,7 +6643,7 @@ void ImGui::EndDisabled() bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; //PopItemFlag(); g.ItemFlagsStack.pop_back(); - g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled; + g.CurrentItemFlags = g.ItemFlagsStack.back(); if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } diff --git a/imgui.h b/imgui.h index c6132abe..b56cb852 100644 --- a/imgui.h +++ b/imgui.h @@ -60,8 +60,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.84.1" -#define IMGUI_VERSION_NUM 18404 +#define IMGUI_VERSION "1.84.2" +#define IMGUI_VERSION_NUM 18405 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From e3e1fbcf025cf83413815751f7c33500e1314d57 Mon Sep 17 00:00:00 2001 From: Nicolas Noble Date: Mon, 23 Aug 2021 15:03:22 +0200 Subject: [PATCH 005/332] Backends: OpenGL3: OpenGL: Fixed ES 3.0 shader ("#version 300 es") to use normal precision floats. (#4463) --- backends/imgui_impl_opengl3.cpp | 3 ++- docs/CHANGELOG.txt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index e2dc83ad..5637a0fe 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions. // 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state. @@ -625,7 +626,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() "}\n"; const GLchar* vertex_shader_glsl_300_es = - "precision mediump float;\n" + "precision highp float;\n" "layout (location = 0) in vec2 Position;\n" "layout (location = 1) in vec2 UV;\n" "layout (location = 2) in vec4 Color;\n" diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 951f3f64..6f6d0bee 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,8 @@ HOW TO UPDATE? Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.2 - Disabled: Fixed nested BeginDisabled()/EndDisabled() calls. (#211, #4452, #4453, #4462) [@Legulysse] +- Backends: OpenGL3: OpenGL: Fixed ES 3.0 shader ("#version 300 es") to use normal precision + floats. Avoid wobbly rendering at HD resolutions. (#4463) [@nicolasnoble] ----------------------------------------------------------------------- From 0649f750b496bd0a671cf46a5bc93ee539142fd2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Aug 2021 15:31:06 +0200 Subject: [PATCH 006/332] Version 1.85 WIP --- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ab27f130..f21509ed 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.84 +// dear imgui, v1.85 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index b56cb852..27397e05 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.84 +// dear imgui, v1.85 WIP // (headers) // Help: @@ -60,8 +60,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.84.2" -#define IMGUI_VERSION_NUM 18405 +#define IMGUI_VERSION "1.85 WIP" +#define IMGUI_VERSION_NUM 18406 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d75989d6..5b7a9e18 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.84 +// dear imgui, v1.85 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e1d7b7ee..29a6866a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.84 +// dear imgui, v1.85 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 1eb0cb7a..f009702e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.84 +// dear imgui, v1.85 WIP // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 71ac00f6..e70a4dea 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.84 +// dear imgui, v1.85 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e3ac15f1..95aa3141 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.84 +// dear imgui, v1.85 WIP // (widgets code) /* From e23bee353c314a2c6330692b4ead22ce7dd05024 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Aug 2021 16:15:11 +0200 Subject: [PATCH 007/332] Removed GetWindowContentRegionWidth() function --- docs/CHANGELOG.txt | 12 ++++++++++++ imgui.cpp | 10 ++-------- imgui.h | 8 +++++--- imgui_demo.cpp | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6f6d0bee..dc018964 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -30,6 +30,18 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.85 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +- Removed GetWindowContentRegionWidth() function. keep inline redirection helper. + Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not + very useful in practice, and the only use of it in the demo was illfit. + + +Other Changes: ----------------------------------------------------------------------- VERSION 1.84.2 (Released 2021-08-23) diff --git a/imgui.cpp b/imgui.cpp index f21509ed..d37315a9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -376,6 +376,7 @@ 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. + - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead. - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder @@ -6618,7 +6619,7 @@ void ImGui::PopItemFlag() } // BeginDisabled()/EndDisabled() -// - Those can be nested but this cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep things disabled) +// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. @@ -7416,7 +7417,6 @@ void ImGuiStackSizes::CompareWithCurrentState() // - GetContentRegionMaxAbs() [Internal] // - GetContentRegionAvail(), // - GetWindowContentRegionMin(), GetWindowContentRegionMax() -// - GetWindowContentRegionWidth() // - BeginGroup() // - EndGroup() // Also see in imgui_widgets: tab bars, columns. @@ -7784,12 +7784,6 @@ ImVec2 ImGui::GetWindowContentRegionMax() return window->ContentRegionRect.Max - window->Pos; } -float ImGui::GetWindowContentRegionWidth() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ContentRegionRect.GetWidth(); -} - // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. void ImGui::BeginGroup() diff --git a/imgui.h b/imgui.h index 27397e05..9a9df67f 100644 --- a/imgui.h +++ b/imgui.h @@ -379,9 +379,8 @@ namespace ImGui // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates - IMGUI_API float GetWindowContentRegionWidth(); // + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates // Windows Scrolling IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] @@ -813,6 +812,7 @@ namespace ImGui // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) + // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); @@ -2816,6 +2816,8 @@ struct ImGuiViewport #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.85 (from August 2021) + static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } // OBSOLETED in 1.81 (from February 2021) IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5b7a9e18..01837846 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2372,7 +2372,7 @@ static void ShowDemoWindowLayout() ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; if (disable_mouse_wheel) window_flags |= ImGuiWindowFlags_NoScrollWithMouse; - ImGui::BeginChild("ChildL", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags); + ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), false, window_flags); for (int i = 0; i < 100; i++) ImGui::Text("%04d: scrollable region", i); ImGui::EndChild(); From 6afe9bbb45499f46a37b23633887b83e20f61a66 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Aug 2021 16:35:44 +0200 Subject: [PATCH 008/332] Projects: added GLFW Vulkan to default solution. --- examples/imgui_examples.sln | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 605b1f57..df1006d5 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -9,11 +9,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx10", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx11", "example_win32_directx11\example_win32_directx11.vcxproj", "{9F316E83-5AE5-4939-A723-305A94F48005}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx12", "example_win32_directx12\example_win32_directx12.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_opengl2", "example_glfw_opengl2\example_glfw_opengl2.vcxproj", "{9CDA7840-B7A5-496D-A527-E95571496D18}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_opengl3", "example_glfw_opengl3\example_glfw_opengl3.vcxproj", "{4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx12", "example_win32_directx12\example_win32_directx12.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_vulkan", "example_glfw_vulkan\example_glfw_vulkan.vcxproj", "{57E2DF5A-6FC8-45BB-99DD-91A18C646E80}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -47,6 +49,14 @@ Global {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.ActiveCfg = Debug|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.Build.0 = Debug|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.ActiveCfg = Debug|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.Build.0 = Debug|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.ActiveCfg = Release|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.Build.0 = Release|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.ActiveCfg = Release|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.Build.0 = Release|x64 {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 @@ -63,14 +73,14 @@ Global {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.ActiveCfg = Debug|Win32 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.Build.0 = Debug|Win32 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.ActiveCfg = Debug|x64 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.Build.0 = Debug|x64 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.ActiveCfg = Release|Win32 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.Build.0 = Release|Win32 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.ActiveCfg = Release|x64 - {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.Build.0 = Release|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|Win32.ActiveCfg = Debug|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|Win32.Build.0 = Debug|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|x64.ActiveCfg = Debug|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|x64.Build.0 = Debug|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|Win32.ActiveCfg = Release|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|Win32.Build.0 = Release|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.ActiveCfg = Release|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 68f428b449aeb5fd0ea8c4831904997f691eaba7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Aug 2021 16:42:58 +0200 Subject: [PATCH 009/332] imgui_freetype: Fixed crash when FT_Render_Glyph() returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145 ?) --- docs/CHANGELOG.txt | 5 ++++- misc/freetype/imgui_freetype.cpp | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dc018964..98df209a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -40,9 +40,12 @@ Breaking Changes: Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not very useful in practice, and the only use of it in the demo was illfit. - Other Changes: +- imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL + (which apparently happens with Freetype 2.11). (#4394, #4145?). + + ----------------------------------------------------------------------- VERSION 1.84.2 (Released 2021-08-23) ----------------------------------------------------------------------- diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 71a18870..a8ec51b3 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -6,6 +6,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL. // 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs. // 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format. // 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+). @@ -539,7 +540,8 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u // Render glyph into a bitmap (currently held by FreeType) const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info); - IM_ASSERT(ft_bitmap); + if (ft_bitmap == NULL) + continue; // Allocate new temporary chunk if needed const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4; From 0608887fb5783999b6682c290d15db8d59e1001c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Aug 2021 17:07:46 +0200 Subject: [PATCH 010/332] InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 98df209a..4797b91d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,6 +42,8 @@ Breaking Changes: Other Changes: +- InputTextMultiline: Fixed label size not being included into window contents rect unless + the whole widget is clipped. - imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 95aa3141..2a760ce9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3969,13 +3969,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImGuiItemStatusFlags item_status_flags = 0; if (is_multiline) { + ImVec2 backup_pos = window->DC.CursorPos; + ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable)) { - ItemSize(total_bb, style.FramePadding.y); EndGroup(); return false; } item_status_flags = g.LastItemData.StatusFlags; + window->DC.CursorPos = backup_pos; // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); From 4c22b3e5d9f2b5586c06a00eeb9c4b67712d64d3 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 24 Aug 2021 14:19:33 +0300 Subject: [PATCH 011/332] Backends: OpenGL3: Fix gl3wProcs colliding with gl3w. (#4445) --- backends/imgui_impl_opengl3_loader.h | 110 +++++++++++++-------------- docs/CHANGELOG.txt | 1 + 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index 021921ea..e70e7a32 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -473,61 +473,61 @@ union GL3WProcs { } gl; }; -GL3W_API extern union GL3WProcs gl3wProcs; +GL3W_API extern union GL3WProcs imgl3wProcs; /* OpenGL functions */ -#define glActiveTexture gl3wProcs.gl.ActiveTexture -#define glAttachShader gl3wProcs.gl.AttachShader -#define glBindBuffer gl3wProcs.gl.BindBuffer -#define glBindSampler gl3wProcs.gl.BindSampler -#define glBindTexture gl3wProcs.gl.BindTexture -#define glBindVertexArray gl3wProcs.gl.BindVertexArray -#define glBlendEquation gl3wProcs.gl.BlendEquation -#define glBlendEquationSeparate gl3wProcs.gl.BlendEquationSeparate -#define glBlendFuncSeparate gl3wProcs.gl.BlendFuncSeparate -#define glBufferData gl3wProcs.gl.BufferData -#define glClear gl3wProcs.gl.Clear -#define glClearColor gl3wProcs.gl.ClearColor -#define glCompileShader gl3wProcs.gl.CompileShader -#define glCreateProgram gl3wProcs.gl.CreateProgram -#define glCreateShader gl3wProcs.gl.CreateShader -#define glDeleteBuffers gl3wProcs.gl.DeleteBuffers -#define glDeleteProgram gl3wProcs.gl.DeleteProgram -#define glDeleteShader gl3wProcs.gl.DeleteShader -#define glDeleteTextures gl3wProcs.gl.DeleteTextures -#define glDeleteVertexArrays gl3wProcs.gl.DeleteVertexArrays -#define glDetachShader gl3wProcs.gl.DetachShader -#define glDisable gl3wProcs.gl.Disable -#define glDrawElements gl3wProcs.gl.DrawElements -#define glDrawElementsBaseVertex gl3wProcs.gl.DrawElementsBaseVertex -#define glEnable gl3wProcs.gl.Enable -#define glEnableVertexAttribArray gl3wProcs.gl.EnableVertexAttribArray -#define glGenBuffers gl3wProcs.gl.GenBuffers -#define glGenTextures gl3wProcs.gl.GenTextures -#define glGenVertexArrays gl3wProcs.gl.GenVertexArrays -#define glGetAttribLocation gl3wProcs.gl.GetAttribLocation -#define glGetIntegerv gl3wProcs.gl.GetIntegerv -#define glGetProgramInfoLog gl3wProcs.gl.GetProgramInfoLog -#define glGetProgramiv gl3wProcs.gl.GetProgramiv -#define glGetShaderInfoLog gl3wProcs.gl.GetShaderInfoLog -#define glGetShaderiv gl3wProcs.gl.GetShaderiv -#define glGetString gl3wProcs.gl.GetString -#define glGetStringi gl3wProcs.gl.GetStringi -#define glGetUniformLocation gl3wProcs.gl.GetUniformLocation -#define glIsEnabled gl3wProcs.gl.IsEnabled -#define glLinkProgram gl3wProcs.gl.LinkProgram -#define glPixelStorei gl3wProcs.gl.PixelStorei -#define glPolygonMode gl3wProcs.gl.PolygonMode -#define glReadPixels gl3wProcs.gl.ReadPixels -#define glScissor gl3wProcs.gl.Scissor -#define glShaderSource gl3wProcs.gl.ShaderSource -#define glTexImage2D gl3wProcs.gl.TexImage2D -#define glTexParameteri gl3wProcs.gl.TexParameteri -#define glUniform1i gl3wProcs.gl.Uniform1i -#define glUniformMatrix4fv gl3wProcs.gl.UniformMatrix4fv -#define glUseProgram gl3wProcs.gl.UseProgram -#define glVertexAttribPointer gl3wProcs.gl.VertexAttribPointer -#define glViewport gl3wProcs.gl.Viewport +#define glActiveTexture imgl3wProcs.gl.ActiveTexture +#define glAttachShader imgl3wProcs.gl.AttachShader +#define glBindBuffer imgl3wProcs.gl.BindBuffer +#define glBindSampler imgl3wProcs.gl.BindSampler +#define glBindTexture imgl3wProcs.gl.BindTexture +#define glBindVertexArray imgl3wProcs.gl.BindVertexArray +#define glBlendEquation imgl3wProcs.gl.BlendEquation +#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate +#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate +#define glBufferData imgl3wProcs.gl.BufferData +#define glClear imgl3wProcs.gl.Clear +#define glClearColor imgl3wProcs.gl.ClearColor +#define glCompileShader imgl3wProcs.gl.CompileShader +#define glCreateProgram imgl3wProcs.gl.CreateProgram +#define glCreateShader imgl3wProcs.gl.CreateShader +#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers +#define glDeleteProgram imgl3wProcs.gl.DeleteProgram +#define glDeleteShader imgl3wProcs.gl.DeleteShader +#define glDeleteTextures imgl3wProcs.gl.DeleteTextures +#define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays +#define glDetachShader imgl3wProcs.gl.DetachShader +#define glDisable imgl3wProcs.gl.Disable +#define glDrawElements imgl3wProcs.gl.DrawElements +#define glDrawElementsBaseVertex imgl3wProcs.gl.DrawElementsBaseVertex +#define glEnable imgl3wProcs.gl.Enable +#define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray +#define glGenBuffers imgl3wProcs.gl.GenBuffers +#define glGenTextures imgl3wProcs.gl.GenTextures +#define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays +#define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation +#define glGetIntegerv imgl3wProcs.gl.GetIntegerv +#define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog +#define glGetProgramiv imgl3wProcs.gl.GetProgramiv +#define glGetShaderInfoLog imgl3wProcs.gl.GetShaderInfoLog +#define glGetShaderiv imgl3wProcs.gl.GetShaderiv +#define glGetString imgl3wProcs.gl.GetString +#define glGetStringi imgl3wProcs.gl.GetStringi +#define glGetUniformLocation imgl3wProcs.gl.GetUniformLocation +#define glIsEnabled imgl3wProcs.gl.IsEnabled +#define glLinkProgram imgl3wProcs.gl.LinkProgram +#define glPixelStorei imgl3wProcs.gl.PixelStorei +#define glPolygonMode imgl3wProcs.gl.PolygonMode +#define glReadPixels imgl3wProcs.gl.ReadPixels +#define glScissor imgl3wProcs.gl.Scissor +#define glShaderSource imgl3wProcs.gl.ShaderSource +#define glTexImage2D imgl3wProcs.gl.TexImage2D +#define glTexParameteri imgl3wProcs.gl.TexParameteri +#define glUniform1i imgl3wProcs.gl.Uniform1i +#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv +#define glUseProgram imgl3wProcs.gl.UseProgram +#define glVertexAttribPointer imgl3wProcs.gl.VertexAttribPointer +#define glViewport imgl3wProcs.gl.Viewport #ifdef __cplusplus } @@ -715,13 +715,13 @@ static const char *proc_names[] = { "glViewport", }; -GL3W_API union GL3WProcs gl3wProcs; +GL3W_API union GL3WProcs imgl3wProcs; static void load_procs(GL3WGetProcAddressProc proc) { size_t i; for (i = 0; i < ARRAY_SIZE(proc_names); i++) - gl3wProcs.ptr[i] = proc(proc_names[i]); + imgl3wProcs.ptr[i] = proc(proc_names[i]); } #ifdef __cplusplus diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4797b91d..60c01ab4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,7 @@ Other Changes: the whole widget is clipped. - imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). +- Backends: OpenGL3: Fix our new GL loader conflicting with user using GL3W. (#4445) ----------------------------------------------------------------------- From 51d841dcf359668e3bc2ad9ffe9dc0da3c58cb27 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Aug 2021 14:30:46 +0200 Subject: [PATCH 012/332] Fix warnings and remove IM_RETURN (#4470) Amend f24abbc4 --- imgui_internal.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index f009702e..017e8de3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -82,19 +82,13 @@ Index of this file: #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #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' #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #endif -// Helper macros -#if defined(__clang__) -#define IM_NORETURN __attribute__((noreturn)) -#else -#define IM_NORETURN -#endif - // Legacy defines #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Renamed in 1.74 #error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS @@ -2428,8 +2422,8 @@ namespace ImGui // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)' // (New) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' // 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_Focusable flag to ItemAdd() - inline IM_NORETURN void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem + inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Focusable flag to ItemAdd() + inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem #endif // Logging/Capture From e6525273131dca258fe36347a584b078147b8077 Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Tue, 24 Aug 2021 13:48:04 +0100 Subject: [PATCH 013/332] Backends: WebGPU: Update impl_wgpu for an emscripten change --- backends/imgui_impl_wgpu.cpp | 7 ++++--- docs/CHANGELOG.txt | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 473154c5..a5c9eb55 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -6,13 +6,14 @@ // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-08-24: Fix for latest specs. // 2021-05-24: Add support for draw_data->FramebufferScale. // 2021-05-19: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-05-16: Update to latest WebGPU specs (compatible with Emscripten 2.0.20 and Chrome Canary 92). @@ -544,7 +545,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() ImGui_ImplWGPU_InvalidateDeviceObjects(); // Create render pipeline - WGPURenderPipelineDescriptor2 graphics_pipeline_desc = {}; + WGPURenderPipelineDescriptor graphics_pipeline_desc = {}; graphics_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined; graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW; @@ -610,7 +611,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() // Configure disabled depth-stencil state graphics_pipeline_desc.depthStencil = nullptr; - g_pipelineState = wgpuDeviceCreateRenderPipeline2(g_wgpuDevice, &graphics_pipeline_desc); + g_pipelineState = wgpuDeviceCreateRenderPipeline(g_wgpuDevice, &graphics_pipeline_desc); ImGui_ImplWGPU_CreateFontsTexture(); ImGui_ImplWGPU_CreateUniformBuffer(); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 60c01ab4..c9fc435a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,7 +46,8 @@ Other Changes: the whole widget is clipped. - imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). -- Backends: OpenGL3: Fix our new GL loader conflicting with user using GL3W. (#4445) +- Backends: OpenGL3: Fixed our new GL loader conflicting with user using GL3W. (#4445) +- Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] ----------------------------------------------------------------------- From 4a7c21d3302fadce991842e79eafa99fd8690e12 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Aug 2021 15:28:39 +0200 Subject: [PATCH 014/332] Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) --- docs/CHANGELOG.txt | 3 ++- imgui.h | 2 +- imgui_draw.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c9fc435a..32944d3e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,8 +44,9 @@ Other Changes: - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. -- imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL +- Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). +- Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) - Backends: OpenGL3: Fixed our new GL loader conflicting with user using GL3W. (#4445) - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] diff --git a/imgui.h b/imgui.h index 9a9df67f..acb051ff 100644 --- a/imgui.h +++ b/imgui.h @@ -61,7 +61,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18406 +#define IMGUI_VERSION_NUM 18407 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 29a6866a..0abc75f3 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1998,7 +1998,7 @@ void ImFontAtlas::ClearInputData() ConfigData.clear(); CustomRects.clear(); PackIdMouseCursors = PackIdLines = -1; - TexReady = false; + // Important: we leave TexReady untouched } void ImFontAtlas::ClearTexData() From f8bad7e1e31713e6d5c66fa71fa78b66a620adda Mon Sep 17 00:00:00 2001 From: Siarhei Fiedartsou Date: Tue, 24 Aug 2021 16:26:55 +0300 Subject: [PATCH 015/332] Backends: OpenGL3: Add TargetConditionals.h (#4473) + standardize include --- backends/imgui_impl_opengl3.cpp | 3 +++ backends/imgui_impl_opengl3.h | 2 +- backends/imgui_impl_sdl.cpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 5637a0fe..eb583a7f 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -105,6 +105,9 @@ #include #endif #elif defined(IMGUI_IMPL_OPENGL_ES3) +#if defined(__APPLE__) +#include +#endif #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) #include // Use GL ES 3 #else diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h index b1fb49c7..98c9aca1 100644 --- a/backends/imgui_impl_opengl3.h +++ b/backends/imgui_impl_opengl3.h @@ -42,7 +42,7 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); // Try to detect GLES on matching platforms #if defined(__APPLE__) -#include "TargetConditionals.h" +#include #endif #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index f5677646..669c7b77 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -57,7 +57,7 @@ #include #include #if defined(__APPLE__) -#include "TargetConditionals.h" +#include #endif #if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) From 2b0bd40b99fb2002f3e4210a9ebe99b746849f1b Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 24 Aug 2021 18:03:52 +0300 Subject: [PATCH 016/332] Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted. (#4464) Backends: Normalize clipping rect handling across backends. + Squashed amends. --- backends/imgui_impl_allegro5.cpp | 10 ++++-- backends/imgui_impl_dx10.cpp | 8 ++++- backends/imgui_impl_dx11.cpp | 8 ++++- backends/imgui_impl_dx12.cpp | 23 ++++++------ backends/imgui_impl_dx9.cpp | 9 ++++- backends/imgui_impl_metal.mm | 62 ++++++++++++++++---------------- backends/imgui_impl_opengl2.cpp | 26 ++++++-------- backends/imgui_impl_opengl3.cpp | 32 ++++++++--------- backends/imgui_impl_vulkan.cpp | 46 +++++++++++------------- backends/imgui_impl_wgpu.cpp | 15 ++++---- docs/CHANGELOG.txt | 3 ++ 11 files changed, 132 insertions(+), 110 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 6a7af437..2b784424 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -171,9 +171,15 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) } else { - // Draw + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply scissor/clipping rectangle, Draw ALLEGRO_BITMAP* texture = (ALLEGRO_BITMAP*)pcmd->GetTexID(); - al_set_clipping_rectangle(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y, pcmd->ClipRect.z - pcmd->ClipRect.x, pcmd->ClipRect.w - pcmd->ClipRect.y); + al_set_clipping_rectangle(clip_min.x, clip_min.y, clip_max.x, clip_max.y); al_draw_prim(&vertices[0], bd->VertexDecl, texture, idx_offset, idx_offset + pcmd->ElemCount, ALLEGRO_PRIM_TRIANGLE_LIST); } idx_offset += pcmd->ElemCount; diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 5fdf8327..8a3206ff 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -253,8 +253,14 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) } else { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + // Apply scissor/clipping rectangle - const D3D10_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y)}; + const D3D10_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; ctx->RSSetScissorRects(1, &r); // Bind texture, Draw diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index b24fbb1a..a56b757e 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -263,8 +263,14 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) } else { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + // Apply scissor/clipping rectangle - const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; + const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; ctx->RSSetScissorRects(1, &r); // Bind texture, Draw diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 38c4dab0..98e56235 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -259,16 +259,19 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL } else { - // Apply Scissor, Bind texture, Draw - const D3D12_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; - if (r.right > r.left && r.bottom > r.top) - { - D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {}; - texture_handle.ptr = (UINT64)pcmd->GetTexID(); - ctx->SetGraphicsRootDescriptorTable(1, texture_handle); - ctx->RSSetScissorRects(1, &r); - ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); - } + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply Scissor/clipping rectangle, Bind texture, Draw + const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; + D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {}; + texture_handle.ptr = (UINT64)pcmd->GetTexID(); + ctx->SetGraphicsRootDescriptorTable(1, texture_handle); + ctx->RSSetScissorRects(1, &r); + ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } } global_idx_offset += cmd_list->IdxBuffer.Size; diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 01e2bd60..259ce928 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -248,7 +248,14 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) } else { - const RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply Scissor/clipping rectangle, Bind texture, Draw + const RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID(); bd->pd3dDevice->SetTexture(0, texture); bd->pd3dDevice->SetScissorRect(&r); diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index a98249aa..18dcc914 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -5,13 +5,14 @@ // [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-08-24: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted. (#4464) // 2021-05-19: Metal: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: Metal: Change blending equation to preserve alpha in output buffer. // 2021-01-25: Metal: Fixed texture storage mode when building on Mac Catalyst. @@ -504,36 +505,37 @@ void ImGui_ImplMetal_DestroyDeviceObjects() else { // Project scissor/clipping rectangles into framebuffer space - ImVec4 clip_rect; - clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; - clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; - clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; - clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; - - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + + // Clamp to viewport as setScissorRect() won't accept values that are off bounds + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply scissor/clipping rectangle + MTLScissorRect scissorRect = { - // Apply scissor/clipping rectangle - MTLScissorRect scissorRect = - { - .x = NSUInteger(clip_rect.x), - .y = NSUInteger(clip_rect.y), - .width = NSUInteger(clip_rect.z - clip_rect.x), - .height = NSUInteger(clip_rect.w - clip_rect.y) - }; - [commandEncoder setScissorRect:scissorRect]; - - - // Bind texture, Draw - if (ImTextureID tex_id = pcmd->GetTexID()) - [commandEncoder setFragmentTexture:(__bridge id)(tex_id) atIndex:0]; - - [commandEncoder setVertexBufferOffset:(vertexBufferOffset + pcmd->VtxOffset * sizeof(ImDrawVert)) atIndex:0]; - [commandEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:pcmd->ElemCount - indexType:sizeof(ImDrawIdx) == 2 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32 - indexBuffer:indexBuffer.buffer - indexBufferOffset:indexBufferOffset + pcmd->IdxOffset * sizeof(ImDrawIdx)]; - } + .x = NSUInteger(clip_min.x), + .y = NSUInteger(clip_min.y), + .width = NSUInteger(clip_max.x - clip_min.x), + .height = NSUInteger(clip_max.y - clip_min.y) + }; + [commandEncoder setScissorRect:scissorRect]; + + // Bind texture, Draw + if (ImTextureID tex_id = pcmd->GetTexID()) + [commandEncoder setFragmentTexture:(__bridge id)(tex_id) atIndex:0]; + + [commandEncoder setVertexBufferOffset:(vertexBufferOffset + pcmd->VtxOffset * sizeof(ImDrawVert)) atIndex:0]; + [commandEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:pcmd->ElemCount + indexType:sizeof(ImDrawIdx) == 2 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32 + indexBuffer:indexBuffer.buffer + indexBufferOffset:indexBufferOffset + pcmd->IdxOffset * sizeof(ImDrawIdx)]; } } diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 0fa94e40..b4ab2a39 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -200,21 +200,17 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) else { // Project scissor/clipping rectangles into framebuffer space - ImVec4 clip_rect; - clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; - clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; - clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; - clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; - - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); - - // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); - } + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply scissor/clipping rectangle (Y is inverted in OpenGL) + glScissor((int)clip_min.x, (int)(fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); } idx_buffer += pcmd->ElemCount; } diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index eb583a7f..dc07ba50 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -442,26 +442,22 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) else { // Project scissor/clipping rectangles into framebuffer space - ImVec4 clip_rect; - clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; - clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; - clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; - clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; - - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); - - // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply scissor/clipping rectangle (Y is inverted in OpenGL) + glScissor((int)clip_min.x, (int)(fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET - if (bd->GlVersion >= 320) - glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); - else + if (bd->GlVersion >= 320) + glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); + else #endif - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); - } + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); } } } diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index a2e85b8d..3533263a 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -511,31 +511,27 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm else { // Project scissor/clipping rectangles into framebuffer space - ImVec4 clip_rect; - clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; - clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; - clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; - clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; - - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Negative offsets are illegal for vkCmdSetScissor - if (clip_rect.x < 0.0f) - clip_rect.x = 0.0f; - if (clip_rect.y < 0.0f) - clip_rect.y = 0.0f; - - // Apply scissor/clipping rectangle - VkRect2D scissor; - scissor.offset.x = (int32_t)(clip_rect.x); - scissor.offset.y = (int32_t)(clip_rect.y); - scissor.extent.width = (uint32_t)(clip_rect.z - clip_rect.x); - scissor.extent.height = (uint32_t)(clip_rect.w - clip_rect.y); - vkCmdSetScissor(command_buffer, 0, 1, &scissor); - - // Draw - vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); - } + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + + // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply scissor/clipping rectangle + VkRect2D scissor; + scissor.offset.x = (int32_t)(clip_min.x); + scissor.offset.y = (int32_t)(clip_min.y); + scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x); + scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y); + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + // Draw + vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } } global_idx_offset += cmd_list->IdxBuffer.Size; diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index a5c9eb55..d3bf28ba 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -443,13 +443,14 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, image_bind_group, 0, NULL); } - // Apply Scissor, Bind texture, Draw - uint32_t clip_rect[4]; - clip_rect[0] = (uint32_t)(clip_scale.x * (pcmd->ClipRect.x - clip_off.x)); - clip_rect[1] = (uint32_t)(clip_scale.y * (pcmd->ClipRect.y - clip_off.y)); - clip_rect[2] = (uint32_t)(clip_scale.x * (pcmd->ClipRect.z - clip_off.x)); - clip_rect[3] = (uint32_t)(clip_scale.y * (pcmd->ClipRect.w - clip_off.y)); - wgpuRenderPassEncoderSetScissorRect(pass_encoder, clip_rect[0], clip_rect[1], clip_rect[2] - clip_rect[0], clip_rect[3] - clip_rect[1]); + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + // Apply scissor/clipping rectangle, Draw + wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y)); wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 32944d3e..c35a1704 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,9 @@ Other Changes: - Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) - Backends: OpenGL3: Fixed our new GL loader conflicting with user using GL3W. (#4445) - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] +- Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via + a direct unclipped PushClipRect() call. (#4464) +- Backends: All renderers: Normalize clipping rect handling across backends. (#4464) ----------------------------------------------------------------------- From 780c1ee2650003c3e4d86e3655322912fecd0621 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Aug 2021 19:30:02 +0200 Subject: [PATCH 017/332] TextUnformatted: Accept null ranges including (NULL,NULL) without asserting. (#3615) --- docs/CHANGELOG.txt | 2 ++ imgui.h | 2 +- imgui_widgets.cpp | 8 ++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c35a1704..b21cf97e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,8 @@ Other Changes: - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. +- TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform + to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). - Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) diff --git a/imgui.h b/imgui.h index acb051ff..aea7a198 100644 --- a/imgui.h +++ b/imgui.h @@ -61,7 +61,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18407 +#define IMGUI_VERSION_NUM 18408 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2a760ce9..6bfb0fd4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -152,9 +152,13 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - ImGuiContext& g = *GImGui; - IM_ASSERT(text != NULL); + + // Accept null ranges + if (text == text_end) + text = text_end = ""; + + // Calculate length const char* text_begin = text; if (text_end == NULL) text_end = text + strlen(text); // FIXME-OPT From 80ed4eba87a5e624ff1d8e3b17620dbd032a3571 Mon Sep 17 00:00:00 2001 From: Cort <1944792+cdwfs@users.noreply.github.com> Date: Tue, 24 Aug 2021 12:53:54 -0700 Subject: [PATCH 018/332] Backends: Vulkan: non-dispatchable handles should compare to VK_NULL_HANDLE, not NULL (#4475) --- backends/imgui_impl_vulkan.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 3533263a..40dd0bcd 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -696,7 +696,7 @@ static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAlloca { // Create the shader modules ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - if (bd->ShaderModuleVert == NULL) + if (bd->ShaderModuleVert == VK_NULL_HANDLE) { VkShaderModuleCreateInfo vert_info = {}; vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; @@ -705,7 +705,7 @@ static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAlloca VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert); check_vk_result(err); } - if (bd->ShaderModuleFrag == NULL) + if (bd->ShaderModuleFrag == VK_NULL_HANDLE) { VkShaderModuleCreateInfo frag_info = {}; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; @@ -1227,7 +1227,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V { VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; - wd->Swapchain = NULL; + wd->Swapchain = VK_NULL_HANDLE; err = vkDeviceWaitIdle(device); check_vk_result(err); From 4c31c98d2293341061c0bfde4a3d7b40f509081d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 12:26:42 +0200 Subject: [PATCH 019/332] Nav: extracted code out of NavUpdate() into NavUpdateCancelRequest() --- imgui.cpp | 85 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d37315a9..ba7c9c57 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -909,6 +909,7 @@ static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); static void NavUpdateMoveResult(); static void NavUpdateInitResult(); +static void NavUpdateCancelRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavEndFrame(); @@ -9193,43 +9194,7 @@ static void ImGui::NavUpdate() io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) - { - IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); - if (g.ActiveId != 0) - { - if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) - ClearActiveID(); - } - else if (g.NavLayer != ImGuiNavLayer_Main) - { - // Leave the "menu" layer - NavRestoreLayer(ImGuiNavLayer_Main); - } - else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - ImRect child_rect = child_window->Rect(); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos)); - } - else if (g.OpenPopupStack.Size > 0) - { - // Close open popup/menu - if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = g.NavFocusScopeId = 0; - } - } + NavUpdateCancelRequest(); // Process manual activation request g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; @@ -9452,6 +9417,52 @@ static void ImGui::NavUpdateMoveResult() g.NavDisableMouseHover = g.NavMousePosDirty = true; } +// Process NavCancel input (to close a popup, get back to parent, clear focus) +// FIXME: In order to support e.g. Escape to clear a selection we'll need: +// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it. +// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept +static void ImGui::NavUpdateCancelRequest() +{ + ImGuiContext& g = *GImGui; + if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) + return; + + IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); + if (g.ActiveId != 0) + { + if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) + ClearActiveID(); + } + else if (g.NavLayer != ImGuiNavLayer_Main) + { + // Leave the "menu" layer + NavRestoreLayer(ImGuiNavLayer_Main); + } + else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + IM_ASSERT(child_window->ChildId != 0); + ImRect child_rect = child_window->Rect(); + FocusWindow(parent_window); + SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos)); + } + else if (g.OpenPopupStack.Size > 0) + { + // Close open popup/menu + if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); + } + else + { + // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were + if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.NavWindow->NavLastIds[0] = 0; + g.NavId = g.NavFocusScopeId = 0; + } +} + // Handle PageUp/PageDown/Home/End keys static float ImGui::NavUpdatePageUpPageDown() { From 84890a30743107d4b22e7796b5c038fa62294b87 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 12:31:11 +0200 Subject: [PATCH 020/332] Nav: simplify wrap requests code (may soon be useable for tabbing) --- imgui.cpp | 25 ++++++++++++------------- imgui_internal.h | 8 ++------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ba7c9c57..367fc857 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8437,7 +8437,7 @@ void ImGui::EndPopup() IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls IM_ASSERT(g.BeginPopupStack.Size > 0); - // Make all menus and popups wrap around for now, may need to expose that policy. + // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests) if (g.NavWindow == window) NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); @@ -8952,14 +8952,14 @@ void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const Im g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; } -void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) +// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire +// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. +void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags) { ImGuiContext& g = *GImGui; - - // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire - // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. - g.NavWrapRequestWindow = window; - g.NavWrapRequestFlags = move_flags; + IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY + if (g.NavWindow == window && g.NavMoveRequest && g.NavLayer == ImGuiNavLayer_Main) + g.NavMoveRequestFlags |= wrap_flags; } // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). @@ -9105,8 +9105,6 @@ static void ImGui::NavUpdate() ImGuiIO& io = g.IO; io.WantSetMousePos = false; - g.NavWrapRequestWindow = NULL; - g.NavWrapRequestFlags = ImGuiNavMoveFlags_None; #if 0 if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif @@ -9546,11 +9544,12 @@ static void ImGui::NavEndFrame() NavUpdateWindowingOverlay(); // Perform wrap-around in menus - ImGuiWindow* window = g.NavWrapRequestWindow; - ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags; - if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main) + // FIXME-NAV: Wrap support could be moved to the scoring function and than WrapX would function without an extra frame. This is essentially same as tabbing! + ImGuiWindow* window = g.NavWindow; + const ImGuiNavMoveFlags move_flags = g.NavMoveRequestFlags; + const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; + if (window && NavMoveRequestButNoResultYet() && (g.NavMoveRequestFlags & wanted_flags) && g.NavMoveRequestForward == ImGuiNavForward_None) { - IM_ASSERT(move_flags != 0); // No points calling this with no wrapping ImRect bb_rel = window->NavRectRel[0]; ImGuiDir clip_dir = g.NavMoveDir; diff --git a/imgui_internal.h b/imgui_internal.h index 017e8de3..a1f67be9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -938,9 +938,9 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side ImGuiNavMoveFlags_LoopY = 1 << 1, ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible. + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible (used by PageUp/PageDown) ImGuiNavMoveFlags_ScrollToEdge = 1 << 6 }; @@ -1534,8 +1534,6 @@ struct ImGuiContext ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) - ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around. - ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags. // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! @@ -1734,8 +1732,6 @@ struct ImGuiContext NavMoveRequestForward = ImGuiNavForward_None; NavMoveRequestKeyMods = ImGuiKeyModFlags_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; - NavWrapRequestWindow = NULL; - NavWrapRequestFlags = ImGuiNavMoveFlags_None; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; From b08ed6190782f1d59c4950c868e6f4af7235b2cd Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 27 Aug 2021 16:06:59 +0300 Subject: [PATCH 021/332] CI: Build with empty IM_ASSERT() macro. --- .github/workflows/build.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 09b09e71..f01bff87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -226,6 +226,18 @@ jobs: make -C examples/example_null clean CXXFLAGS="$CXXFLAGS -m64 -Werror" CXX=clang++ make -C examples/example_null WITH_EXTRA_WARNINGS=1 + - name: Build example_null (extra warnings, empty IM_ASSERT) + run: | + cat > example_single_file.cpp <<'EOF' + + #define IM_ASSERT(x) + #define IMGUI_IMPLEMENTATION + #include "misc/single_file/imgui_single_file.h" + #include "examples/example_null/main.cpp" + + EOF + g++ -I. -Wall -Wformat -Wextra -Werror -Wno-zero-as-null-pointer-constant -Wno-double-promotion -Wno-variadic-macros -Wno-empty-body -o example_single_file example_single_file.cpp + - name: Build example_null (freetype) run: | make -C examples/example_null clean From 20a1edef89f3dcbd9bf8e125a54e6f5a7f1cf9c6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 16:21:28 +0200 Subject: [PATCH 022/332] Nav: made EndMenuBar() use NavMoveRequestForward() for consistency. Moved forward clearing to NavMoveRequestApplyResult(). Improved/fixed comments. --- imgui.cpp | 51 +++++++++++++++++++++++------------------------ imgui_internal.h | 5 +++-- imgui_widgets.cpp | 5 ++--- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 367fc857..d94f28c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -907,7 +907,6 @@ namespace ImGui static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); -static void NavUpdateMoveResult(); static void NavUpdateInitResult(); static void NavUpdateCancelRequest(); static float NavUpdatePageUpPageDown(); @@ -8940,7 +8939,8 @@ void ImGui::NavMoveRequestCancel() NavUpdateAnyRequestFlag(); } -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) +// Forward will reuse the move request again on the next frame (generally with modifications done to it) +void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); @@ -8949,7 +8949,6 @@ void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const Im g.NavMoveClipDir = clip_dir; g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; g.NavMoveRequestFlags = move_flags; - g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; } // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire @@ -9151,16 +9150,7 @@ static void ImGui::NavUpdate() // Process navigation move request if (g.NavMoveRequest) - NavUpdateMoveResult(); - - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - { - IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - g.NavDisableHighlight = false; - g.NavMoveRequestForward = ImGuiNavForward_None; - } + NavMoveRequestApplyResult(); // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) @@ -9178,7 +9168,7 @@ static void ImGui::NavUpdate() g.NavJustTabbedId = 0; IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 + // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) NavSaveLastChildNavWindowIntoParent(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) @@ -9235,12 +9225,11 @@ static void ImGui::NavUpdate() } g.NavMoveClipDir = g.NavMoveDir; } - else + else if (g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued) { // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; } @@ -9251,7 +9240,7 @@ static void ImGui::NavUpdate() if (nav_keyboard_active) nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); - // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match + // If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match if (g.NavMoveDir != ImGuiDir_None) { g.NavMoveRequest = true; @@ -9350,10 +9339,14 @@ static void ImGui::NavUpdateInitResult() } } -// Apply result from previous frame navigation directional move request -static void ImGui::NavUpdateMoveResult() +// Apply result from previous frame navigation directional move request. Always called from NavUpdate() +void ImGui::NavMoveRequestApplyResult() { ImGuiContext& g = *GImGui; + + if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) + g.NavMoveRequestForward = ImGuiNavForward_None; + if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) { // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) @@ -9462,6 +9455,7 @@ static void ImGui::NavUpdateCancelRequest() } // Handle PageUp/PageDown/Home/End keys +// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; @@ -9544,14 +9538,14 @@ static void ImGui::NavEndFrame() NavUpdateWindowingOverlay(); // Perform wrap-around in menus - // FIXME-NAV: Wrap support could be moved to the scoring function and than WrapX would function without an extra frame. This is essentially same as tabbing! + // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. ImGuiWindow* window = g.NavWindow; const ImGuiNavMoveFlags move_flags = g.NavMoveRequestFlags; const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; if (window && NavMoveRequestButNoResultYet() && (g.NavMoveRequestFlags & wanted_flags) && g.NavMoveRequestForward == ImGuiNavForward_None) { - ImRect bb_rel = window->NavRectRel[0]; - + bool do_forward = false; + ImRect bb_rel = window->NavRectRel[g.NavLayer]; ImGuiDir clip_dir = g.NavMoveDir; if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { @@ -9562,7 +9556,7 @@ static void ImGui::NavEndFrame() bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + do_forward = true; } if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { @@ -9572,7 +9566,7 @@ static void ImGui::NavEndFrame() bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + do_forward = true; } if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) { @@ -9583,7 +9577,7 @@ static void ImGui::NavEndFrame() bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + do_forward = true; } if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) { @@ -9593,7 +9587,12 @@ static void ImGui::NavEndFrame() bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + do_forward = true; + } + if (do_forward) + { + window->NavRectRel[g.NavLayer] = bb_rel; + NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags); } } } diff --git a/imgui_internal.h b/imgui_internal.h index a1f67be9..3db068aa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2452,9 +2452,10 @@ namespace ImGui // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); - IMGUI_API bool NavMoveRequestButNoResultYet(); + IMGUI_API bool NavMoveRequestButNoResultYet(); // Should be called ~NavMoveRequestIsActiveButNoResultYet() + IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestCancel(); - IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags); + IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6bfb0fd4..5a809ccc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6693,15 +6693,14 @@ void ImGui::EndMenuBar() if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) const ImGuiNavLayer layer = ImGuiNavLayer_Menu; IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check FocusWindow(window); SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. g.NavDisableMouseHover = g.NavMousePosDirty = true; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - NavMoveRequestCancel(); + NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveRequestFlags); // Repeat } } From 4351febe9fd8d73140c5a82117e3645c3485f5e9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 18:01:01 +0200 Subject: [PATCH 023/332] Nav: moved enums/struct declarations in imgui_internal.h --- imgui_internal.h | 119 ++++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 3db068aa..942a1397 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -18,6 +18,7 @@ Index of this file: // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Navigation support // [SECTION] Columns support // [SECTION] Multi-select support // [SECTION] Docking support @@ -915,49 +916,6 @@ enum ImGuiInputReadMode ImGuiInputReadMode_RepeatFast }; -enum ImGuiNavHighlightFlags_ -{ - ImGuiNavHighlightFlags_None = 0, - ImGuiNavHighlightFlags_TypeDefault = 1 << 0, - ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. - ImGuiNavHighlightFlags_NoRounding = 1 << 3 -}; - -enum ImGuiNavDirSourceFlags_ -{ - ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_Keyboard = 1 << 0, - ImGuiNavDirSourceFlags_PadDPad = 1 << 1, - ImGuiNavDirSourceFlags_PadLStick = 1 << 2 -}; - -enum ImGuiNavMoveFlags_ -{ - ImGuiNavMoveFlags_None = 0, - ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side - ImGuiNavMoveFlags_LoopY = 1 << 1, - ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness - ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible (used by PageUp/PageDown) - ImGuiNavMoveFlags_ScrollToEdge = 1 << 6 -}; - -enum ImGuiNavForward -{ - ImGuiNavForward_None, - ImGuiNavForward_ForwardQueued, - ImGuiNavForward_ForwardActive -}; - -enum ImGuiNavLayer -{ - ImGuiNavLayer_Main = 0, // Main scrolling layer - ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) - ImGuiNavLayer_COUNT -}; - enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, @@ -1104,20 +1062,6 @@ struct ImGuiPopupData ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; } }; -struct ImGuiNavItemData -{ - ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) - ImGuiID ID; // Init,Move // Best candidate item ID - ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID - ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space - 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 - - ImGuiNavItemData() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; } -}; - enum ImGuiNextWindowDataFlags_ { ImGuiNextWindowDataFlags_None = 0, @@ -1207,6 +1151,67 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// [SECTION] Navigation support +//----------------------------------------------------------------------------- + +enum ImGuiNavHighlightFlags_ +{ + ImGuiNavHighlightFlags_None = 0, + ImGuiNavHighlightFlags_TypeDefault = 1 << 0, + ImGuiNavHighlightFlags_TypeThin = 1 << 1, + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. + ImGuiNavHighlightFlags_NoRounding = 1 << 3 +}; + +enum ImGuiNavDirSourceFlags_ +{ + ImGuiNavDirSourceFlags_None = 0, + ImGuiNavDirSourceFlags_Keyboard = 1 << 0, + ImGuiNavDirSourceFlags_PadDPad = 1 << 1, + ImGuiNavDirSourceFlags_PadLStick = 1 << 2 +}; + +enum ImGuiNavMoveFlags_ +{ + ImGuiNavMoveFlags_None = 0, + ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side + ImGuiNavMoveFlags_LoopY = 1 << 1, + ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness + ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible (used by PageUp/PageDown) + ImGuiNavMoveFlags_ScrollToEdge = 1 << 6 +}; + +enum ImGuiNavForward +{ + ImGuiNavForward_None, + ImGuiNavForward_ForwardQueued, + ImGuiNavForward_ForwardActive +}; + +enum ImGuiNavLayer +{ + ImGuiNavLayer_Main = 0, // Main scrolling layer + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) + ImGuiNavLayer_COUNT +}; + +struct ImGuiNavItemData +{ + ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) + ImGuiID ID; // Init,Move // Best candidate item ID + ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID + ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + 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 + + ImGuiNavItemData() { Clear(); } + void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; } +}; + //----------------------------------------------------------------------------- // [SECTION] Columns support //----------------------------------------------------------------------------- From ccfb20095e93953f9329f4fc60bba907dc65b16d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 20:48:01 +0200 Subject: [PATCH 024/332] Nav: small refactor of forwarding, clarified that MoveDir only set when RequestActive, removed one indent level in NavUpdatePageUpPageDown(). --- imgui.cpp | 134 +++++++++++++++++++++++----------------------- imgui_internal.h | 16 ++---- imgui_widgets.cpp | 10 ++-- 3 files changed, 77 insertions(+), 83 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d94f28c2..2b9684cb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8943,12 +8943,12 @@ void ImGui::NavMoveRequestCancel() void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); + IM_ASSERT(g.NavMoveRequestForwardToNextFrame == false); NavMoveRequestCancel(); + g.NavMoveRequestForwardToNextFrame = true; g.NavMoveDir = move_dir; g.NavMoveClipDir = clip_dir; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - g.NavMoveRequestFlags = move_flags; + g.NavMoveRequestFlags = move_flags | ImGuiNavMoveFlags_Forwarded; } // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire @@ -9151,6 +9151,7 @@ static void ImGui::NavUpdate() // Process navigation move request if (g.NavMoveRequest) NavMoveRequestApplyResult(); + g.NavMoveRequest = false; // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) @@ -9203,16 +9204,24 @@ static void ImGui::NavUpdate() g.NavDisableHighlight = true; if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - g.NavMoveRequest = false; // Process programmatic activation request if (g.NavNextActivateId != 0) g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; g.NavNextActivateId = 0; - // Initiate directional inputs request - if (g.NavMoveRequestForward == ImGuiNavForward_None) + if (g.NavMoveRequestForwardToNextFrame) + { + // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) + // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) + IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); + IM_ASSERT(g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded); + IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); + g.NavMoveRequestForwardToNextFrame = false; + } + else { + // Initiate directional inputs request g.NavMoveDir = ImGuiDir_None; g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -9225,14 +9234,6 @@ static void ImGui::NavUpdate() } g.NavMoveClipDir = g.NavMoveDir; } - else if (g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued) - { - // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) - // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) - IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); - g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; - } // Update PageUp/PageDown/Home/End scroll // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? @@ -9256,6 +9257,8 @@ static void ImGui::NavUpdate() g.NavDisableHighlight = false; } NavUpdateAnyRequestFlag(); + if (g.NavMoveDir != ImGuiDir_None) + IM_ASSERT(g.NavMoveRequest); // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) @@ -9344,9 +9347,6 @@ void ImGui::NavMoveRequestApplyResult() { ImGuiContext& g = *GImGui; - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - g.NavMoveRequestForward = ImGuiNavForward_None; - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) { // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) @@ -9471,60 +9471,60 @@ static float ImGui::NavUpdatePageUpPageDown() const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); - if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed + if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out + return 0.0f; + + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) { - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) + // Fallback manual-scroll when window has no navigable item + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) + SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) + SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); + else if (home_pressed) + SetScrollY(window, 0.0f); + else if (end_pressed) + SetScrollY(window, window->ScrollMax.y); + } + else + { + ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; + const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + float nav_scoring_rect_offset_y = 0.0f; + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) { - // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) - SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); - else if (home_pressed) - SetScrollY(window, 0.0f); - else if (end_pressed) - SetScrollY(window, window->ScrollMax.y); + 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.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } - else + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) { - ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); - float nav_scoring_rect_offset_y = 0.0f; - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - { - 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.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (IsKeyPressed(io.KeyMap[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.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (home_pressed) - { - // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y - // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. - // Preserve current horizontal position if we have any. - nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - else if (end_pressed) - { - nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - return nav_scoring_rect_offset_y; + 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.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + else if (home_pressed) + { + // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y + // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. + // Preserve current horizontal position if we have any. + nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Down; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; + } + else if (end_pressed) + { + nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Up; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; } + return nav_scoring_rect_offset_y; } return 0.0f; } @@ -9542,7 +9542,7 @@ static void ImGui::NavEndFrame() ImGuiWindow* window = g.NavWindow; const ImGuiNavMoveFlags move_flags = g.NavMoveRequestFlags; const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; - if (window && NavMoveRequestButNoResultYet() && (g.NavMoveRequestFlags & wanted_flags) && g.NavMoveRequestForward == ImGuiNavForward_None) + if (window && NavMoveRequestButNoResultYet() && (g.NavMoveRequestFlags & wanted_flags) && (g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded) == 0) { bool do_forward = false; ImRect bb_rel = window->NavRectRel[g.NavLayer]; diff --git a/imgui_internal.h b/imgui_internal.h index 942a1397..3c0ab631 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1181,14 +1181,8 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible (used by PageUp/PageDown) - ImGuiNavMoveFlags_ScrollToEdge = 1 << 6 -}; - -enum ImGuiNavForward -{ - ImGuiNavForward_None, - ImGuiNavForward_ForwardQueued, - ImGuiNavForward_ForwardActive + ImGuiNavMoveFlags_ScrollToEdge = 1 << 6, + ImGuiNavMoveFlags_Forwarded = 1 << 7 }; enum ImGuiNavLayer @@ -1525,14 +1519,14 @@ struct ImGuiContext 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) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. - bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest + bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() bool NavInitRequest; // Init request for appearing window to select first item bool NavInitRequestFromMove; ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) bool NavMoveRequest; // Move request for this frame + bool NavMoveRequestForwardToNextFrame; ImGuiNavMoveFlags NavMoveRequestFlags; - ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) ImGuiKeyModFlags NavMoveRequestKeyMods; ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? @@ -1733,8 +1727,8 @@ struct ImGuiContext NavInitRequestFromMove = false; NavInitResultId = 0; NavMoveRequest = false; + NavMoveRequestForwardToNextFrame = false; NavMoveRequestFlags = ImGuiNavMoveFlags_None; - NavMoveRequestForward = ImGuiNavForward_None; NavMoveRequestKeyMods = ImGuiKeyModFlags_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5a809ccc..e26540d1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5897,12 +5897,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; NavMoveRequestCancel(); } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? { toggled = true; NavMoveRequestCancel(); @@ -6690,7 +6690,7 @@ void ImGui::EndMenuBar() ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded) == 0) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) @@ -6910,7 +6910,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_close = menu_is_open; want_open = !menu_is_open; } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = true; NavMoveRequestCancel(); @@ -6928,7 +6928,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { want_open = true; } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open { want_open = true; NavMoveRequestCancel(); From bb6a60b1ff70ff18188a5b2b0576c87558f3f01d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 20:57:21 +0200 Subject: [PATCH 025/332] Nav: extracted sections of NavUpdate() into a NavUpdateCreateMoveRequest() function. Only clearing results when a request is activated. --- imgui.cpp | 157 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 71 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2b9684cb..67225bcf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -909,6 +909,7 @@ static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); static void NavUpdateInitResult(); static void NavUpdateCancelRequest(); +static void NavUpdateCreateMoveRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavEndFrame(); @@ -9110,8 +9111,8 @@ static void ImGui::NavUpdate() // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) // (do it before we map Keyboard input!) - bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - 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 nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad) { if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f @@ -9210,10 +9211,73 @@ static void ImGui::NavUpdate() g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; g.NavNextActivateId = 0; + // Process move requests + NavUpdateCreateMoveRequest(); + NavUpdateAnyRequestFlag(); + + // Scrolling + if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) + { + // *Fallback* manual-scroll with Nav directional keys when window has no navigable item + ImGuiWindow* window = g.NavWindow; + const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) //-V560 + { + if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) + SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) + SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + } + + // *Normal* Manual scroll with NavScrollXXX keys + // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. + ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f); + if (scroll_dir.x != 0.0f && window->ScrollbarX) + SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); + if (scroll_dir.y != 0.0f) + SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); + } + + // [DEBUG] + g.NavScoringCount = 0; +#if IMGUI_DEBUG_NAV_RECTS + if (g.NavWindow) + { + ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); + if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + } +#endif +} + +static void ImGui::NavUpdateInitResult() +{ + // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) + ImGuiContext& g = *GImGui; + if (!g.NavWindow) + return; + + // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) + // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); + SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); + if (g.NavInitRequestFromMove) + { + g.NavDisableHighlight = false; + g.NavDisableMouseHover = g.NavMousePosDirty = true; + } +} + +void ImGui::NavUpdateCreateMoveRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiWindow* window = g.NavWindow; + if (g.NavMoveRequestForwardToNextFrame) { // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) - // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) + // (preserve most state, which were already set by the NavMoveRequestForward() function) IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); IM_ASSERT(g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded); IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); @@ -9224,7 +9288,7 @@ static void ImGui::NavUpdate() // Initiate directional inputs request g.NavMoveDir = ImGuiDir_None; g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; - if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } @@ -9237,6 +9301,7 @@ static void ImGui::NavUpdate() // Update PageUp/PageDown/Home/End scroll // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; float nav_scoring_rect_offset_y = 0.0f; if (nav_keyboard_active) nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); @@ -9244,56 +9309,29 @@ static void ImGui::NavUpdate() // If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match if (g.NavMoveDir != ImGuiDir_None) { + IM_ASSERT(window != NULL); g.NavMoveRequest = true; g.NavMoveRequestKeyMods = io.KeyMods; g.NavMoveDirLast = g.NavMoveDir; + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisibleSet.Clear(); + g.NavMoveResultOther.Clear(); } + + // Moving with no reference triggers a init request if (g.NavMoveRequest && g.NavId == 0) { IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); g.NavInitRequest = g.NavInitRequestFromMove = true; - // Reassigning with same value, we're being explicit here. - g.NavInitResultId = 0; // -V1048 + g.NavInitResultId = 0; g.NavDisableHighlight = false; } - NavUpdateAnyRequestFlag(); - if (g.NavMoveDir != ImGuiDir_None) - IM_ASSERT(g.NavMoveRequest); - - // Scrolling - if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) - { - // *Fallback* manual-scroll with Nav directional keys when window has no navigable item - ImGuiWindow* window = g.NavWindow; - const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) - { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); - } - - // *Normal* Manual scroll with NavScrollXXX keys - // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. - ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f); - if (scroll_dir.x != 0.0f && window->ScrollbarX) - SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); - if (scroll_dir.y != 0.0f) - SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); - } - - // Reset search results - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisibleSet.Clear(); - g.NavMoveResultOther.Clear(); // When using gamepad, we project the reference nav bounding box into window visible area. // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative // (can't focus a visible object like we can with the mouse). - if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main) + if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) { - ImGuiWindow* window = g.NavWindow; ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { @@ -9306,39 +9344,16 @@ static void ImGui::NavUpdate() } // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted() ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0); - g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); - g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); - g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; - IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - g.NavScoringCount = 0; -#if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) - { - ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } - } -#endif -} - -static void ImGui::NavUpdateInitResult() -{ - // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) - ImGuiContext& g = *GImGui; - if (!g.NavWindow) - return; - - // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) - // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); - SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); - if (g.NavInitRequestFromMove) + g.NavScoringRect = ImRect(); + if (window) { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; + ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); + g.NavScoringRect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max); + g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); + g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); + g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; + IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + //GetForegroundDrawList()->AddRect(g.NavScoringRect.Min, g.NavScoringRect.Max, IM_COL32(255,200,0,255)); // [DEBUG] } } From 11103f82539b23867bb8f04dd8567c2c870f8756 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 21:32:36 +0200 Subject: [PATCH 026/332] Examples: SDL+OpenGL3: fixed build on ES2 target. (#4492) --- examples/example_sdl_opengl3/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index c9a059c7..a7e7fc01 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -8,9 +8,10 @@ #include "imgui_impl_opengl3.h" #include #include -#include #if defined(IMGUI_IMPL_OPENGL_ES2) #include +#else +#include #endif // Main code From 333807b483929a7dc40d80c889fe27e7d5d32327 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Aug 2021 13:08:41 +0200 Subject: [PATCH 027/332] Windows: fixed background order of overlapping childs submitted sequentially. (#4493) Amend 07704496 --- docs/CHANGELOG.txt | 1 + imgui.cpp | 15 ++++++++++----- imgui.h | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b21cf97e..01cd1ea9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,6 +42,7 @@ Breaking Changes: Other Changes: +- Windows: fixed background order of overlapping childs submitted sequentially. (#4493) - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform diff --git a/imgui.cpp b/imgui.cpp index 67225bcf..cd4640bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6214,16 +6214,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); } - // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. + // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. - // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. - // We also disabled this when we have dimming overlay behind this specific one child. - // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. + // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) { bool render_decorations_in_parent = false; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) + { + // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here) + // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs + ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL; + bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false; + bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0; + if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping) render_decorations_in_parent = true; + } if (render_decorations_in_parent) window->DrawList = parent_window->DrawList; diff --git a/imgui.h b/imgui.h index aea7a198..f262ecf2 100644 --- a/imgui.h +++ b/imgui.h @@ -61,7 +61,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18408 +#define IMGUI_VERSION_NUM 18409 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 66102880a3f31d4f5100eb3f0bbf497264df3bf4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Aug 2021 17:06:51 +0200 Subject: [PATCH 028/332] Updated links to Discussions --- .github/issue_template.md | 8 ++++---- .github/pull_request_template.md | 6 +++--- docs/FAQ.md | 2 ++ docs/README.md | 4 +++- imgui.cpp | 5 ++++- imgui.h | 5 ++++- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index 4fe6119c..fdd09439 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,12 +1,12 @@ (Click "Preview" above ^ to turn URL into clickable links) -1. PLEASE CAREFULLY READ: [FAQ](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md) +1. FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING or LOADING FONTS, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions). -2. PLEASE CAREFULLY READ: [Issue Submitting Guidelines](https://github.com/ocornut/imgui/issues/2261) +2. PLEASE CAREFULLY READ: [FAQ](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md) -3. FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING/LOADING FONTS, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions). +3. PLEASE CAREFULLY READ: [Issue Submitting Guidelines](https://github.com/ocornut/imgui/issues/2261) -4. PLEASE MAKE SURE that you have: read the FAQ; explored the contents of `ShowDemoWindow()` including the Examples menu; searched among Issues; used your IDE to search for keywords in all sources and text files; and read the link provided in (1) (2). +4. PLEASE MAKE SURE that you have: read the FAQ; explored the contents of `ShowDemoWindow()` including the Examples menu; searched among Issues; used your IDE to search for keywords in all sources and text files; and read the links above. 5. Be mindful that messages are being sent to the e-mail box of "Watching" users. Try to proof-read your messages before sending them. Edits are not seen by those users. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 533027c9..187d7105 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ (Click "Preview" to turn any http URL into a clickable link) -PLEASE CAREFULLY READ: -https://github.com/ocornut/imgui/issues/2261 +1. PLEASE CAREFULLY READ: [Issue Submitting Guidelines](https://github.com/ocornut/imgui/issues/2261) + +2. Clear this template before submitting your PR. -(Clear this template before submitting your PR) diff --git a/docs/FAQ.md b/docs/FAQ.md index 24909131..f0128890 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -94,6 +94,8 @@ Read [BACKENDS.md](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md Read `PROGRAMMER GUIDE` section of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp).
The [Wiki](https://github.com/ocornut/imgui/wiki) is a hub to many resources and links. +For first-time users having issues compiling/linking/running or issues loading fonts, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions). + ##### [Return to Index](#index) --- diff --git a/docs/README.md b/docs/README.md index 6f3253b1..25850973 100644 --- a/docs/README.md +++ b/docs/README.md @@ -162,7 +162,9 @@ See: [Wiki](https://github.com/ocornut/imgui/wiki) for many links, references, a See: [Articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) to read/learn about the Immediate Mode GUI paradigm. -For questions, bug reports, requests, feedback, you may post on [GitHub Issues](https://github.com/ocornut/imgui/issues) or [GitHub Discussions](https://github.com/ocornut/imgui/discussions). Please read and fill the New Issue template carefully. +Getting started? For first-time users having issues compiling/linking/running or issues loading fonts, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions). + +For other questions, bug reports, requests, feedback, you may post on [GitHub Issues](https://github.com/ocornut/imgui/issues). Please read and fill the New Issue template carefully. Private support is available for paying business customers (E-mail: _contact @ dearimgui dot com_). diff --git a/imgui.cpp b/imgui.cpp index cd4640bf..3da32323 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15,7 +15,10 @@ // - 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 -// - Discussions https://github.com/ocornut/imgui/discussions + +// 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. // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). diff --git a/imgui.h b/imgui.h index f262ecf2..64b5466d 100644 --- a/imgui.h +++ b/imgui.h @@ -15,7 +15,10 @@ // - 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 -// - Discussions https://github.com/ocornut/imgui/discussions + +// 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. /* From 4aea1c5adbd0d745367db56b8569a935da950271 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Aug 2021 14:55:26 +0200 Subject: [PATCH 029/332] IO: added io.WantCaptureMouseAllowPopupClose (#4480) + comments --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 56 +++++++++++++++++++++++++++++----------------- imgui.h | 6 +++-- imgui_demo.cpp | 1 + 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 01cd1ea9..06c49487 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,9 @@ Other Changes: the whole widget is clipped. - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) +- IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) + This allows apps to receive the click on void when that click is used to close popup (by default, + clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). - Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). - Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) diff --git a/imgui.cpp b/imgui.cpp index 3da32323..550a8339 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3872,6 +3872,7 @@ void ImGui::UpdateTabFocus() void ImGui::UpdateHoveredWindowAndCaptureFlags() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); // Find the window hovered by mouse: @@ -3887,48 +3888,61 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() clear_hovered_windows = true; // Disabled mouse? - if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) clear_hovered_windows = true; - // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. - int mouse_earliest_button_down = -1; + // We track click ownership. When clicked outside of a window the click is owned by the application and + // won't report hovering nor request capture even while dragging over our windows afterward. + const bool has_open_popup = (g.OpenPopupStack.Size > 0); + const bool has_open_modal = (modal_window != NULL); + int mouse_earliest_down = -1; bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { - if (g.IO.MouseClicked[i]) - g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0); - mouse_any_down |= g.IO.MouseDown[i]; - if (g.IO.MouseDown[i]) - if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) - mouse_earliest_button_down = i; + if (io.MouseClicked[i]) + { + io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup; + io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal; + } + mouse_any_down |= io.MouseDown[i]; + if (io.MouseDown[i]) + if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down]) + mouse_earliest_down = i; } - const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down]; + const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down]; // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; - if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) + if (!mouse_avail && !mouse_dragging_extern_payload) clear_hovered_windows = true; if (clear_hovered_windows) g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; - // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) + // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app) + // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag if (g.WantCaptureMouseNextFrame != -1) - g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); + { + io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0); + } else - g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0); + { + io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup; + io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal; + } - // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) + // 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) - g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); + io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else - g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) - g.IO.WantCaptureKeyboard = true; + io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); + if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) + io.WantCaptureKeyboard = true; // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible - g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; + io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() diff --git a/imgui.h b/imgui.h index 64b5466d..2db63b96 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18409 +#define IMGUI_VERSION_NUM 18410 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1918,6 +1918,7 @@ struct ImGuiIO // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ + bool WantCaptureMouseUnlessPopupClose;// Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() ImGuiKeyModFlags KeyModsPrev; // Previous key mods ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) @@ -1926,7 +1927,8 @@ struct ImGuiIO bool MouseClicked[5]; // Mouse button went from !Down to Down bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window. We don't request mouse capture from the application if click started outside ImGui bounds. + 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 MouseDownWasDoubleClick[5]; // Track if button down was a double-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 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 01837846..acc0d72e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5481,6 +5481,7 @@ static void ShowDemoWindowMisc() // Display ImGuiIO output flags ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); + ImGui::Text("WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("WantTextInput: %d", io.WantTextInput); ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos); From 7c4ffe490d65155b58b1df2ea83d4f06732fb2b2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Aug 2021 19:54:42 +0200 Subject: [PATCH 030/332] Menus: added internal's BeginMenuEx() matching MenuItemEx() with icon parameter. (amend f8fae022) --- imgui_internal.h | 1 + imgui_widgets.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 3c0ab631..c2f03a00 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2442,6 +2442,7 @@ namespace ImGui IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); // Menus + IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); // Combos diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e26540d1..1ec4ba6d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6789,7 +6789,7 @@ void ImGui::EndMainMenuBar() End(); } -bool ImGui::BeginMenu(const char* label, bool enabled) +bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6856,13 +6856,15 @@ bool ImGui::BeginMenu(const char* label, bool enabled) // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // 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 = 0.0f; // FIXME: This not currently exposed for BeginMenu() however you can call window->DC.MenuColumns.DeclColumns(w, 0, 0, 0) yourself + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float checkmark_w = IM_FLOOR(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); pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); RenderText(text_pos, label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); } if (!enabled) @@ -6967,6 +6969,11 @@ bool ImGui::BeginMenu(const char* label, bool enabled) return menu_is_open; } +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + return BeginMenuEx(label, NULL, enabled); +} + void ImGui::EndMenu() { // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). From 03ce17f0a507a1d1fecd36b3dc09b79ffdb2d0e8 Mon Sep 17 00:00:00 2001 From: Jackamilio Date: Mon, 30 Aug 2021 21:36:01 +0200 Subject: [PATCH 031/332] Backends: Allegro: Fixed clipping rectangles (#4498) broken by 2b0bd40b (#4464) --- backends/imgui_impl_allegro5.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 2b784424..93d84f1b 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -179,7 +179,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) // Apply scissor/clipping rectangle, Draw ALLEGRO_BITMAP* texture = (ALLEGRO_BITMAP*)pcmd->GetTexID(); - al_set_clipping_rectangle(clip_min.x, clip_min.y, clip_max.x, clip_max.y); + al_set_clipping_rectangle(clip_min.x, clip_min.y, clip_max.x - clip_min.x, clip_max.y - clip_min.y); al_draw_prim(&vertices[0], bd->VertexDecl, texture, idx_offset, idx_offset + pcmd->ElemCount, ALLEGRO_PRIM_TRIANGLE_LIST); } idx_offset += pcmd->ElemCount; From eb6c16d3dd6bc6b88477dc836f182938dd1cdcdc Mon Sep 17 00:00:00 2001 From: Filippo Crocchini Date: Thu, 22 Jul 2021 00:29:42 +0200 Subject: [PATCH 032/332] PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) + amended demo tweaks by ocornut --- docs/CHANGELOG.txt | 1 + imgui_demo.cpp | 8 ++++---- imgui_widgets.cpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 06c49487..b592b5e8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,7 @@ Other Changes: the whole widget is clipped. - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) +- PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) This allows apps to receive the click on void when that click is used to close popup (by default, clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index acc0d72e..2a16e3c6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1561,16 +1561,17 @@ static void ShowDemoWindowWidgets() } // Plot/Graph widgets are not very good. - // Consider writing your own, or using a third-party one, see: - // - ImPlot https://github.com/epezent/implot - // - others https://github.com/ocornut/imgui/wiki/Useful-Extensions + // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot + // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions) if (ImGui::TreeNode("Plots Widgets")) { static bool animate = true; ImGui::Checkbox("Animate", &animate); + // Plot as lines and plot as histogram static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); + ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); // Fill an array of contiguous float values to plot // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float @@ -1600,7 +1601,6 @@ static void ShowDemoWindowWidgets() sprintf(overlay, "avg %f", average); ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); } - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); // Use functions to generate output // FIXME: This is rather awkward because current plot API only pass in indices. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1ec4ba6d..23c678a6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6471,7 +6471,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get float v0 = values_getter(data, (0 + values_offset) % values_count); float t0 = 0.0f; ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands + float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); From 53589092b22b6fa5e92140f30621b6d40eea4eb2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Aug 2021 20:53:22 +0200 Subject: [PATCH 033/332] Docking: warning fix for when IM_ASSERT() is empty --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5e86bc1d..3728c37b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13356,7 +13356,8 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window erased = true; break; } - IM_ASSERT(erased); + if (!erased) + IM_ASSERT(erased); if (node->VisibleWindow == window) node->VisibleWindow = NULL; From 7b913db1ce9dd2fd98e5790aa59974dd4496be3b Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 31 Aug 2021 17:44:58 +0200 Subject: [PATCH 034/332] Nav: split NavMoveRequest into NavMoveSubmitted + NavMoveScoringItems to allow operation to defer a move request and provide result immediately + fix regular scoring needlesly running during init + some renaming. --- imgui.cpp | 181 +++++++++++++++++++++++++--------------------- imgui_internal.h | 41 ++++++----- imgui_widgets.cpp | 5 +- 3 files changed, 124 insertions(+), 103 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 550a8339..e50eca85 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2258,21 +2258,21 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items return; } - // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect + // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect ImRect unclipped_rect = window->ClipRect; - if (g.NavMoveRequest) + if (g.NavMoveScoringItems) unclipped_rect.Add(g.NavScoringRect); if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); + unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); // Could store and use NavJustMovedToRectRel const ImVec2 pos = window->DC.CursorPos; int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); int end = (int)((unclipped_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 - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) + if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up) start--; - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) + if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down) end++; start = ImClamp(start, 0, items_count); @@ -6534,7 +6534,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavFocusScopeId = 0; g.NavIdIsAlive = false; g.NavLayer = ImGuiNavLayer_Main; - g.NavInitRequest = g.NavMoveRequest = false; + g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } @@ -8755,7 +8755,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) if (g.NavLayer != window->DC.NavLayerCurrent) return false; - const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) g.NavScoringCount++; // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring @@ -8822,7 +8822,6 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) } else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); @@ -8835,7 +8834,8 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) // Is it in the quadrant we're interesting in moving to? bool new_best = false; - if (quadrant == g.NavMoveDir) + const ImGuiDir move_dir = g.NavMoveDir; + if (quadrant == move_dir) { // Does it beat the current best candidate? if (dist_box < result->DistBox) @@ -8857,7 +8857,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance new_best = true; } } @@ -8870,7 +8870,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) + if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f)) { result->DistAxial = dist_axial; new_best = true; @@ -8891,9 +8891,6 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* w static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) { ImGuiContext& g = *GImGui; - //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. - // return; - const ImGuiItemFlags item_flags = g.LastItemData.InFlags; const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); @@ -8915,27 +8912,29 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con } // Process Move Request (scoring for navigation) - // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) + if (g.NavMoveScoringItems) { - ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + { + ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + bool new_best = NavScoreItem(result, nav_bb); + #if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Score all items in NavWindow at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; -#else - bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); + // [DEBUG] Scoring all items in NavWindow at all times + if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) + new_best = false; #endif - if (new_best) - NavApplyItemToResult(result, window, id, nav_bb_rel); + if (new_best) + NavApplyItemToResult(result, window, id, nav_bb_rel); - // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveRequestFlags & 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.NavMoveResultLocalVisibleSet, nav_bb)) - NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel); + // 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, nav_bb)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb_rel); + } } // Update window-relative bounding box of navigated item @@ -8952,13 +8951,13 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con bool ImGui::NavMoveRequestButNoResultYet() { ImGuiContext& g = *GImGui; - return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; + return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; } void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; - g.NavMoveRequest = false; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } @@ -8966,12 +8965,12 @@ void ImGui::NavMoveRequestCancel() void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveRequestForwardToNextFrame == false); + IM_ASSERT(g.NavMoveForwardToNextFrame == false); NavMoveRequestCancel(); - g.NavMoveRequestForwardToNextFrame = true; + g.NavMoveForwardToNextFrame = true; g.NavMoveDir = move_dir; g.NavMoveClipDir = clip_dir; - g.NavMoveRequestFlags = move_flags | ImGuiNavMoveFlags_Forwarded; + g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded; } // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire @@ -8980,8 +8979,9 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wra { ImGuiContext& g = *GImGui; IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY - if (g.NavWindow == window && g.NavMoveRequest && g.NavLayer == ImGuiNavLayer_Main) - g.NavMoveRequestFlags |= wrap_flags; + // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test + if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main) + g.NavMoveFlags |= wrap_flags; } // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). @@ -9026,7 +9026,7 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) static inline void ImGui::NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); + g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); if (g.NavAnyRequest) IM_ASSERT(g.NavWindow != NULL); } @@ -9128,7 +9128,7 @@ static void ImGui::NavUpdate() io.WantSetMousePos = false; #if 0 - if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); + if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) @@ -9172,9 +9172,9 @@ static void ImGui::NavUpdate() g.NavJustMovedToId = 0; // Process navigation move request - if (g.NavMoveRequest) + if (g.NavMoveSubmitted) NavMoveRequestApplyResult(); - g.NavMoveRequest = false; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) @@ -9243,12 +9243,13 @@ static void ImGui::NavUpdate() // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) //-V560 + const ImGuiDir move_dir = g.NavMoveDir; + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None) { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + 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)); + 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)); } // *Normal* Manual scroll with NavScrollXXX keys @@ -9296,20 +9297,20 @@ void ImGui::NavUpdateCreateMoveRequest() ImGuiIO& io = g.IO; ImGuiWindow* window = g.NavWindow; - if (g.NavMoveRequestForwardToNextFrame) + if (g.NavMoveForwardToNextFrame && window != NULL) { // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) // (preserve most state, which were already set by the NavMoveRequestForward() function) IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded); + IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded); IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); - g.NavMoveRequestForwardToNextFrame = false; + g.NavMoveForwardToNextFrame = false; } else { // Initiate directional inputs request g.NavMoveDir = ImGuiDir_None; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; + g.NavMoveFlags = ImGuiNavMoveFlags_None; if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; @@ -9324,24 +9325,36 @@ void ImGui::NavUpdateCreateMoveRequest() // Update PageUp/PageDown/Home/End scroll // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - float nav_scoring_rect_offset_y = 0.0f; - if (nav_keyboard_active) - nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); + float scoring_rect_offset_y = 0.0f; + if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) + scoring_rect_offset_y = NavUpdatePageUpPageDown(); + + // [DEBUG] Always send a request +#if IMGUI_DEBUG_NAV_SCORING + if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); + if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None) + { + g.NavMoveDir = g.NavMoveDirForDebug; + g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult; + } +#endif // If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match + // FIXME: Would be nice to call a single function to initiate a new request if (g.NavMoveDir != ImGuiDir_None) { IM_ASSERT(window != NULL); - g.NavMoveRequest = true; - g.NavMoveRequestKeyMods = io.KeyMods; - g.NavMoveDirLast = g.NavMoveDir; + g.NavMoveSubmitted = g.NavMoveScoringItems = true; + g.NavMoveKeyMods = io.KeyMods; + g.NavMoveDirForDebug = g.NavMoveDir; g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisibleSet.Clear(); + g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultOther.Clear(); } // Moving with no reference triggers a init request - if (g.NavMoveRequest && g.NavId == 0) + if (g.NavMoveSubmitted && g.NavId == 0) { IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); g.NavInitRequest = g.NavInitRequestFromMove = true; @@ -9352,7 +9365,7 @@ void ImGui::NavUpdateCreateMoveRequest() // When using gamepad, we project the reference nav bounding box into window visible area. // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative // (can't focus a visible object like we can with the mouse). - if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) + if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) { ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) @@ -9366,17 +9379,18 @@ void ImGui::NavUpdateCreateMoveRequest() } // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - g.NavScoringRect = ImRect(); - if (window) + ImRect scoring_rect; + if (window != NULL) { ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - g.NavScoringRect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max); - g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); - g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); - g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; - IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //GetForegroundDrawList()->AddRect(g.NavScoringRect.Min, g.NavScoringRect.Max, IM_COL32(255,200,0,255)); // [DEBUG] + scoring_rect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max); + scoring_rect.TranslateY(scoring_rect_offset_y); + scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); + scoring_rect.Max.x = scoring_rect.Min.x; + IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] } + g.NavScoringRect = scoring_rect; } // Apply result from previous frame navigation directional move request. Always called from NavUpdate() @@ -9399,9 +9413,9 @@ void ImGui::NavMoveRequestApplyResult() ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) - if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) - result = &g.NavMoveResultLocalVisibleSet; + if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId) + result = &g.NavMoveResultLocalVisible; // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) @@ -9413,7 +9427,7 @@ void ImGui::NavMoveRequestApplyResult() if (g.NavLayer == ImGuiNavLayer_Main) { ImVec2 delta_scroll; - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge) + if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdge) { float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; delta_scroll.y = result->Window->Scroll.y - scroll_target; @@ -9437,7 +9451,7 @@ void ImGui::NavMoveRequestApplyResult() // 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.NavMoveRequestKeyMods; + g.NavJustMovedToKeyMods = g.NavMoveKeyMods; } IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result 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); @@ -9492,18 +9506,17 @@ static void ImGui::NavUpdateCancelRequest() } // Handle PageUp/PageDown/Home/End keys +// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request // FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; - if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) - return 0.0f; - if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) + ImGuiWindow* window = g.NavWindow; + if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) return 0.0f; - ImGuiWindow* window = g.NavWindow; const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); @@ -9533,14 +9546,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.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } else if (IsKeyPressed(io.KeyMap[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.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } else if (home_pressed) { @@ -9551,7 +9564,8 @@ static float ImGui::NavUpdatePageUpPageDown() if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; + // FIXME-NAV: MoveClipDir left to _None, intentional? } else if (end_pressed) { @@ -9559,7 +9573,8 @@ static float ImGui::NavUpdatePageUpPageDown() if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; + // FIXME-NAV: MoveClipDir left to _None, intentional? } return nav_scoring_rect_offset_y; } @@ -9577,9 +9592,9 @@ static void ImGui::NavEndFrame() // Perform wrap-around in menus // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. ImGuiWindow* window = g.NavWindow; - const ImGuiNavMoveFlags move_flags = g.NavMoveRequestFlags; + const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; - if (window && NavMoveRequestButNoResultYet() && (g.NavMoveRequestFlags & wanted_flags) && (g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded) == 0) + if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) { bool do_forward = false; ImRect bb_rel = window->NavRectRel[g.NavLayer]; diff --git a/imgui_internal.h b/imgui_internal.h index c2f03a00..7300623c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1180,9 +1180,10 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible (used by PageUp/PageDown) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) ImGuiNavMoveFlags_ScrollToEdge = 1 << 6, - ImGuiNavMoveFlags_Forwarded = 1 << 7 + ImGuiNavMoveFlags_Forwarded = 1 << 7, + ImGuiNavMoveFlags_DebugNoResult = 1 << 8 }; enum ImGuiNavLayer @@ -1511,27 +1512,31 @@ struct ImGuiContext ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. - ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. - int NavScoringCount; // Metrics for debugging 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. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing 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) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. + + // Navigation: Init & Move Requests bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() bool NavInitRequest; // Init request for appearing window to select first item bool NavInitRequestFromMove; ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) - bool NavMoveRequest; // Move request for this frame - bool NavMoveRequestForwardToNextFrame; - ImGuiNavMoveFlags NavMoveRequestFlags; - ImGuiKeyModFlags NavMoveRequestKeyMods; - ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request + bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() + bool NavMoveScoringItems; // Move request submitted, still scoring incoming items + bool NavMoveForwardToNextFrame; + ImGuiNavMoveFlags NavMoveFlags; + ImGuiKeyModFlags NavMoveKeyMods; + ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) + ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? + ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. + int NavScoringCount; // Metrics for debugging ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavItemData NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) @@ -1714,8 +1719,6 @@ struct ImGuiContext NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavInputSource = ImGuiInputSource_None; - NavScoringRect = ImRect(); - NavScoringCount = 0; NavLayer = ImGuiNavLayer_Main; NavIdTabCounter = INT_MAX; NavIdIsAlive = false; @@ -1726,11 +1729,13 @@ struct ImGuiContext NavInitRequest = false; NavInitRequestFromMove = false; NavInitResultId = 0; - NavMoveRequest = false; - NavMoveRequestForwardToNextFrame = false; - NavMoveRequestFlags = ImGuiNavMoveFlags_None; - NavMoveRequestKeyMods = ImGuiKeyModFlags_None; - NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; + NavMoveSubmitted = false; + NavMoveScoringItems = false; + NavMoveForwardToNextFrame = false; + NavMoveFlags = ImGuiNavMoveFlags_None; + NavMoveKeyMods = ImGuiKeyModFlags_None; + NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; + NavScoringCount = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; @@ -2452,7 +2457,7 @@ namespace ImGui // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); - IMGUI_API bool NavMoveRequestButNoResultYet(); // Should be called ~NavMoveRequestIsActiveButNoResultYet() + IMGUI_API bool NavMoveRequestButNoResultYet(); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 23c678a6..c933344b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6687,10 +6687,11 @@ void ImGui::EndMenuBar() // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { + // Try to find out if the request is for one of our child menu ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveRequestFlags & ImGuiNavMoveFlags_Forwarded) == 0) + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) @@ -6700,7 +6701,7 @@ void ImGui::EndMenuBar() SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. g.NavDisableMouseHover = g.NavMousePosDirty = true; - NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveRequestFlags); // Repeat + NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags); // Repeat } } From ee351d3548d5c97d03862ad70c6155e35f95aa6c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 31 Aug 2021 18:50:56 +0200 Subject: [PATCH 035/332] Nav: move code into NavMoveRequestSubmit(). NavApplyItemToResult() takes absolute rect., comments --- docs/TODO.txt | 3 ++- imgui.cpp | 58 ++++++++++++++++++++++++++++-------------------- imgui_internal.h | 7 +++--- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 71f995f6..dff2d24d 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -334,9 +334,10 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: patterns to make it possible for arrows key to update selection (see JustMovedTo in range_select branch) - nav: restore/find nearest NavId when current one disappear (e.g. pressed a button that disappear, or perhaps auto restoring when current button change name) - nav: SetItemDefaultFocus() level of priority, so widget like Selectable when inside a popup could claim a low-priority default focus on the first selected iem + - nav: NavFlattened: init requests don't work properly on flattened siblings. + - nav: NavFlattened: pageup/pagedown/home/end don't work properly on flattened siblings. - nav: NavFlattened: ESC on a flattened child should select something. - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child are currently not considered to enter into a NavFlattened child. - - nav: NavFlattened: init request doesn't select items that are part of a NavFlattened child - nav: NavFlattened: cannot access menu-bar of a flattened child window with Alt/menu key (not a very common use case..). - nav: simulate right-click or context activation? (SHIFT+F10) - nav/tabbing: refactor old tabbing system and turn into navigation, should pass through all widgets (in submission order?). diff --git a/imgui.cpp b/imgui.cpp index e50eca85..196aa7b3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -917,8 +917,8 @@ static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavEndFrame(); static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand); -static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); +static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb); +static void NavProcessItem(ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); @@ -7495,6 +7495,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu 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.InFlags = g.CurrentItemFlags; @@ -7516,7 +7517,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); + NavProcessItem(window, id, nav_bb_arg ? *nav_bb_arg : bb); // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX @@ -8879,20 +8880,22 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) return new_best; } -static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel) +static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb) { + ImGuiContext& g = *GImGui; + IM_ASSERT(g.LastItemData.ID == id); // Otherwise pulling from window->DC wouldn't be right result->Window = window; result->ID = id; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->RectRel = nav_bb_rel; + result->RectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +// This is called after LastItemData is set. +static void ImGui::NavProcessItem(ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb) { ImGuiContext& g = *GImGui; const ImGuiItemFlags item_flags = g.LastItemData.InFlags; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) @@ -8902,7 +8905,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con if (candidate_for_nav_default_focus || g.NavInitResultId == 0) { g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; + g.NavInitResultRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); } if (candidate_for_nav_default_focus) { @@ -8926,14 +8929,14 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con new_best = false; #endif if (new_best) - NavApplyItemToResult(result, window, id, nav_bb_rel); + NavApplyItemToResult(result, window, id, nav_bb); // 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, nav_bb)) - NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb_rel); + NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb); } } @@ -8944,7 +8947,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) + window->NavRectRel[window->DC.NavLayerCurrent] = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); // Store item bounding box (relative to window position) } } @@ -8954,6 +8957,21 @@ bool ImGui::NavMoveRequestButNoResultYet() return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; } +void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindow != NULL); + g.NavMoveSubmitted = g.NavMoveScoringItems = true; + g.NavMoveDir = move_dir; + g.NavMoveClipDir = clip_dir; + g.NavMoveFlags = move_flags; + g.NavMoveForwardToNextFrame = false; + g.NavMoveKeyMods = g.IO.KeyMods; + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisible.Clear(); + g.NavMoveResultOther.Clear(); +} + void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; @@ -9304,7 +9322,6 @@ void ImGui::NavUpdateCreateMoveRequest() IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded); IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); - g.NavMoveForwardToNextFrame = false; } else { @@ -9340,20 +9357,12 @@ void ImGui::NavUpdateCreateMoveRequest() } #endif - // If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match - // FIXME: Would be nice to call a single function to initiate a new request + // Submit + g.NavMoveForwardToNextFrame = false; if (g.NavMoveDir != ImGuiDir_None) - { - IM_ASSERT(window != NULL); - g.NavMoveSubmitted = g.NavMoveScoringItems = true; - g.NavMoveKeyMods = io.KeyMods; - g.NavMoveDirForDebug = g.NavMoveDir; - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisible.Clear(); - g.NavMoveResultOther.Clear(); - } + NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags); - // Moving with no reference triggers a init request + // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) if (g.NavMoveSubmitted && g.NavId == 0) { IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); @@ -9507,6 +9516,7 @@ static void ImGui::NavUpdateCancelRequest() // Handle PageUp/PageDown/Home/End keys // Called from NavUpdateCreateMoveRequest() which will use our output to create a move request +// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference // FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? static float ImGui::NavUpdatePageUpPageDown() { diff --git a/imgui_internal.h b/imgui_internal.h index 7300623c..94522e2c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -758,7 +758,7 @@ enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // window->DC.LastItemDisplayRect is valid + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. @@ -1123,8 +1123,8 @@ struct ImGuiLastItemData ImGuiID ID; ImGuiItemFlags InFlags; // See ImGuiItemFlags_ ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ - ImRect Rect; - ImRect DisplayRect; + ImRect Rect; // Full rectangle + ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set) ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } }; @@ -2458,6 +2458,7 @@ namespace ImGui // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); IMGUI_API bool NavMoveRequestButNoResultYet(); + IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); From a735a8c0844c9278a1d97a12774c279c19f7ad62 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Sep 2021 12:07:17 +0200 Subject: [PATCH 036/332] Nav: storing NavRect in LastItemData. Fix race condition where framed items (Rect != NavRect) calling SetFocusID() would store the wrong NavRectRel until next frame (haven't dugged in the possible side-effects of that race condition) --- imgui.cpp | 5 +++-- imgui_internal.h | 1 + imgui_widgets.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 196aa7b3..19fe3079 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7498,6 +7498,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // (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.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; @@ -7517,7 +7518,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(window, id, nav_bb_arg ? *nav_bb_arg : bb); + NavProcessItem(window, id, g.LastItemData.NavRect); // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX @@ -8710,7 +8711,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) - window->NavRectRel[nav_layer] = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos); + window->NavRectRel[nav_layer] = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; diff --git a/imgui_internal.h b/imgui_internal.h index 94522e2c..ac58b6c7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1124,6 +1124,7 @@ struct ImGuiLastItemData ImGuiItemFlags InFlags; // See ImGuiItemFlags_ 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) ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c933344b..10ea9cfb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6206,7 +6206,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); // (bb == NavRect) g.NavDisableHighlight = true; } } From 73a5e82e04666640ab072e7177b656fda6650ad7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Sep 2021 12:39:12 +0200 Subject: [PATCH 037/332] Nav: removed parameters from NavScoreItem(), NavProcessItem(), NavApplyItemToResult(): made little sense / misleading because pulling from other current state anyway. Will readd in a more thorough manner is actually needed. --- imgui.cpp | 41 +++++++++++++++++++++++------------------ imgui_internal.h | 2 +- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 19fe3079..e92df254 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -916,9 +916,9 @@ static void NavUpdateCreateMoveRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavEndFrame(); -static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand); -static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb); -static void NavProcessItem(ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb); +static bool NavScoreItem(ImGuiNavItemData* result); +static void NavApplyItemToResult(ImGuiNavItemData* result); +static void NavProcessItem(); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); @@ -7518,7 +7518,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(window, id, g.LastItemData.NavRect); + NavProcessItem(); // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX @@ -8742,7 +8742,7 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); } - else + else // FIXME: PageUp/PageDown are leaving move_dir == None { r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); @@ -8750,14 +8750,16 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect } // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) +static bool ImGui::NavScoreItem(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavLayer != window->DC.NavLayerCurrent) return false; - const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + // FIXME: Those are not good variables names + ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle + const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) g.NavScoringCount++; // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring @@ -8820,7 +8822,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); + draw_list->AddText(cand.Max, ~0U, buf); } else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. { @@ -8829,7 +8831,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf); } } #endif @@ -8881,21 +8883,24 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) return new_best; } -static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb) +static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.LastItemData.ID == id); // Otherwise pulling from window->DC wouldn't be right + ImGuiWindow* window = g.CurrentWindow; result->Window = window; - result->ID = id; + result->ID = g.LastItemData.ID; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->RectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); } // 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. -static void ImGui::NavProcessItem(ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb) +static void ImGui::NavProcessItem() { ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = g.LastItemData.ID; + const ImRect nav_bb = g.LastItemData.NavRect; const ImGuiItemFlags item_flags = g.LastItemData.InFlags; // Process Init Request @@ -8922,7 +8927,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, ImGuiID id, const ImRect& if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) { ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - bool new_best = NavScoreItem(result, nav_bb); + bool new_best = NavScoreItem(result); #if IMGUI_DEBUG_NAV_SCORING // [DEBUG] Scoring all items in NavWindow at all times @@ -8930,14 +8935,14 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, ImGuiID id, const ImRect& new_best = false; #endif if (new_best) - NavApplyItemToResult(result, window, id, nav_bb); + 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, nav_bb)) - NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb); + if (NavScoreItem(&g.NavMoveResultLocalVisible)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible); } } diff --git a/imgui_internal.h b/imgui_internal.h index ac58b6c7..2ff806e5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1851,7 +1851,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() - int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign) + int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via ImGuiItemAddFlags_Focusable (FIXME-NAV: Needs redesign) int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // Local parameters stacks From 3d9d3b49aec18b9b86093912db890799d280cf45 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Sep 2021 14:41:24 +0200 Subject: [PATCH 038/332] Internals: folded ImGuiItemAddFlags into ImGuiItemFlags. ImGuiItemAddFlags_Focusable > ImGuiItemFlags_Inputable. One step in the big nav/tab/focus rework. Bonus simplified Selectable() handling of its custom disabled flag. (#211) --- imgui.cpp | 16 +++++++++------- imgui.h | 2 +- imgui_internal.h | 24 +++++++++--------------- imgui_widgets.cpp | 28 ++++++++-------------------- 4 files changed, 27 insertions(+), 43 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e92df254..4ce8cf04 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3297,7 +3297,8 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged // Called by ItemAdd() // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. -void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id) +// [WIP] This will eventually be refactored and moved into NavProcessItem() +void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0 && id == g.LastItemData.ID); @@ -7489,7 +7490,7 @@ void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) // 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, ImGuiItemAddFlags flags) +bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7499,7 +7500,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu g.LastItemData.ID = id; g.LastItemData.Rect = bb; g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; - g.LastItemData.InFlags = g.CurrentItemFlags; + g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags; g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; // Directional navigation processing @@ -7542,10 +7543,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] - // Tab stop handling (previously was using internal ItemFocusable() api) - // FIXME-NAV: We would now want to move this above the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343) - if (flags & ImGuiItemAddFlags_Focusable) - ItemFocusable(window, id); + // [WIP] Tab stop handling (previously was using internal FocusableItemRegister() api) + // FIXME-NAV: We would now want to move this before the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343) + if (extra_flags & ImGuiItemFlags_Inputable) + ItemInputable(window, id); // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (IsMouseHoveringRect(bb.Min, bb.Max)) @@ -8963,6 +8964,7 @@ bool ImGui::NavMoveRequestButNoResultYet() return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; } +// FIXME: ScoringRect is not set void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 2db63b96..78ac06fc 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18410 +#define IMGUI_VERSION_NUM 18411 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 2ff806e5..99cddcf3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -144,7 +144,6 @@ struct ImGuiWindowSettings; // Storage for a window .ini settings (we ke // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() -typedef int ImGuiItemAddFlags; // -> enum ImGuiItemAddFlags_ // Flags: for ItemAdd() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -742,15 +741,8 @@ enum ImGuiItemFlags_ ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items) ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window 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. -}; - -// Flags for ItemAdd() -// FIXME-NAV: _Focusable is _ALMOST_ what you would expect to be called '_TabStop' but because SetKeyboardFocusHere() works on items with no TabStop we distinguish Focusable from TabStop. -enum ImGuiItemAddFlags_ -{ - ImGuiItemAddFlags_None = 0, - ImGuiItemAddFlags_Focusable = 1 << 0 // FIXME-NAV: In current/legacy scheme, Focusable+TabStop support are opt-in by widgets. We will transition it toward being opt-out, so this flag is expected to eventually disappear. + ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. + ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate item when focused. Currently only used and supported by a few items before it becomes a generic feature. }; // Storage for LastItem data @@ -1851,7 +1843,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() - int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via ImGuiItemAddFlags_Focusable (FIXME-NAV: Needs redesign) + int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase when ImGuiItemFlags_Inputable (FIXME-NAV: Needs redesign) int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // Local parameters stacks @@ -2403,9 +2395,9 @@ namespace ImGui // Basic Helpers for widget code IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f); - IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemAddFlags flags = 0); + 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 void ItemFocusable(ImGuiWindow* window, ImGuiID id); + IMGUI_API void ItemInputable(ImGuiWindow* window, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); @@ -2419,11 +2411,13 @@ namespace ImGui IMGUI_API void PopItemFlag(); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // Currently refactoring focus/nav/tabbing system // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)' - // (New) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18411: using 'ItemAdd(..., ImGuiItemAddFlags_Inputable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' // 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_Focusable flag to ItemAdd() + 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 diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 10ea9cfb..4b347245 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2389,7 +2389,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -3005,7 +3005,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemAddFlags_Focusable : 0)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -3443,7 +3443,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data style.FramePadding.x = style.FramePadding.y; ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; if (flags & ImGuiInputTextFlags_ReadOnly) - BeginDisabled(true); + BeginDisabled(); SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) { @@ -3975,7 +3975,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { ImVec2 backup_pos = window->DC.CursorPos; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) { EndGroup(); return false; @@ -4006,7 +4006,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) ItemSize(total_bb, style.FramePadding.y); if (!(flags & ImGuiInputTextFlags_MergedItem)) - if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemAddFlags_Focusable)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) return false; item_status_flags = g.LastItemData.StatusFlags; } @@ -6144,20 +6144,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl window->ClipRect.Max.x = window->ParentWorkRect.Max.x; } - bool item_add; const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; - if (disabled_item) - { - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_Disabled; - item_add = ItemAdd(bb, id); - g.CurrentItemFlags = backup_item_flags; - } - else - { - item_add = ItemAdd(bb, id); - } - + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; @@ -6169,7 +6157,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; if (disabled_item && !disabled_global) // Only testing this as an optimization - BeginDisabled(true); + BeginDisabled(); // 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. @@ -7007,7 +6995,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut bool pressed; PushID(label); if (!enabled) - BeginDisabled(true); + BeginDisabled(); const ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) From dff15acdb543590c0beff6fd0d1dd56b6654bfae Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Sep 2021 15:51:21 +0200 Subject: [PATCH 039/332] Nav: Fixed toggling menu layer with Alt exiting menu layer with Esc not moving mouse when NavEnableSetMousePos config flag is set. --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 28 +++++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b592b5e8..73fc3b4e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,11 +42,13 @@ Breaking Changes: Other Changes: -- Windows: fixed background order of overlapping childs submitted sequentially. (#4493) +- Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) +- Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when + the NavEnableSetMousePos config flag is set. - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) This allows apps to receive the click on void when that click is used to close popup (by default, diff --git a/imgui.cpp b/imgui.cpp index 4ce8cf04..140f2385 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8682,6 +8682,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) //----------------------------------------------------------------------------- // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked. +// In our terminology those should be interchangeable. Those two functions are merely a legacy artifact, so at minimum naming should be clarified. void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; @@ -9039,14 +9040,14 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) if (window->NavLastIds[layer] != 0) { SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; } else { g.NavLayer = layer; NavInitWindow(window, true); } + g.NavDisableHighlight = false; + g.NavDisableMouseHover = g.NavMousePosDirty = true; } static inline void ImGui::NavUpdateAnyRequestFlag() @@ -9211,6 +9212,7 @@ static void ImGui::NavUpdate() { io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); io.WantSetMousePos = true; + //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); } g.NavMousePosDirty = false; } @@ -9310,6 +9312,7 @@ static void ImGui::NavUpdateInitResult() // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); + g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result if (g.NavInitRequestFromMove) { g.NavDisableHighlight = false; @@ -9415,9 +9418,10 @@ void ImGui::NavMoveRequestApplyResult() { ImGuiContext& g = *GImGui; + // No result + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) { - // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) if (g.NavId != 0) { g.NavDisableHighlight = false; @@ -9470,8 +9474,12 @@ void ImGui::NavMoveRequestApplyResult() g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; } + + // Focus IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result 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); + + // Enable nav highlight g.NavDisableHighlight = false; g.NavDisableMouseHover = g.NavMousePosDirty = true; } @@ -9863,14 +9871,16 @@ static void ImGui::NavUpdateWindowing() FocusWindow(new_nav_window); new_nav_window->NavLastChildNavWindow = old_nav_window; } - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - // Reinitialize navigation when entering menu bar with the Alt key. + // Toggle layer const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; - if (new_nav_layer == ImGuiNavLayer_Menu) - g.NavWindow->NavLastIds[new_nav_layer] = 0; - NavRestoreLayer(new_nav_layer); + if (new_nav_layer != g.NavLayer) + { + // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?) + if (new_nav_layer == ImGuiNavLayer_Menu) + g.NavWindow->NavLastIds[new_nav_layer] = 0; + NavRestoreLayer(new_nav_layer); + } } } From 5ee40c8d34bea3009cf462ec963225bd22067e5e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Sep 2021 17:36:17 +0200 Subject: [PATCH 040/332] Nav: always disable highlight if nav is disabled, fix for IMGUI_DEBUG_NAV_SCORING, minor renaming. --- imgui.cpp | 33 ++++++++++++++++++++------------- imgui_internal.h | 7 +++---- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 140f2385..1df609d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3685,13 +3685,15 @@ static void ImGui::UpdateMouseInputs() // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) if (IsMousePosValid(&g.IO.MousePos)) - g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); + g.IO.MousePos = g.MouseLastValidPos = ImFloor(g.IO.MousePos); // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + + // 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 (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; @@ -8762,7 +8764,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) // FIXME: Those are not good variables names ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringCount++; + g.NavScoringDebugCount++; // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring if (window->ParentWindow == g.NavWindow) @@ -8929,14 +8931,7 @@ static void ImGui::NavProcessItem() if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) { ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - bool new_best = NavScoreItem(result); - -#if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Scoring all items in NavWindow at all times - if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) - new_best = false; -#endif - if (new_best) + if (NavScoreItem(result)) NavApplyItemToResult(result); // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. @@ -8972,6 +8967,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM IM_ASSERT(g.NavWindow != NULL); g.NavMoveSubmitted = g.NavMoveScoringItems = true; g.NavMoveDir = move_dir; + g.NavMoveDirForDebug = move_dir; g.NavMoveClipDir = clip_dir; g.NavMoveFlags = move_flags; g.NavMoveForwardToNextFrame = false; @@ -9098,7 +9094,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() // Mouse (we need a fallback in case the mouse becomes invalid after being used) if (IsMousePosValid(&g.IO.MousePos)) return g.IO.MousePos; - return g.LastValidMousePos; + return g.MouseLastValidPos; } else { @@ -9155,7 +9151,7 @@ static void ImGui::NavUpdate() io.WantSetMousePos = false; #if 0 - if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); + if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) @@ -9289,8 +9285,15 @@ static void ImGui::NavUpdate() SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); } + // Always prioritize mouse highlight if navigation is disabled + if (!nav_keyboard_active && !nav_gamepad_active) + { + g.NavDisableHighlight = true; + g.NavDisableMouseHover = g.NavMousePosDirty = false; + } + // [DEBUG] - g.NavScoringCount = 0; + g.NavScoringDebugCount = 0; #if IMGUI_DEBUG_NAV_RECTS if (g.NavWindow) { @@ -9417,6 +9420,10 @@ void ImGui::NavUpdateCreateMoveRequest() void ImGui::NavMoveRequestApplyResult() { ImGuiContext& g = *GImGui; +#if IMGUI_DEBUG_NAV_SCORING + if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times + return; +#endif // No result // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) diff --git a/imgui_internal.h b/imgui_internal.h index 99cddcf3..04b2b913 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1527,7 +1527,7 @@ struct ImGuiContext ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. - int NavScoringCount; // Metrics for debugging + int NavScoringDebugCount; // Metrics for debugging ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) @@ -1587,7 +1587,7 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Widget state - ImVec2 LastValidMousePos; + ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. @@ -1728,7 +1728,7 @@ struct ImGuiContext NavMoveFlags = ImGuiNavMoveFlags_None; NavMoveKeyMods = ImGuiKeyModFlags_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; - NavScoringCount = 0; + NavScoringDebugCount = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; @@ -1758,7 +1758,6 @@ struct ImGuiContext CurrentTableStackIdx = -1; CurrentTabBar = NULL; - LastValidMousePos = ImVec2(0.0f, 0.0f); TempInputId = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditLastHue = ColorEditLastSat = 0.0f; From 528104a89bd46d01ede9011ee529ddfb554d4c6b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Sep 2021 16:34:38 +0200 Subject: [PATCH 041/332] Menus: adjust closing logic to accomodate for varying font size and dpi + remove seemingly redundant use of g.NavActivateId where pressed was enough. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 28 ++++++++++------------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 73fc3b4e..612c21de 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,7 @@ Other Changes: to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when the NavEnableSetMousePos config flag is set. +- Menus: adjust closing logic to accomodate for varying font size and dpi. - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) This allows apps to receive the click on void when that click is used to close popup (by default, diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4b347245..1f35431c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1581,7 +1581,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF bool pressed = ButtonBehavior(bb, id, &hovered, &held); const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); - if ((pressed || g.NavActivateId == id) && !popup_open) + if (pressed && !popup_open) { OpenPopupEx(popup_id, ImGuiPopupFlags_None); popup_open = true; @@ -6870,37 +6870,29 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_toward_other_child_menu = false; - ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) { - // FIXME-DPI: Values should be derived from a master "scale" factor. + float ref_unit = g.FontSize; // FIXME-DPI ImRect next_window_rect = child_menu_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??) + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } - - // FIXME: Hovering a disabled BeginMenu or MenuItem won't close us if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) want_close = true; - if (!menu_is_open && hovered && pressed) // Click to open + // Open + if (!menu_is_open && pressed) // Click/activate to open want_open = true; else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open want_open = true; - - if (g.NavActivateId == id) - { - want_close = menu_is_open; - want_open = !menu_is_open; - } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = true; From ade4c15eeaba58d1c272383bc7c330269f2cfa9e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Sep 2021 18:41:00 +0200 Subject: [PATCH 042/332] Nav: Tidying up, renaming + update AnyRequest flag in NavMoveRequestSubmit(). --- imgui.cpp | 22 ++++++++++------------ imgui_internal.h | 1 + imgui_widgets.cpp | 1 - 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1df609d6..5ec21689 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -910,7 +910,6 @@ namespace ImGui static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); -static void NavUpdateInitResult(); static void NavUpdateCancelRequest(); static void NavUpdateCreateMoveRequest(); static float NavUpdatePageUpPageDown(); @@ -3332,7 +3331,7 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) } if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) { - g.NavJustTabbedId = id; + g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing; return; } @@ -8975,6 +8974,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultOther.Clear(); + NavUpdateAnyRequestFlag(); } void ImGui::NavMoveRequestCancel() @@ -9150,9 +9150,7 @@ static void ImGui::NavUpdate() ImGuiIO& io = g.IO; io.WantSetMousePos = false; -#if 0 - if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); -#endif + //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) // (do it before we map Keyboard input!) @@ -9188,7 +9186,7 @@ static void ImGui::NavUpdate() // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0) - NavUpdateInitResult(); + NavInitRequestApplyResult(); g.NavInitRequest = false; g.NavInitRequestFromMove = false; g.NavInitResultId = 0; @@ -9253,6 +9251,7 @@ static void ImGui::NavUpdate() IM_ASSERT(g.NavActivateDownId == g.NavActivateId); // Process programmatic activation request + // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; g.NavNextActivateId = 0; @@ -9304,7 +9303,7 @@ static void ImGui::NavUpdate() #endif } -static void ImGui::NavUpdateInitResult() +void ImGui::NavInitRequestApplyResult() { // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) ImGuiContext& g = *GImGui; @@ -9425,9 +9424,11 @@ void ImGui::NavMoveRequestApplyResult() return; #endif - // No result + // Select which result to use + ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) + if (result == NULL) { if (g.NavId != 0) { @@ -9437,9 +9438,6 @@ void ImGui::NavMoveRequestApplyResult() return; } - // Select which result to use - ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId) diff --git a/imgui_internal.h b/imgui_internal.h index 04b2b913..c010a980 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2451,6 +2451,7 @@ namespace ImGui // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API void NavInitRequestApplyResult(); IMGUI_API bool NavMoveRequestButNoResultYet(); IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1f35431c..c9cdcff7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2416,7 +2416,6 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, temp_input_is_active = true; } // Experimental: simple click (without moving) turns Drag into an InputText - // FIXME: Currently polling ImGuiConfigFlags_IsTouchScreen, may either poll an hypothetical ImGuiBackendFlags_HasKeyboard and/or an explicit drag settings. if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { From 0a154b30ad0f628256e3cd61b50ceb0840a1f335 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Sep 2021 17:27:17 +0200 Subject: [PATCH 043/332] Nav, ButtonBehavior: reworked old code which ended up 1) calling SetActiveId repeately and 2) misleadingly set g.NavActivateId (amend af38749e) --- imgui_widgets.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c9cdcff7..0ac0fb05 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -604,13 +604,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_inputs = IsNavInputTest(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); if (nav_activated_by_code || nav_activated_by_inputs) - pressed = true; - if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) { // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source + pressed = true; SetActiveID(id, window); - if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus)) + g.ActiveIdSource = ImGuiInputSource_Nav; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); } } From bd6c9e99fc0aee14ab07e2f916d5a0dc85eb7b78 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Sep 2021 18:45:50 +0200 Subject: [PATCH 044/332] InputText: Allow cancelling/validating input with gamepad nav events. Amend 158995f2 (#2321) --- docs/CHANGELOG.txt | 2 ++ imgui.h | 2 +- imgui_widgets.cpp | 8 ++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 612c21de..0aff3529 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,8 @@ Other Changes: - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. +- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing + an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when diff --git a/imgui.h b/imgui.h index 78ac06fc..2fcdbf89 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18411 +#define IMGUI_VERSION_NUM 18412 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0ac0fb05..234ba585 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4246,6 +4246,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; + // 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 is_validate = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter) || IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); + if (IsKeyPressedMap(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 (IsKeyPressedMap(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 (IsKeyPressedMap(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); } @@ -4266,7 +4270,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + else if (is_validate) { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) @@ -4280,7 +4284,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed((int)c); } } - else if (IsKeyPressedMap(ImGuiKey_Escape)) + else if (is_cancel) { clear_active_id = cancel_edit = true; } From 607ad8c949a5411f4ce069e8e0f8ae773718e7ec Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Sep 2021 20:42:40 +0200 Subject: [PATCH 045/332] Drag and Drop: Fixed using BeginDragDropSource() within a Begin()/BeginChild() that returned false. (#4515) + BeginDragDropTarget() Note how 79ae6d3b adedd a SkipItems test in BeginDragDropTargetCustom() only. Catching this similar to work needed to neatly represent the error in #4375 #4158, #4008, #2562 --- docs/CHANGELOG.txt | 1 + imgui.cpp | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0aff3529..a99593c4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,7 @@ Other Changes: - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when the NavEnableSetMousePos config flag is set. - Menus: adjust closing logic to accomodate for varying font size and dpi. +- Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) This allows apps to receive the click on void when that click is used to close popup (by default, diff --git a/imgui.cpp b/imgui.cpp index 5ec21689..a4e6ed61 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7813,6 +7813,7 @@ ImVec2 ImGui::GetWindowContentRegionMax() // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. +// FIXME-OPT: Could we safely early out on ->SkipItems? void ImGui::BeginGroup() { ImGuiContext& g = *GImGui; @@ -9978,14 +9979,16 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) return false; if (g.ActiveIdMouseButton != -1) mouse_button = g.ActiveIdMouseButton; - if (g.IO.MouseDown[mouse_button] == false) + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) return false; g.ActiveIdAllowOverlap = false; } else { // Uncommon path: items without ID - if (g.IO.MouseDown[mouse_button] == false) + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) + return false; + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) return false; // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: @@ -9996,10 +9999,6 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) return false; } - // Early out - if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) - return false; - // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() // We build a throwaway ID based on current ID stack + relative AABB of items in window. // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. @@ -10165,7 +10164,7 @@ bool ImGui::BeginDragDropTarget() if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) + if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems) return false; const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect; From 755bf985d2c8ca29a83e7a2900907a3992910edb Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 7 Sep 2021 14:48:26 +0200 Subject: [PATCH 046/332] Backends: OpenGL3: Made our custom GL loader a bit more tolerant of use of another loader in same compilation unit. (#4445, #4514) --- backends/imgui_impl_opengl3_loader.h | 32 +++++++++++++++++++++++----- docs/CHANGELOG.txt | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index e70e7a32..9313deda 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -1,3 +1,22 @@ +//----------------------------------------------------------------------------- +// About imgui_impl_opengl3_loader.h: +// +// We embed our own OpenGL loader to not require user to provide their own or to have to use ours, +// which proved to be endless problems for users. +// Our loader is custom-generated, based on gl3w but automatically filtered to only include +// enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small. +// +// YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY. +// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE. +// +// Regenerate with: +// python 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 +// https://github.com/ocornut/imgui/issues/4445 +//----------------------------------------------------------------------------- + /* * This file was generated with gl3w_gen.py, part of imgl3w * (hosted at https://github.com/dearimgui/gl3w_stripped) @@ -26,14 +45,11 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -// We embed our own OpenGL loader to not require user to provide their own or to have to use ours, which proved to be endless problems for users. -// Our loader is custom-generated, based on gl3w but automatically filtered to only include enums/functions that we use in this source file. -// Regenerate with: python gl3w_gen.py --imgui-dir /path/to/imgui/ -// see https://github.com/dearimgui/gl3w_stripped for more info. #ifndef __gl3w_h_ #define __gl3w_h_ // Adapted from KHR/khrplatform.h to avoid including entire file. +#ifndef __khrplatform_h_ typedef float khronos_float_t; typedef signed char khronos_int8_t; typedef unsigned char khronos_uint8_t; @@ -58,6 +74,7 @@ typedef uint64_t khronos_uint64_t; typedef signed long long khronos_int64_t; typedef unsigned long long khronos_uint64_t; #endif +#endif // __khrplatform_h_ #ifndef __gl_glcorearb_h_ #define __gl_glcorearb_h_ 1 @@ -162,6 +179,7 @@ typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); @@ -177,6 +195,7 @@ GLAPI void APIENTRY glDisable (GLenum cap); GLAPI void APIENTRY glEnable (GLenum cap); GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +GLAPI GLenum APIENTRY glGetError (void); GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data); GLAPI const GLubyte *APIENTRY glGetString (GLenum name); GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap); @@ -416,7 +435,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); /* gl3w internal state */ union GL3WProcs { - GL3WglProc ptr[52]; + GL3WglProc ptr[53]; struct { PFNGLACTIVETEXTUREPROC ActiveTexture; PFNGLATTACHSHADERPROC AttachShader; @@ -448,6 +467,7 @@ union GL3WProcs { PFNGLGENTEXTURESPROC GenTextures; PFNGLGENVERTEXARRAYSPROC GenVertexArrays; PFNGLGETATTRIBLOCATIONPROC GetAttribLocation; + PFNGLGETERRORPROC GetError; PFNGLGETINTEGERVPROC GetIntegerv; PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog; PFNGLGETPROGRAMIVPROC GetProgramiv; @@ -506,6 +526,7 @@ GL3W_API extern union GL3WProcs imgl3wProcs; #define glGenTextures imgl3wProcs.gl.GenTextures #define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays #define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation +#define glGetError imgl3wProcs.gl.GetError #define glGetIntegerv imgl3wProcs.gl.GetIntegerv #define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog #define glGetProgramiv imgl3wProcs.gl.GetProgramiv @@ -691,6 +712,7 @@ static const char *proc_names[] = { "glGenTextures", "glGenVertexArrays", "glGetAttribLocation", + "glGetError", "glGetIntegerv", "glGetProgramInfoLog", "glGetProgramiv", diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a99593c4..6cf3d6aa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -60,7 +60,7 @@ Other Changes: - Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). - Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) -- Backends: OpenGL3: Fixed our new GL loader conflicting with user using GL3W. (#4445) +- Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via a direct unclipped PushClipRect() call. (#4464) From d9b427cce06e9804466409e6611816b21ca9b003 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Sep 2021 19:15:20 +0200 Subject: [PATCH 047/332] Nav: clarified/renamed NavInputId as NavAcivateInputId, added flags shared by both. (this commit should have no visible side effect but is designed to introduce the followup commit refactoring SetKeyboardFocusHere into using a Nav request) --- imgui.cpp | 29 +++++++++++++++++++++++------ imgui_internal.h | 16 ++++++++++++++-- imgui_tables.cpp | 1 + imgui_widgets.cpp | 20 ++++++++++++-------- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a4e6ed61..0cb5d7d8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3106,7 +3106,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } // Clear declaration of inputs claimed by the widget @@ -7063,6 +7063,7 @@ void ImGui::ActivateItem(ImGuiID id) { ImGuiContext& g = *GImGui; g.NavNextActivateId = id; + g.NavNextActivateFlags = ImGuiActivateFlags_None; } void ImGui::PushFocusScope(ImGuiID id) @@ -9232,19 +9233,28 @@ static void ImGui::NavUpdate() NavUpdateCancelRequest(); // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0; + g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool input_down = IsNavInputDown(ImGuiNavInput_Input); bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); + bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); if (g.ActiveId == 0 && activate_pressed) + { g.NavActivateId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; + } + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed) + { + g.NavActivateInputId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; + } if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavDisableHighlight = true; @@ -9254,7 +9264,13 @@ static void ImGui::NavUpdate() // Process programmatic activation request // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; + { + if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput) + g.NavActivateInputId = g.NavNextActivateId; + else + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId; + g.NavActivateFlags = g.NavNextActivateFlags; + } g.NavNextActivateId = 0; // Process move requests @@ -11393,7 +11409,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); Text("NavInputSource: %s", input_source_names[g.NavInputSource]); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); + Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); + Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); diff --git a/imgui_internal.h b/imgui_internal.h index c010a980..c5d7d747 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -143,6 +143,7 @@ struct ImGuiWindowSettings; // Storage for a window .ini settings (we ke // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical +typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() @@ -1148,6 +1149,14 @@ struct ImGuiPtrOrIndex // [SECTION] Navigation support //----------------------------------------------------------------------------- +enum ImGuiActivateFlags_ +{ + ImGuiActivateFlags_None = 0, + ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available. + ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available. + ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) +}; + enum ImGuiNavHighlightFlags_ { ImGuiNavHighlightFlags_None = 0, @@ -1498,12 +1507,14 @@ struct ImGuiContext ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 + ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. + ImGuiActivateFlags NavActivateFlags; ImGuiID NavJustTabbedId; // Just tabbed to this id. 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). ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. 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. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing @@ -1708,8 +1719,9 @@ struct ImGuiContext CurrentItemFlags = ImGuiItemFlags_None; NavWindow = NULL; - NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; + NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavInputSource = ImGuiInputSource_None; NavLayer = ImGuiNavLayer_Main; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index e70a4dea..b60676a6 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1479,6 +1479,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. + // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) { int order_n = table->DisplayOrderToIndex[column_n]; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 234ba585..6e6d679d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -273,6 +273,7 @@ void ImGui::TextV(const char* fmt, va_list args) if (window->SkipItems) return; + // FIXME-OPT: Handle the %s shortcut? ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); @@ -2405,20 +2406,23 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); - if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id) + if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)) - temp_input_is_active = true; + if (temp_input_allowed) + if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + temp_input_is_active = true; } + // Experimental: simple click (without moving) turns Drag into an InputText if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { - g.NavInputId = id; + g.NavActivateId = g.NavActivateInputId = id; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; temp_input_is_active = true; } } @@ -3019,13 +3023,13 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat { const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) + if (focus_requested || clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)) + if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) temp_input_is_active = true; } } @@ -3177,7 +3181,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); @@ -4019,7 +4023,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); + const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); From 40caab47480c93407243d1462e907506cab463ce Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 7 Sep 2021 18:21:47 +0200 Subject: [PATCH 048/332] Fixed bad merge of Changelog in docking branch --- docs/CHANGELOG.txt | 109 +++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 59 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 69525b86..97961447 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -30,62 +30,6 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! ------------------------------------------------------------------------ - VERSION 1.85 WIP (In Progress) ------------------------------------------------------------------------ - -Breaking Changes: - -- Removed GetWindowContentRegionWidth() function. keep inline redirection helper. - Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not - very useful in practice, and the only use of it in the demo was illfit. - -Other Changes: - -- Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) -- InputTextMultiline: Fixed label size not being included into window contents rect unless - the whole widget is clipped. -- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing - an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) -- TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform - to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) -- Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when - the NavEnableSetMousePos config flag is set. -- Menus: adjust closing logic to accomodate for varying font size and dpi. -- Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) -- PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] -- IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) - This allows apps to receive the click on void when that click is used to close popup (by default, - clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). -- Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL - (which apparently happens with Freetype 2.11). (#4394, #4145?). -- Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) -- Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] -- Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] -- Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via - a direct unclipped PushClipRect() call. (#4464) -- Backends: All renderers: Normalize clipping rect handling across backends. (#4464) - - ------------------------------------------------------------------------ - VERSION 1.84.2 (Released 2021-08-23) ------------------------------------------------------------------------ - -Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.2 - -- Disabled: Fixed nested BeginDisabled()/EndDisabled() calls. (#211, #4452, #4453, #4462) [@Legulysse] -- Backends: OpenGL3: OpenGL: Fixed ES 3.0 shader ("#version 300 es") to use normal precision - floats. Avoid wobbly rendering at HD resolutions. (#4463) [@nicolasnoble] - - ------------------------------------------------------------------------ - VERSION 1.84.1 (Released 2021-08-20) ------------------------------------------------------------------------ - -Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.1 - -- Disabled: Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) - ----------------------------------------------------------------------- DOCKING+MULTI-VIEWPORT BRANCH (In Progress) @@ -155,9 +99,58 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.85 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +- Removed GetWindowContentRegionWidth() function. keep inline redirection helper. + Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not + very useful in practice, and the only use of it in the demo was illfit. + +Other Changes: + +- Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) +- InputTextMultiline: Fixed label size not being included into window contents rect unless + the whole widget is clipped. +- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing + an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) +- TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform + to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) +- Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when + the NavEnableSetMousePos config flag is set. +- Menus: adjust closing logic to accomodate for varying font size and dpi. +- Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) +- PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] +- IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) + This allows apps to receive the click on void when that click is used to close popup (by default, + clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). +- Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL + (which apparently happens with Freetype 2.11). (#4394, #4145?). +- Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) +- Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] +- Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] +- Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via + a direct unclipped PushClipRect() call. (#4464) +- Backends: All renderers: Normalize clipping rect handling across backends. (#4464) + Docking+Viewports Branch: - Docking: fixed settings load issue when mouse wheeling. (#4310) +- Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that + returned false because the window is docked. (#4515) + + +----------------------------------------------------------------------- + VERSION 1.84.2 (Released 2021-08-23) +----------------------------------------------------------------------- + +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.2 + +- Disabled: Fixed nested BeginDisabled()/EndDisabled() calls. (#211, #4452, #4453, #4462) [@Legulysse] +- Backends: OpenGL3: OpenGL: Fixed ES 3.0 shader ("#version 300 es") to use normal precision + floats. Avoid wobbly rendering at HD resolutions. (#4463) [@nicolasnoble] ----------------------------------------------------------------------- @@ -166,9 +159,7 @@ Docking+Viewports Branch: Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.1 -Other Changes: - -- Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) +- Disabled: Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) ----------------------------------------------------------------------- From 321b84f01fbf3f64db6dbb53deb707d150ce30dc Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Sep 2021 15:05:27 +0200 Subject: [PATCH 049/332] Internals: refactored IsWindowHovered()/IsWindowFocused() to make their logic more similar + change underlying value of ImGuiHoveredFlags_AllowWhenBlockedByPopup + comment out docking only flags. --- imgui.cpp | 66 +++++++++++++++++++++++++------------------------------ imgui.h | 10 +++++---- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0cb5d7d8..bb051e8b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6740,37 +6740,31 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; - if (g.HoveredWindow == NULL) + ImGuiWindow* ref_window = g.HoveredWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) return false; if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { - ImGuiWindow* window = g.CurrentWindow; - switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) - { - case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow->RootWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_ChildWindows: - if (!IsWindowChildOf(g.HoveredWindow, window)) - return false; - break; - default: - if (g.HoveredWindow != window) - return false; - break; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = cur_window->RootWindow; + + bool result; + if (flags & ImGuiHoveredFlags_ChildWindows) + result = IsWindowChildOf(ref_window, cur_window); + else + result = (ref_window == cur_window); + if (!result) + return false; } - if (!IsWindowContentHoverable(g.HoveredWindow, flags)) + if (!IsWindowContentHoverable(ref_window, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) + if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; return true; } @@ -6778,22 +6772,22 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; + ImGuiWindow* ref_window = g.NavWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) + return false; if (flags & ImGuiFocusedFlags_AnyWindow) - return g.NavWindow != NULL; + return true; + IM_ASSERT(cur_window); // Not inside a Begin()/End() - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() - switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) - { - case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); - default: - return g.NavWindow == g.CurrentWindow; - } + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = cur_window->RootWindow; + + if (flags & ImGuiHoveredFlags_ChildWindows) + return IsWindowChildOf(ref_window, cur_window); + else + return (ref_window == cur_window); } // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) diff --git a/imgui.h b/imgui.h index 2fcdbf89..9c4cb933 100644 --- a/imgui.h +++ b/imgui.h @@ -1264,9 +1264,10 @@ enum ImGuiTableBgTarget_ enum ImGuiFocusedFlags_ { ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused + ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) + ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + //ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1279,7 +1280,8 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window From cfb837203c5e7b550b03315d2a0fe1da0fa17b40 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Sep 2021 15:05:27 +0200 Subject: [PATCH 050/332] Internals: refactored IsWindowHovered()/IsWindowFocused() to make their logic more similar + change underlying value of ImGuiHoveredFlags_AllowWhenBlockedByPopup + comment out docking only flags. --- imgui.cpp | 66 +++++++++++++++++++++++++------------------------------ imgui.h | 10 +++++---- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bb5f7d85..ba47e2ec 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7286,37 +7286,31 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; - if (g.HoveredWindow == NULL) + ImGuiWindow* ref_window = g.HoveredWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) return false; if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { - ImGuiWindow* window = g.CurrentWindow; - switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) - { - case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow->RootWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_ChildWindows: - if (!IsWindowChildOf(g.HoveredWindow, window)) - return false; - break; - default: - if (g.HoveredWindow != window) - return false; - break; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = cur_window->RootWindow; + + bool result; + if (flags & ImGuiHoveredFlags_ChildWindows) + result = IsWindowChildOf(ref_window, cur_window); + else + result = (ref_window == cur_window); + if (!result) + return false; } - if (!IsWindowContentHoverable(g.HoveredWindow, flags)) + if (!IsWindowContentHoverable(ref_window, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) + if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; return true; } @@ -7324,22 +7318,22 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; + ImGuiWindow* ref_window = g.NavWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) + return false; if (flags & ImGuiFocusedFlags_AnyWindow) - return g.NavWindow != NULL; + return true; + IM_ASSERT(cur_window); // Not inside a Begin()/End() - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() - switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) - { - case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); - default: - return g.NavWindow == g.CurrentWindow; - } + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = cur_window->RootWindow; + + if (flags & ImGuiHoveredFlags_ChildWindows) + return IsWindowChildOf(ref_window, cur_window); + else + return (ref_window == cur_window); } ImGuiID ImGui::GetWindowDockID() diff --git a/imgui.h b/imgui.h index 1dfc4e88..ac11ce15 100644 --- a/imgui.h +++ b/imgui.h @@ -1309,9 +1309,10 @@ enum ImGuiTableBgTarget_ enum ImGuiFocusedFlags_ { ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused + ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) + ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + //ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1324,7 +1325,8 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window From 6b1e094cfbd353792ab926dc054125e25d8370d8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Sep 2021 16:49:35 +0200 Subject: [PATCH 051/332] Fixed _ChildWindows from leaking docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy and ImGuiHoveredFlags_DockHierarchy. --- docs/CHANGELOG.txt | 8 ++++++++ imgui.cpp | 20 ++++++++++++-------- imgui.h | 6 +++--- imgui_demo.cpp | 8 ++++++++ imgui_internal.h | 2 +- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 97961447..a7e5064b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -108,6 +108,10 @@ Breaking Changes: - Removed GetWindowContentRegionWidth() function. keep inline redirection helper. Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not very useful in practice, and the only use of it in the demo was illfit. +- (Docking branch) IsWindowFocused() and IsWindowHovered() with only the _ChildWindows flag + and without the _RootWindow flag used to leak docking hierarchy, so a docked window would + return as the child of the window hosting the dockspace. This was inconsistent and incorrect + with other behaviors so we fixed it. Added a new _DockHierarchy flag to opt-in this behavior. Other Changes: @@ -137,6 +141,10 @@ Other Changes: Docking+Viewports Branch: +- IsWindowFocused: Fixed using ImGuiFocusedFlags_ChildWindows (without _RootWindow) from leaking the + docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. +- IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the + docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. - Docking: fixed settings load issue when mouse wheeling. (#4310) - Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that returned false because the window is docked. (#4515) diff --git a/imgui.cpp b/imgui.cpp index ba47e2ec..634f6062 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4024,7 +4024,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true)) clear_hovered_windows = true; // Disabled mouse? @@ -7254,15 +7254,16 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -// FIXME: We are exposing the docking hierarchy to end-user here (via IsWindowHovered, IsWindowFocused) which is unusual. -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy) { - if (window->RootWindowDockTree == potential_parent) + if ((dock_hierarchy ? window->RootWindowDockTree : window->RootWindow) == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; + if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) + return false; window = window->ParentWindow; } return false; @@ -7294,13 +7295,14 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; bool result; if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window); + result = IsWindowChildOf(ref_window, cur_window, dock_hierarchy); else result = (ref_window == cur_window); if (!result) @@ -7325,13 +7327,15 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) return false; if (flags & ImGuiFocusedFlags_AnyWindow) return true; + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window); + return IsWindowChildOf(ref_window, cur_window, dock_hierarchy); else return (ref_window == cur_window); } diff --git a/imgui.h b/imgui.h index ac11ce15..b95ea778 100644 --- a/imgui.h +++ b/imgui.h @@ -1312,7 +1312,7 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! - //ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1325,7 +1325,7 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - //ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. @@ -1346,7 +1346,7 @@ enum ImGuiDockNodeFlags_ ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Shared // Disable docking inside the Central Node, which will be always kept empty. ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // Shared // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. ImGuiDockNodeFlags_NoSplit = 1 << 4, // Shared/Local // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. - ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces. ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Shared/Local // Tab bar will automatically hide when there is a single window in the dock node. }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b48f11f7..ebd1d962 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2334,11 +2334,15 @@ static void ShowDemoWindowWidgets() "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" + "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); @@ -2350,6 +2354,8 @@ static void ShowDemoWindowWidgets() "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" + "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", @@ -2358,6 +2364,8 @@ static void ShowDemoWindowWidgets() ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); diff --git a/imgui_internal.h b/imgui_internal.h index 52ff9874..af48bcda 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2557,7 +2557,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); From 9a49c1ddbd8447c44c66867269a0a93d3f412800 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Sep 2021 20:51:53 +0200 Subject: [PATCH 052/332] Viewports: fixed unnecessary creation of temporary viewports when multiple docked windows got reassigned to a new node (created mid-frame) which already has a HostWindow --- docs/CHANGELOG.txt | 6 ++++-- imgui.cpp | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a7e5064b..ce108079 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -146,8 +146,10 @@ Docking+Viewports Branch: - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. - Docking: fixed settings load issue when mouse wheeling. (#4310) -- Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that - returned false because the window is docked. (#4515) +- Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() + that returned false because the window is docked. (#4515) +- Viewports: Viewports: fixed unnecessary creation of temporary viewports when multiple docked windows + got reassigned to a new node (created mid-frame) which already has a HostWindow. ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 634f6062..d3f0391e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12008,8 +12008,15 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) { // Always inherit viewport from parent window + if (window->DockNode && window->DockNode->HostWindow) + IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport); window->Viewport = window->ParentWindow->Viewport; } + else if (window->DockNode && window->DockNode->HostWindow) + { + // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame + window->Viewport = window->DockNode->HostWindow->Viewport; + } else if (flags & ImGuiWindowFlags_Tooltip) { window->Viewport = g.MouseViewport; @@ -12052,7 +12059,7 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } - else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) + else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL) { // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; @@ -13445,6 +13452,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window if (node->HostWindow->ViewportOwned && node->IsRootNode()) { // Transfer viewport back to the remaining loose window + IMGUI_DEBUG_LOG_VIEWPORT("Node %08X transfer Viewport %08X=>%08X for Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, remaining_window->ID, remaining_window->Name); IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow); node->HostWindow->Viewport->Window = remaining_window; node->HostWindow->Viewport->ID = remaining_window->ID; From 6b77668171240d52ffe85e1c6aa3ee24fe22aaf2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Sep 2021 21:05:40 +0200 Subject: [PATCH 053/332] Viewports: Fixed a crash while a window owning its viewport disappear while being dragged. t would manifest when e.g. reconfiguring dock nodes while dragging. --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 29 ++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ce108079..3cd090e6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -148,7 +148,9 @@ Docking+Viewports Branch: - Docking: fixed settings load issue when mouse wheeling. (#4310) - Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that returned false because the window is docked. (#4515) -- Viewports: Viewports: fixed unnecessary creation of temporary viewports when multiple docked windows +- Viewports: Fixed a crash while a window owning its viewport disappear while being dragged. + It would manifest when e.g. reconfiguring dock nodes while dragging. +- Viewports: Fixed unnecessary creation of temporary viewports when multiple docked windows got reassigned to a new node (created mid-frame) which already has a HostWindow. diff --git a/imgui.cpp b/imgui.cpp index d3f0391e..edbc0a15 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3690,7 +3690,10 @@ void ImGui::UpdateMouseMovingWindowNewFrame() KeepAliveID(g.ActiveId); IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree); ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; - if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) + + // When a window stop being submitted while being dragged, it may will its viewport until next Begin() + const bool window_disappared = (!moving_window->WasActive || moving_window->Viewport == NULL); + if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) @@ -3707,17 +3710,20 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } else { - // Try to merge the window back into the main viewport. - // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) - UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); + if (!window_disappared) + { + // Try to merge the window back into the main viewport. + // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); - // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. - if (!IsDragDropPayloadBeingAccepted()) - g.MouseViewport = moving_window->Viewport; + // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. + if (!IsDragDropPayloadBeingAccepted()) + g.MouseViewport = moving_window->Viewport; - // Clear the NoInput window flag set by the Viewport system - moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. + // Clear the NoInput window flag set by the Viewport system + moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. + } g.MovingWindow = NULL; ClearActiveID(); @@ -11855,7 +11861,8 @@ static void ImGui::UpdateViewportsNewFrame() // Update mouse reference viewport // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) - if (g.MovingWindow) + // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details) + if (g.MovingWindow && g.MovingWindow->Viewport) g.MouseViewport = g.MovingWindow->Viewport; else g.MouseViewport = g.MouseLastHoveredViewport; From 79d39b190b9266a6c6e7fb72196323cd6c753868 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 10 Sep 2021 14:58:45 +0200 Subject: [PATCH 054/332] Viewports: fix window with viewport ini data immediately merged into a host viewport from leaving a temporary viewport alive for a frame (would leak into backend). --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3cd090e6..807f3c25 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -152,6 +152,8 @@ Docking+Viewports Branch: It would manifest when e.g. reconfiguring dock nodes while dragging. - Viewports: Fixed unnecessary creation of temporary viewports when multiple docked windows got reassigned to a new node (created mid-frame) which already has a HostWindow. +- Viewports: Fixed window with viewport ini data immediately merged into a host viewport from + leaving a temporary viewport alive for a frame (would leak into backend). ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index edbc0a15..5e39be10 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11574,6 +11574,10 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { + // Abandon viewport + if (window->ViewportOwned && window->Viewport->Window == window) + window->Viewport->Size = ImVec2(0.0f, 0.0f); + window->Viewport = viewport; window->ViewportId = viewport->ID; window->ViewportOwned = (viewport->Window == window); @@ -11606,6 +11610,7 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG if (GetWindowAlwaysWantOwnViewport(window)) return false; + // FIXME: Can't use g.WindowsFocusOrder[] for root windows only as we care about Z order. If we maintained a DisplayOrder along with FocusOrder we could.. for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window_behind = g.Windows[n]; From 17a7084b57f9713ec6538881079d6c5a1b6b3598 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 10 Sep 2021 15:37:59 +0200 Subject: [PATCH 055/332] Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). --- docs/CHANGELOG.txt | 3 ++- imgui.h | 2 +- imgui_widgets.cpp | 11 ++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6cf3d6aa..248483ea 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,7 +51,8 @@ Other Changes: to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when the NavEnableSetMousePos config flag is set. -- Menus: adjust closing logic to accomodate for varying font size and dpi. +- Menus: Adjust closing logic to accomodate for varying font size and dpi. +- Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) diff --git a/imgui.h b/imgui.h index 9c4cb933..f36c44b0 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18412 +#define IMGUI_VERSION_NUM 18413 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6e6d679d..ddb50b67 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6968,11 +6968,12 @@ void ImGui::EndMenu() // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - { - ClosePopupToLevel(g.BeginPopupStack.Size, true); - NavMoveRequestCancel(); - } + if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window) + { + ClosePopupToLevel(g.BeginPopupStack.Size, true); + NavMoveRequestCancel(); + } EndPopup(); } From 92a39f78b97ddea0e4d23b37aca2c9d3e97fa834 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 11 Sep 2021 14:05:53 +0200 Subject: [PATCH 056/332] Fixed IsWindowFocused/IsWindowHovered with _ChildWindows for not following through popup parents (amend 6b1e094c, fix #4527) --- imgui.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5e39be10..1bfdaa74 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7268,8 +7268,11 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, { if (window == potential_parent) return true; - if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) - return false; + // 2021-09-11: we broke the unexpressed contract that this function (prior to 6b1e094, #4527) + // would follow through popup parents as well. Restoring this for now. May want to add a ImGuiFocusedFlags_PopupHierarchy flag later. + if ((window->Flags & ImGuiWindowFlags_Popup) == 0) + if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) + return false; window = window->ParentWindow; } return false; From 5d95e7eef9cad6ca1f09dcfb9c8b520b6624eb98 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 11 Sep 2021 14:06:43 +0200 Subject: [PATCH 057/332] Viewports: extracted DestroyViewport() out of UpdateViewportsNewFrame() function. --- imgui.cpp | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1bfdaa74..a0003fd8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -956,6 +956,7 @@ static void EndFrameDrawDimmedBackgrounds(); // 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 ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); +static void DestroyViewport(ImGuiViewportP* viewport); static void UpdateViewportsNewFrame(); static void UpdateViewportsEndFrame(); static void WindowSelectViewport(ImGuiWindow* window); @@ -11747,22 +11748,7 @@ static void ImGui::UpdateViewportsNewFrame() // Erase unused viewports if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { - // Clear references to this viewport in windows (window->ViewportId becomes the master data) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - { - g.Windows[window_n]->Viewport = NULL; - g.Windows[window_n]->ViewportOwned = false; - } - if (viewport == g.MouseLastHoveredViewport) - g.MouseLastHoveredViewport = NULL; - g.Viewports.erase(g.Viewports.Data + n); - - // Destroy - IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. - IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); - IM_DELETE(viewport); + DestroyViewport(viewport); n--; continue; } @@ -11794,7 +11780,7 @@ static void ImGui::UpdateViewportsNewFrame() // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. viewport->Alpha = 1.0f; - // Translate imgui windows when a Host Viewport has been moved + // Translate Dear ImGui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos; if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f)) @@ -11974,6 +11960,30 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const return viewport; } +static void ImGui::DestroyViewport(ImGuiViewportP* viewport) +{ + // Clear references to this viewport in windows (window->ViewportId becomes the master data) + ImGuiContext& g = *GImGui; + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + { + ImGuiWindow* window = g.Windows[window_n]; + if (window->Viewport != viewport) + continue; + window->Viewport = NULL; + window->ViewportOwned = false; + } + if (viewport == g.MouseLastHoveredViewport) + g.MouseLastHoveredViewport = NULL; + + // Destroy + IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. + IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); + IM_ASSERT(g.Viewports[viewport->Idx] == viewport); + g.Viewports.erase(g.Viewports.Data + viewport->Idx); + IM_DELETE(viewport); +} + // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. static void ImGui::WindowSelectViewport(ImGuiWindow* window) { From 82754561e23f824ec2e88287c00255d049e0d51a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Sep 2021 15:17:54 +0200 Subject: [PATCH 058/332] Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when activated with mouse. --- docs/CHANGELOG.txt | 5 +++++ imgui.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 248483ea..37f6e455 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,6 +51,11 @@ Other Changes: to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when the NavEnableSetMousePos config flag is set. +- Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when + activated with mouse. More specifically: BeginTabItem(), the scrolling arrows of BeginTabBar(), + the arrow section of TreeNode(), the +/- buttons of InputInt()/InputFloat(), Selectable() with + ImGuiSelectableFlags_SelectOnRelease. More generally: any direct use of ButtonBehavior() with + the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) diff --git a/imgui.h b/imgui.h index f36c44b0..6ea22bbb 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18413 +#define IMGUI_VERSION_NUM 18414 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_tables.cpp b/imgui_tables.cpp index b60676a6..7b783e3c 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1170,7 +1170,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) KeepAliveID(column_id); bool hovered = false, held = false; - bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); + bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); if (pressed && IsMouseDoubleClicked(0)) { TableSetColumnWidthAutoSingle(table, column_n); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ddb50b67..f8aea16d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -571,6 +571,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool ClearActiveID(); else SetActiveID(id, window); // Hold on ID + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); g.ActiveIdMouseButton = mouse_button_clicked; FocusWindow(window); } @@ -581,6 +583,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; if (!has_repeated_at_least_once) pressed = true; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); ClearActiveID(); } From 8dfb52245bc92fef3e58b14f88135bd75a28faf8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Sep 2021 18:03:22 +0200 Subject: [PATCH 059/332] Docking: bits. --- imgui.cpp | 92 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a0003fd8..826374db 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12493,7 +12493,7 @@ void ImGui::DestroyPlatformWindows() // | - DockContextProcessDock() - process one docking request // | - DockNodeUpdate() // | - DockNodeUpdateForRootNode() -// | - DockNodeUpdateVisibleFlagAndInactiveChilds() +// | - DockNodeUpdateFlagsAndCollapse() // | - DockNodeFindInfo() // | - destroy unused node or tab bar // | - create dock node host window @@ -12626,7 +12626,7 @@ namespace ImGui static void DockNodeHideHostWindow(ImGuiDockNode* node); static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateForRootNode(ImGuiDockNode* node); - static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); + static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); static void DockNodeRemoveTabBar(ImGuiDockNode* node); @@ -13300,7 +13300,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - ImGuiDockNodeFindInfoResults // - DockNodeFindInfo() // - DockNodeFindWindowByID() -// - DockNodeUpdateVisibleFlagAndInactiveChilds() +// - DockNodeUpdateFlagsAndCollapse() // - DockNodeUpdateVisibleFlag() // - DockNodeStartMouseMovingWindow() // - DockNodeUpdate() @@ -13568,36 +13568,36 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) } // Search function called once by root node in DockNodeUpdate() -struct ImGuiDockNodeFindInfoResults +struct ImGuiDockNodeTreeInfo { ImGuiDockNode* CentralNode; ImGuiDockNode* FirstNodeWithWindows; int CountNodesWithWindows; //ImGuiWindowClass WindowClassForMerges; - ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } + ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); } }; -static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results) +static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info) { if (node->Windows.Size > 0) { - if (results->FirstNodeWithWindows == NULL) - results->FirstNodeWithWindows = node; - results->CountNodesWithWindows++; + if (info->FirstNodeWithWindows == NULL) + info->FirstNodeWithWindows = node; + info->CountNodesWithWindows++; } if (node->IsCentralNode()) { - IM_ASSERT(results->CentralNode == NULL); // Should be only one + IM_ASSERT(info->CentralNode == NULL); // Should be only one IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this."); - results->CentralNode = node; + info->CentralNode = node; } - if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL) + if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL) return; if (node->ChildNodes[0]) - DockNodeFindInfo(node->ChildNodes[0], results); + DockNodeFindInfo(node->ChildNodes[0], info); if (node->ChildNodes[1]) - DockNodeFindInfo(node->ChildNodes[1], results); + DockNodeFindInfo(node->ChildNodes[1], info); } static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id) @@ -13611,7 +13611,7 @@ static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID i // - Remove inactive windows/nodes. // - Update visibility flag. -static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) +static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); @@ -13625,11 +13625,11 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) if (node->ChildNodes[0]) - DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]); + DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]); if (node->ChildNodes[1]) - DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]); + DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]); - // Remove inactive windows + // Remove inactive windows, collapse nodes // Merge node flags overrides stored in windows node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; for (int window_n = 0; window_n < node->Windows.Size; window_n++) @@ -13654,13 +13654,12 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod } DockNodeRemoveWindow(node, window, node->ID); window_n--; + continue; } - else - { - // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. - //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear; - node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet; - } + + // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. + //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear; + node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet; } node->UpdateMergedFlags(); @@ -13707,22 +13706,22 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind // Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class. static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) { - DockNodeUpdateVisibleFlagAndInactiveChilds(node); + DockNodeUpdateFlagsAndCollapse(node); - // FIXME-DOCK: Merge this scan into the one above. // - Setup central node pointers // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) - ImGuiDockNodeFindInfoResults results; - DockNodeFindInfo(node, &results); - node->CentralNode = results.CentralNode; - node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; - if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL) - node->LastFocusedNodeId = results.FirstNodeWithWindows->ID; + // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing + ImGuiDockNodeTreeInfo info; + DockNodeFindInfo(node, &info); + node->CentralNode = info.CentralNode; + node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL; + if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL) + node->LastFocusedNodeId = info.FirstNodeWithWindows->ID; // Copy the window class from of our first window so it can be used for proper dock filtering. // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. - if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) + if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows) { node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; for (int n = 1; n < first_node_with_windows->Windows.Size; n++) @@ -14850,6 +14849,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG } // Update Pos/Size for a node hierarchy (don't affect child Windows yet) +// (Depth-first, Pre-Order) void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes) { // During the regular dock node update we write to all nodes. @@ -14879,6 +14879,10 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si ImGuiContext& g = *GImGui; const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); + // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing. + // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImFloor() + // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce + // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge) if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce) { @@ -14896,19 +14900,19 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { // FIXME-DOCK: We cannot honor the requested size, so apply ratio. // Currently this path will only be taken if code programmatically sets WantLockSizeOnce - float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); - child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0); + float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); + child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * split_ratio); child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node - else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f) + else if (child_0->SizeRef[axis] != 0.0f && child_1->IsCentralNode()) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f) + else if (child_1->SizeRef[axis] != 0.0f && child_0->IsCentralNode()) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); @@ -14917,7 +14921,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { // 4) Otherwise distribute according to the relative ratio of each SizeRef value float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]); - child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F)); + child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5f)); child_1_size[axis] = (size_avail - child_0_size[axis]); } @@ -14946,6 +14950,7 @@ static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGu DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes); } +// (Depth-First, Pre-Order) void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) { if (node->IsLeafNode()) @@ -14980,7 +14985,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) //bb.Max[axis] -= 1; PushID(node->ID); - // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes. + // Find resizing limits by gathering list of nodes that are touching the splitter line. ImVector touching_nodes[2]; float min_size = g.Style.WindowMinSize[axis]; float resize_limits[2]; @@ -14988,9 +14993,8 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; ImGuiID splitter_id = GetID("##Splitter"); - if (g.ActiveId == splitter_id) + if (g.ActiveId == splitter_id) // Only process when splitter is active { - // Only process when splitter is active DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) @@ -14998,14 +15002,18 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); + // [DEBUG] Render touching nodes & limits /* - // [DEBUG] Render limits ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); for (int n = 0; n < 2; n++) + { + for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++) + draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255)); if (axis == ImGuiAxis_X) draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); else draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); + } */ } From 7b8bc864e9af6c6c9a22125d65595d526ba674c5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Sep 2021 11:09:39 +0200 Subject: [PATCH 060/332] Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken by f8fae022). (#4538) --- docs/CHANGELOG.txt | 1 + imgui_demo.cpp | 1 + imgui_widgets.cpp | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 37f6e455..2322101f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,7 @@ Other Changes: the arrow section of TreeNode(), the +/- buttons of InputInt()/InputFloat(), Selectable() with ImGuiSelectableFlags_SelectOnRelease. More generally: any direct use of ButtonBehavior() with the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. +- Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2a16e3c6..3cc50a8f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -379,6 +379,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } + //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! if (ImGui::BeginMenu("Tools")) { ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f8aea16d..b055e1ae 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6851,7 +6851,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) } else { - // Menu inside a menu + // Menu inside a regular/vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // 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); @@ -7007,10 +7007,11 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut // 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); + 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, flags, ImVec2(w, 0.0f)); PopStyleVar(); - RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + 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(). } else From 29828d04692016e1aa7e812b962442739db9f4f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Sep 2021 16:58:35 +0200 Subject: [PATCH 061/332] Docking: floating node with a central node hides properly when nothing is docked + rename. --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 11 +++++++++-- imgui_internal.h | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 807f3c25..0cd8dfc0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,7 +145,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. -- Docking: fixed settings load issue when mouse wheeling. (#4310) +- Docking: Fixed settings load issue when mouse wheeling. (#4310) +- Docking: Fixed manually created floating node with a central node from not hiding when windows are gone. - Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that returned false because the window is docked. (#4515) - Viewports: Fixed a crash while a window owning its viewport disappear while being dragged. diff --git a/imgui.cpp b/imgui.cpp index 826374db..8c7d7c52 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13330,6 +13330,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) State = ImGuiDockNodeState_Unknown; HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; + CountNodeWithWindows = 0; LastFrameAlive = LastFrameActive = LastFrameFocused = -1; LastFocusedNodeId = 0; SelectedTabId = 0; @@ -13715,6 +13716,7 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) DockNodeFindInfo(node, &info); node->CentralNode = info.CentralNode; node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL; + node->CountNodeWithWindows = info.CountNodesWithWindows; if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL) node->LastFocusedNodeId = info.FirstNodeWithWindows->ID; @@ -13764,9 +13766,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) bool want_to_hide_host_window = false; - if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode()) - if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) + if (node->IsFloatingNode()) + { + if (node->Windows.Size <= 1 && node->IsLeafNode()) + if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) + want_to_hide_host_window = true; + if (node->CountNodeWithWindows == 0) want_to_hide_host_window = true; + } if (want_to_hide_host_window) { if (node->Windows.Size == 1) diff --git a/imgui_internal.h b/imgui_internal.h index af48bcda..a2da0a60 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1363,6 +1363,7 @@ struct IMGUI_API ImGuiDockNode ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. + int CountNodeWithWindows; // [Root node only] int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. int LastFrameFocused; // Last frame number the node was focused. @@ -1617,7 +1618,7 @@ struct ImGuiContext 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. - ImGuiDockNode* HoveredDockNode; // Hovered dock node. + ImGuiDockNode* HoveredDockNode; // [Debug] Hovered dock node. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree. 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; From e7cc53436706ea1d25ec68f9ed5bb9a7cb08de8e Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Sep 2021 17:57:47 +0200 Subject: [PATCH 062/332] Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 43 ++++++++++++++++++++++++++++++++++++++----- imgui_internal.h | 1 + 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0cd8dfc0..9bd2f0d0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,6 +145,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. +- Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. + The algorithm is still not handling the repartition of size idealy for nested sibling, but it got better. - Docking: Fixed settings load issue when mouse wheeling. (#4310) - Docking: Fixed manually created floating node with a central node from not hiding when windows are gone. - Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() diff --git a/imgui.cpp b/imgui.cpp index 8c7d7c52..7a0f081f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12627,6 +12627,7 @@ namespace ImGui static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateForRootNode(ImGuiDockNode* node); static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node); + static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); static void DockNodeRemoveTabBar(ImGuiDockNode* node); @@ -13301,6 +13302,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - DockNodeFindInfo() // - DockNodeFindWindowByID() // - DockNodeUpdateFlagsAndCollapse() +// - DockNodeUpdateHasCentralNodeFlag() // - DockNodeUpdateVisibleFlag() // - DockNodeStartMouseMovingWindow() // - DockNodeUpdate() @@ -13338,7 +13340,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; - IsFocused = HasCloseButton = HasWindowMenuButton = false; + IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; MarkedForPosSizeWrite = false; } @@ -13625,6 +13627,7 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) + node->HasCentralNodeChild = false; if (node->ChildNodes[0]) DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]); if (node->ChildNodes[1]) @@ -13684,6 +13687,25 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) DockNodeUpdateVisibleFlag(node); } +// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames. +static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node) +{ + node->HasCentralNodeChild = false; + if (node->ChildNodes[0]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]); + if (node->IsRootNode()) + { + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } + } +} + static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag @@ -13733,6 +13755,13 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) break; } } + + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } } static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window) @@ -14791,6 +14820,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node)); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) @@ -14914,12 +14944,12 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node - else if (child_0->SizeRef[axis] != 0.0f && child_1->IsCentralNode()) + else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_1->SizeRef[axis] != 0.0f && child_0->IsCentralNode()) + else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); @@ -15696,6 +15726,7 @@ void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_docks } } +// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node. void ImGui::DockBuilderFinish(ImGuiID root_id) { ImGuiContext* ctx = GImGui; @@ -15759,6 +15790,7 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu ancestor_node = ancestor_node->ParentNode; } IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node)); DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); } @@ -16993,11 +17025,12 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) DebugNodeWindow(node->HostWindow, "HostWindow"); DebugNodeWindow(node->VisibleWindow, "VisibleWindow"); BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); - BulletText("Misc:%s%s%s%s%s", + BulletText("Misc:%s%s%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", - node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); + node->WantLockSizeOnce ? " WantLockSizeOnce" : "", + node->HasCentralNodeChild ? " HasCentralNodeChild" : ""); if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags)) { if (BeginTable("flags", 4)) diff --git a/imgui_internal.h b/imgui_internal.h index a2da0a60..ae0c4efb 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1377,6 +1377,7 @@ struct IMGUI_API ImGuiDockNode bool IsFocused :1; bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. bool HasWindowMenuButton :1; + bool HasCentralNodeChild :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window From 4f10fe0a27f73fc44d49c8b9d970c30fcd5131f8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 12:12:49 +0200 Subject: [PATCH 063/332] TreePush: removed arbitrary/weird suppot for TreePush((const char*)NULL) --- docs/CHANGELOG.txt | 5 +++++ docs/FAQ.md | 2 +- imgui_widgets.cpp | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2322101f..adea7106 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,11 @@ Other Changes: the arrow section of TreeNode(), the +/- buttons of InputInt()/InputFloat(), Selectable() with ImGuiSelectableFlags_SelectOnRelease. More generally: any direct use of ButtonBehavior() with the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. +- TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to + the TreePush(const char*) and TreePush(const void*) functions would use an hardcoded replacement. + The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) + (_explicitely_ casting a null pointer to const char*), which is unlikely and will now crash. + You may replace it with anything else. - Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). diff --git a/docs/FAQ.md b/docs/FAQ.md index f0128890..ec90ed46 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -162,7 +162,7 @@ Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-lik --- ### Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... -Your renderer is not using the font texture correctly or it hasn't be uploaded to GPU. +Your renderer is not using the font texture correctly or it hasn't been uploaded to the GPU. - If this happens using the standard backends: A) have you modified the font atlas after `ImGui_ImplXXX_NewFrame()`? B) maybe the texture failed to upload, which could happens if for some reason your texture is too big. Also see [docs/FONTS.md](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md). - If this happens with a custom backend: make sure you have uploaded the font texture to the GPU, that all shaders are rendering states are setup properly (e.g. texture is bound). Compare your code to existing backends and use a graphics debugger such as [RenderDoc](https://renderdoc.org) to debug your rendering states. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b055e1ae..5aad4c8b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5983,7 +5983,7 @@ void ImGui::TreePush(const char* str_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); + PushID(str_id); } void ImGui::TreePush(const void* ptr_id) @@ -5991,7 +5991,7 @@ void ImGui::TreePush(const void* ptr_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); + PushID(ptr_id); } void ImGui::TreePushOverrideID(ImGuiID id) From d3666940621fcccb500e2725ddd4ec7371eeb8f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 15:16:58 +0200 Subject: [PATCH 064/332] Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) + Added asserts for missing PopItemFlag() calls. Added both to ErrorCheckEndFrameRecover (#1651) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 17 +++++++++++++++++ imgui.h | 2 +- imgui_internal.h | 10 +++++++--- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index adea7106..09e395da 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,7 @@ Other Changes: an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) +- Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when the NavEnableSetMousePos config flag is set. - Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when @@ -66,6 +67,7 @@ Other Changes: - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] +- Misc: Added asserts for missing PopItemFlag() calls. - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) This allows apps to receive the click on void when that click is used to close popup (by default, clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). diff --git a/imgui.cpp b/imgui.cpp index bb051e8b..b3256979 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6661,11 +6661,14 @@ void ImGui::BeginDisabled(bool disabled) if (was_disabled || disabled) g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.DisabledStackSize++; } void ImGui::EndDisabled() { ImGuiContext& g = *GImGui; + IM_ASSERT(g.DisabledStackSize > 0); + g.DisabledStackSize--; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; //PopItemFlag(); g.ItemFlagsStack.pop_back(); @@ -7340,11 +7343,21 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); PopID(); } + while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + EndDisabled(); + } while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); PopStyleColor(); } + while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + PopItemFlag(); + } while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); @@ -7385,7 +7398,9 @@ void ImGuiStackSizes::SetToCurrentState() SizeOfFontStack = (short)g.FontStack.Size; SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; SizeOfGroupStack = (short)g.GroupStack.Size; + SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; + SizeOfDisabledStack = (short)g.DisabledStackSize; } // Compare to detect usage errors @@ -7403,6 +7418,8 @@ void ImGuiStackSizes::CompareWithCurrentState() // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); + IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!"); + IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!"); IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); diff --git a/imgui.h b/imgui.h index 6ea22bbb..3f107e49 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18414 +#define IMGUI_VERSION_NUM 18415 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index c5d7d747..aec5f498 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1381,7 +1381,9 @@ struct IMGUI_API ImGuiStackSizes short SizeOfFontStack; short SizeOfFocusScopeStack; short SizeOfGroupStack; + short SizeOfItemFlagsStack; short SizeOfBeginPopupStack; + short SizeOfDisabledStack; ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } void SetToCurrentState(); @@ -1613,9 +1615,10 @@ struct ImGuiContext bool DragCurrentAccumDirty; float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio - float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? - int TooltipOverrideCount; + float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() + short DisabledStackSize; + short TooltipOverrideCount; float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work) ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once @@ -1780,6 +1783,7 @@ struct ImGuiContext DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; DisabledAlphaBackup = 0.0f; + DisabledStackSize = 0; ScrollbarClickDeltaToGrabCenter = 0.0f; TooltipOverrideCount = 0; TooltipSlowDelay = 0.50f; @@ -1863,7 +1867,7 @@ struct IMGUI_API ImGuiWindowTempData float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting // FIXME: Can be moved to ImGuiWindowStackData }; // Storage for one window From 66cd21db886be6fe114b9550ff72d65526a34efe Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 15:23:55 +0200 Subject: [PATCH 065/332] Misc: extracted ErrorCheckEndWindowRecover() out of ErrorCheckEndFrameRecover(). (#1651) --- imgui.cpp | 113 +++++++++++++++++++++++++---------------------- imgui_internal.h | 1 + 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b3256979..5cab7224 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7316,61 +7316,11 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi ImGuiContext& g = *GImGui; while (g.CurrentWindowStack.Size > 0) { - while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); - EndTable(); - } + ErrorCheckEndWindowRecover(log_callback, user_data); ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window != NULL); - while (g.CurrentTabBar != NULL) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); - EndTabBar(); - } - while (window->DC.TreeDepth > 0) - { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); - TreePop(); - } - while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); - EndGroup(); - } - while (window->IDStack.Size > 1) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); - PopID(); - } - while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); - EndDisabled(); - } - while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); - PopStyleColor(); - } - while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); - PopItemFlag(); - } - while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); - PopStyleVar(); - } - while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); - PopFocusScope(); - } if (g.CurrentWindowStack.Size == 1) { - IM_ASSERT(g.CurrentWindow->IsFallbackWindow); + IM_ASSERT(window->IsFallbackWindow); break; } IM_ASSERT(window == g.CurrentWindow); @@ -7387,6 +7337,65 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi } } +// Must be called before End()/EndChild() +void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) +{ + ImGuiContext& g = *GImGui; + while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); + EndTable(); + } + + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(window != NULL); + while (g.CurrentTabBar != NULL) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); + EndTabBar(); + } + while (window->DC.TreeDepth > 0) + { + if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); + TreePop(); + } + while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); + EndGroup(); + } + while (window->IDStack.Size > 1) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); + PopID(); + } + while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + EndDisabled(); + } + while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); + PopStyleColor(); + } + while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + PopItemFlag(); + } + while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); + PopStyleVar(); + } + while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); + PopFocusScope(); + } +} + // Save current stack sizes for later compare void ImGuiStackSizes::SetToCurrentState() { diff --git a/imgui_internal.h b/imgui_internal.h index aec5f498..0aa49df7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2681,6 +2681,7 @@ namespace ImGui // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); + IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); 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; } From 2d0a6a4969b4da5eae0704e740e1ae0e11b946d9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 15:36:03 +0200 Subject: [PATCH 066/332] Misc: moved StacSizeOnBegin out of window instance into window stack data. --- imgui.cpp | 21 +++++++++++---------- imgui_internal.h | 36 ++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5cab7224..264493b8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5845,12 +5845,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // 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; window_stack_data.ParentLastItemDataBackup = g.LastItemData; + window_stack_data.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindowStack.push_back(window_stack_data); - g.CurrentWindow = window; - window->DC.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindow = NULL; if (flags & ImGuiWindowFlags_Popup) @@ -6465,10 +6465,10 @@ void ImGui::End() // Pop from window stack g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; - g.CurrentWindowStack.pop_back(); if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - window->DC.StackSizesOnBegin.CompareWithCurrentState(); + g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); + g.CurrentWindowStack.pop_back(); SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } @@ -7348,6 +7348,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo } ImGuiWindow* window = g.CurrentWindow; + ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; IM_ASSERT(window != NULL); while (g.CurrentTabBar != NULL) //-V1044 { @@ -7359,7 +7360,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); TreePop(); } - while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) + while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) { if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); EndGroup(); @@ -7369,27 +7370,27 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); PopID(); } - while (g.DisabledStackSize > window->DC.StackSizesOnBegin.SizeOfDisabledStack) + while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) { if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); EndDisabled(); } - while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) + while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); PopStyleColor(); } - while (g.ItemFlagsStack.Size > window->DC.StackSizesOnBegin.SizeOfItemFlagsStack) + while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); PopItemFlag(); } - while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) + while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); PopStyleVar(); } - while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) { if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); PopFocusScope(); diff --git a/imgui_internal.h b/imgui_internal.h index 0aa49df7..2c9f5b96 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1123,11 +1123,29 @@ struct ImGuiLastItemData ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } }; +struct IMGUI_API ImGuiStackSizes +{ + short SizeOfIDStack; + short SizeOfColorStack; + short SizeOfStyleVarStack; + short SizeOfFontStack; + short SizeOfFocusScopeStack; + short SizeOfGroupStack; + short SizeOfItemFlagsStack; + short SizeOfBeginPopupStack; + short SizeOfDisabledStack; + + ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } + void SetToCurrentState(); + void CompareWithCurrentState(); +}; + // Data saved for each window pushed into the stack struct ImGuiWindowStackData { ImGuiWindow* Window; ImGuiLastItemData ParentLastItemDataBackup; + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting }; struct ImGuiShrinkWidthItem @@ -1373,23 +1391,6 @@ struct ImGuiMetricsConfig } }; -struct IMGUI_API ImGuiStackSizes -{ - short SizeOfIDStack; - short SizeOfColorStack; - short SizeOfStyleVarStack; - short SizeOfFontStack; - short SizeOfFocusScopeStack; - short SizeOfGroupStack; - short SizeOfItemFlagsStack; - short SizeOfBeginPopupStack; - short SizeOfDisabledStack; - - ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } - void SetToCurrentState(); - void CompareWithCurrentState(); -}; - //----------------------------------------------------------------------------- // [SECTION] Generic context hooks //----------------------------------------------------------------------------- @@ -1867,7 +1868,6 @@ struct IMGUI_API ImGuiWindowTempData float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting // FIXME: Can be moved to ImGuiWindowStackData }; // Storage for one window From 3973de793316e84bee8719bf29091df1ad61b514 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Sep 2021 16:50:20 +0200 Subject: [PATCH 067/332] Internals: removed last parameter to IsClippedEx() + fix PVS studio warnings. --- imgui.cpp | 18 +++++++++--------- imgui_internal.h | 2 +- imgui_tables.cpp | 4 ++-- imgui_widgets.cpp | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 264493b8..0a72737a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3283,13 +3283,13 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return true; } -bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) +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 (clip_even_when_logged || !g.LogEnabled) + if (!g.LogEnabled) return true; return false; } @@ -7314,7 +7314,7 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; - while (g.CurrentWindowStack.Size > 0) + while (g.CurrentWindowStack.Size > 0) //-V1044 { ErrorCheckEndWindowRecover(log_callback, user_data); ImGuiWindow* window = g.CurrentWindow; @@ -7360,7 +7360,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); TreePop(); } - while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) + while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); EndGroup(); @@ -7370,7 +7370,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); PopID(); } - while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) + while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); EndDisabled(); @@ -7380,17 +7380,17 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); PopStyleColor(); } - while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) + while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); PopItemFlag(); } - while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) + while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); PopStyleVar(); } - while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); PopFocusScope(); @@ -7561,7 +7561,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu #endif // Clipping test - const bool is_clipped = IsClippedEx(bb, id, false); + const bool is_clipped = IsClippedEx(bb, id); if (is_clipped) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] diff --git a/imgui_internal.h b/imgui_internal.h index 2c9f5b96..66f31a89 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2413,7 +2413,7 @@ namespace ImGui 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 void ItemInputable(ImGuiWindow* window, ImGuiID id); - IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); + IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 7b783e3c..945e739a 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -324,7 +324,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG 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, false)) + if (use_child_window && IsClippedEx(outer_rect, 0)) { ItemSize(outer_rect); return false; @@ -3988,7 +3988,7 @@ void ImGui::EndColumns() const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); KeepAliveID(column_id); - if (IsClippedEx(column_hit_rect, column_id, false)) + if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test continue; bool hovered = false, held = false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5aad4c8b..3aef1a49 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -205,7 +205,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); while (line < text_end) { - if (IsClippedEx(line_rect, 0, false)) + if (IsClippedEx(line_rect, 0)) break; const char* line_end = (const char*)memchr(line, '\n', text_end - line); From 2cffcbdc64ee9301e473225fff697049697aac2d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 17 Sep 2021 15:44:43 +0200 Subject: [PATCH 068/332] InputText: fix Space key with nav enabled interfering with input text boxes (fix bd6c9e99). made it possible to activate InputText with tweak gamepad button (why not, now that we can cancel) (#4552, #2321) --- docs/CHANGELOG.txt | 3 +-- imgui.cpp | 2 +- imgui.h | 2 +- imgui_widgets.cpp | 13 ++++++++++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 09e395da..557b7154 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,8 +45,7 @@ Other Changes: - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. -- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing - an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) +- InputText: Allow activating/cancelling/validating input with gamepad nav events. (#2321, #4552) - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) diff --git a/imgui.cpp b/imgui.cpp index 0a72737a..4f3105f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5118,7 +5118,7 @@ void ImGui::EndChild() ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); - // When browsing a window that has no activable items (scroll only) we keep a highlight on the child + // 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); } diff --git a/imgui.h b/imgui.h index 3f107e49..a1d1f4b0 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18415 +#define IMGUI_VERSION_NUM 18416 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3aef1a49..590cb4bf 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3990,6 +3990,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ window->DC.CursorPos = backup_pos; // 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); @@ -4027,7 +4028,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); + const bool user_nav_input_start = (g.ActiveId != id) && (g.NavActivateInputId == id || g.NavActivateId == id); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); @@ -4255,7 +4256,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; // 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 is_validate = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter) || IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter); + const bool is_validate_nav = (g.NavInputSource == ImGuiInputSource_Gamepad && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); if (IsKeyPressedMap(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); } @@ -4278,7 +4280,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (is_validate) + else if (is_validate_enter) { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) @@ -4292,6 +4294,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed((int)c); } } + else if (is_validate_nav) + { + IM_ASSERT(!is_validate_enter); + enter_pressed = clear_active_id = true; + } else if (is_cancel) { clear_active_id = cancel_edit = true; From ddddabdccfdafffd8664fb4e29230dc4f848137e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 17 Sep 2021 16:44:14 +0200 Subject: [PATCH 069/332] InputText: amend validation test to allow pushing Activate events remotely. (#4552, #2321) --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 590cb4bf..6e3f6f23 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4257,7 +4257,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // 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 is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter); - const bool is_validate_nav = (g.NavInputSource == ImGuiInputSource_Gamepad && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); if (IsKeyPressedMap(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); } From c6ca327fb2c4a0bb35b92a1ca8633991379463df Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Sep 2021 18:43:05 +0200 Subject: [PATCH 070/332] Backends: Added more implicit asserts to detect invalid/redundant calls to Shutdown functions. (#4562) --- backends/imgui_impl_allegro5.cpp | 3 ++- backends/imgui_impl_dx10.cpp | 3 ++- backends/imgui_impl_dx11.cpp | 3 ++- backends/imgui_impl_dx12.cpp | 3 ++- backends/imgui_impl_dx9.cpp | 3 ++- backends/imgui_impl_glfw.cpp | 3 ++- backends/imgui_impl_opengl2.cpp | 3 ++- backends/imgui_impl_opengl3.cpp | 3 ++- backends/imgui_impl_sdl.cpp | 3 ++- backends/imgui_impl_vulkan.cpp | 3 ++- backends/imgui_impl_win32.cpp | 3 ++- docs/CHANGELOG.txt | 1 + 12 files changed, 23 insertions(+), 11 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 93d84f1b..ccb1396d 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -335,8 +335,9 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) void ImGui_ImplAllegro5_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplAllegro5_InvalidateDeviceObjects(); if (bd->VertexDecl) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 8a3206ff..5be46f67 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -556,8 +556,9 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) void ImGui_ImplDX10_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX10_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index a56b757e..bfe5dca2 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -571,8 +571,9 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co void ImGui_ImplDX11_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX11_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 98e56235..5058885c 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -725,8 +725,9 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO void ImGui_ImplDX12_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_InvalidateDeviceObjects(); delete[] bd->pFrameResources; diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 259ce928..9234cb01 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -295,8 +295,9 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) void ImGui_ImplDX9_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX9_InvalidateDeviceObjects(); if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 8cac7241..bf2a6543 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -308,8 +308,9 @@ bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) void ImGui_ImplGlfw_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); if (bd->InstalledCallbacks) { diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index b4ab2a39..ee52632d 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -86,8 +86,9 @@ bool ImGui_ImplOpenGL2_Init() void ImGui_ImplOpenGL2_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL2_DestroyDeviceObjects(); io.BackendRendererName = NULL; diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index dc07ba50..58664ea1 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -269,8 +269,9 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) void ImGui_ImplOpenGL3_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); io.BackendRendererName = NULL; diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 669c7b77..e5f99bcb 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -280,8 +280,9 @@ bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window) void ImGui_ImplSDL2_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 40dd0bcd..296304fa 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1048,8 +1048,9 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend void ImGui_ImplVulkan_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_DestroyDeviceObjects(); io.BackendRendererName = NULL; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 740b1765..51fb4702 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -172,8 +172,9 @@ bool ImGui_ImplWin32_Init(void* hwnd) void ImGui_ImplWin32_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 557b7154..d53e5b8f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -73,6 +73,7 @@ Other Changes: - Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL (which apparently happens with Freetype 2.11). (#4394, #4145?). - Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) +- Backends: Added more implicit asserts to detect invalid/redundant calls to Shutdown functions. (#4562) - Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via From bc3d267c5155fee705e9204c1e0958a73b819459 Mon Sep 17 00:00:00 2001 From: James McCartney Date: Mon, 20 Sep 2021 00:20:04 -0700 Subject: [PATCH 071/332] Backends: OSX: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. (#4557, #4563) --- backends/imgui_impl_osx.mm | 24 +++++++++++++++++++++--- docs/CHANGELOG.txt | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 1eb79d99..97555e6c 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -16,9 +16,11 @@ #include "imgui.h" #include "imgui_impl_osx.h" #import +#include // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. // 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. // 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. // 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application. @@ -37,7 +39,8 @@ @class ImFocusObserver; // Data -static CFAbsoluteTime g_Time = 0.0; +static double g_HostClockPeriod = 0.0; +static double g_Time = 0.0; static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; static bool g_MouseCursorHidden = false; static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; @@ -52,6 +55,18 @@ static ImFocusObserver* g_FocusObserver = NULL; + (id)_windowResizeEastWestCursor; @end +static void InitHostClockPeriod() +{ + struct mach_timebase_info info; + mach_timebase_info(&info); + g_HostClockPeriod = 1e-9 * ((double)info.denom / (double)info.numer); // Period is the reciprocal of frequency. +} + +static double GetMachAbsoluteTimeInSeconds() +{ + return (double)mach_absolute_time() * g_HostClockPeriod; +} + static void resetKeys() { ImGuiIO& io = ImGui::GetIO(); @@ -232,8 +247,11 @@ void ImGui_ImplOSX_NewFrame(NSView* view) // Setup time step if (g_Time == 0.0) - g_Time = CFAbsoluteTimeGetCurrent(); - CFAbsoluteTime current_time = CFAbsoluteTimeGetCurrent(); + { + InitHostClockPeriod(); + g_Time = GetMachAbsoluteTimeInSeconds(); + } + double current_time = GetMachAbsoluteTimeInSeconds(); io.DeltaTime = (float)(current_time - g_Time); g_Time = current_time; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d53e5b8f..781677c9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -78,6 +78,7 @@ Other Changes: - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via a direct unclipped PushClipRect() call. (#4464) +- Backends: OSX: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. (#4557, #4563) [@lfnoise] - Backends: All renderers: Normalize clipping rect handling across backends. (#4464) From fa0a314f59ffe317a0de9af5f292f3564ca3a566 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Sep 2021 13:16:00 +0200 Subject: [PATCH 072/332] Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 73262018..8f1d8151 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -152,6 +152,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. +- Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation + is disabled. (#4547, #4439) [@PathogenDavid] - Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. The algorithm is still not handling the repartition of size idealy for nested sibling, but it got better. - Docking: Fixed settings load issue when mouse wheeling. (#4310) diff --git a/imgui.cpp b/imgui.cpp index 7a0f081f..765e291b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10413,8 +10413,9 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && nav_keyboard_active && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { @@ -10465,7 +10466,7 @@ 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 (io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) + if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) { g.NavWindowingToggleLayer = true; g.NavInputSource = ImGuiInputSource_Keyboard; From 15132217a3767de0ddfe0d8e2b1bf5764e827670 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Sep 2021 13:16:00 +0200 Subject: [PATCH 073/332] Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 781677c9..87b782bf 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,8 @@ Other Changes: the arrow section of TreeNode(), the +/- buttons of InputInt()/InputFloat(), Selectable() with ImGuiSelectableFlags_SelectOnRelease. More generally: any direct use of ButtonBehavior() with the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. +- Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation + is disabled. (#4547, #4439) [@PathogenDavid] - TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to the TreePush(const char*) and TreePush(const void*) functions would use an hardcoded replacement. The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) diff --git a/imgui.cpp b/imgui.cpp index 4f3105f2..3a012e3b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9777,8 +9777,9 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && nav_keyboard_active && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { @@ -9829,7 +9830,7 @@ 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 (io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) + if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) { g.NavWindowingToggleLayer = true; g.NavInputSource = ImGuiInputSource_Keyboard; From 30546bc0e7bccbebda0e328780e48bc95af3ae2c Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 8 Apr 2021 14:37:03 +0300 Subject: [PATCH 074/332] ColorEdit: Fix multiple issues. (#4014) * Change g.ColorEditLastColor type to ImU32 and store RGB color value. - Fixes inability to change hue when saturation is 0. (#4014) - Fixes edgecases where lossy color conversion prevent restoration of hue/saturation. - Fixes hue value jitter when modifying color using SV square. * Fix hue resetting to 0 when it is set to 255 by explicitly restoring hue if it is 0 and previous value was 1. * Further reduce hue jitter by restoring hue when color is modified using SV square. --- docs/CHANGELOG.txt | 4 ++++ imgui.h | 2 +- imgui_internal.h | 8 +++---- imgui_widgets.cpp | 56 +++++++++++++++++++++++++++------------------- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 87b782bf..753d7e49 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -63,6 +63,10 @@ Other Changes: The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) (_explicitely_ casting a null pointer to const char*), which is unlikely and will now crash. You may replace it with anything else. +- ColorEdit4: Fixed not being able to change hue when saturation is 0. (#4014) [@rokups] +- ColorEdit4: Fixed hue resetting to 0 when it is set to 255. [@rokups] +- ColorEdit4: Fixed hue value jitter when source color is stored as RGB in 32-bit integer and perform + RGB<>HSV round trips every frames. [@rokups] - Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). diff --git a/imgui.h b/imgui.h index a1d1f4b0..91041a8c 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18416 +#define IMGUI_VERSION_NUM 18417 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 66f31a89..01a41953 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1606,9 +1606,9 @@ struct ImGuiContext ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets - float ColorEditLastHue; // Backup of last Hue associated to LastColor[3], so we can restore Hue in lossy RGB<>HSV round trips - float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips - float ColorEditLastColor[3]; + float ColorEditLastHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips + float ColorEditLastSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips + ImU32 ColorEditLastColor; // RGB value with alpha set to 0. ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. ImGuiComboPreviewData ComboPreviewData; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. @@ -1777,7 +1777,7 @@ struct ImGuiContext TempInputId = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditLastHue = ColorEditLastSat = 0.0f; - ColorEditLastColor[0] = ColorEditLastColor[1] = ColorEditLastColor[2] = FLT_MAX; + ColorEditLastColor = 0; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; DragCurrentAccumDirty = false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6e3f6f23..633a1f2e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4781,6 +4781,30 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); } +// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. +// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. +static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) +{ + // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined. + // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one. + // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined. + // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision. + // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined, + // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker. + ImGuiContext& g = *GImGui; + if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + return; + + // When S == 0, H is undefined. + // When H == 1 it wraps around to 0. + if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1)) + *H = g.ColorEditLastHue; + + // When V == 0, S is undefined. + if (*V == 0.0f) + *S = g.ColorEditLastSat; +} + // Edit colors components (each component in 0.0f..1.0f range). // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. // With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. @@ -4836,13 +4860,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (f[1] == 0) - f[0] = g.ColorEditLastHue; - if (f[2] == 0) - f[1] = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); } int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; @@ -4977,7 +4995,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag g.ColorEditLastHue = f[0]; g.ColorEditLastSat = f[1]; ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - memcpy(g.ColorEditLastColor, f, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); } if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); @@ -5112,13 +5130,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5171,6 +5183,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { 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)); + + // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. + if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + H = g.ColorEditLastHue; value_changed = value_changed_sv = true; } if (!(flags & ImGuiColorEditFlags_NoOptions)) @@ -5247,7 +5263,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); g.ColorEditLastHue = H; g.ColorEditLastSat = S; - memcpy(g.ColorEditLastColor, col, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5301,13 +5317,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl G = col[1]; B = col[2]; ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) // Fix local Hue as display below will use it immediately. - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. } else if (flags & ImGuiColorEditFlags_InputHSV) { From 15fe7ba31ff3b281c3b4336aa2bdf70ab2058e31 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 12 Apr 2021 12:42:04 +0300 Subject: [PATCH 075/332] ColorPicker: Fix not being able to pick exactly (1.0f, 1.0f, 1.0f) color by dragging toward the edges of the SV square. (#3517) Old code attempted to mitigate hue/saturation resetting for colors where these components are undefined. Since we now explicitly back up and restore these components this workaround is no longer necessary. --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 753d7e49..b12ce98f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,6 +67,8 @@ Other Changes: - ColorEdit4: Fixed hue resetting to 0 when it is set to 255. [@rokups] - ColorEdit4: Fixed hue value jitter when source color is stored as RGB in 32-bit integer and perform RGB<>HSV round trips every frames. [@rokups] +- ColorPicker4: Fixed picker being unable to select exact 1.0f color when dragging toward the edges + of the SV square (previously picked 0.999989986f). (#3517) [@rokups] - Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) - Menus: Adjust closing logic to accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 633a1f2e..9e045e9c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5260,7 +5260,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if (flags & ImGuiColorEditFlags_InputRGB) { - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); g.ColorEditLastHue = H; g.ColorEditLastSat = S; g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); From d7260104b145fc8a25e3423575962a082bfd2e33 Mon Sep 17 00:00:00 2001 From: Martin Ejdestig Date: Tue, 21 Sep 2021 19:18:21 +0200 Subject: [PATCH 076/332] Added comments about sliders clamping and ImGuiSliderFlags_AlwaysClamp (#4573) --- imgui.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.h b/imgui.h index 91041a8c..df08ea64 100644 --- a/imgui.h +++ b/imgui.h @@ -522,12 +522,12 @@ namespace ImGui 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); // Widgets: Drag Sliders - // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - 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. // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits. + // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -546,7 +546,7 @@ namespace ImGui IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders - // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any slider 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. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. From 62b17f928ee43cf681e5e1dcbe27a463c1ebef4d Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 21 Sep 2021 17:12:30 +0200 Subject: [PATCH 077/332] Backends: SDL_Renderer: Added renderer backend for SDL 2.0.17+ (#3926) (Squashed 20 commits) --- backends/imgui_impl_sdl.cpp | 5 + backends/imgui_impl_sdl.h | 1 + backends/imgui_impl_sdlrenderer.cpp | 223 ++++++++++++++++++ backends/imgui_impl_sdlrenderer.h | 19 ++ docs/BACKENDS.md | 1 + docs/EXAMPLES.md | 5 + docs/README.md | 2 +- examples/example_sdl_sdlrenderer/Makefile | 79 +++++++ examples/example_sdl_sdlrenderer/README.md | 25 ++ .../example_sdl_sdlrenderer/build_win32.bat | 8 + .../example_sdl_sdlrenderer.vcxproj | 181 ++++++++++++++ .../example_sdl_sdlrenderer.vcxproj.filters | 61 +++++ examples/example_sdl_sdlrenderer/main.cpp | 168 +++++++++++++ 13 files changed, 777 insertions(+), 1 deletion(-) create mode 100644 backends/imgui_impl_sdlrenderer.cpp create mode 100644 backends/imgui_impl_sdlrenderer.h create mode 100644 examples/example_sdl_sdlrenderer/Makefile create mode 100644 examples/example_sdl_sdlrenderer/README.md create mode 100644 examples/example_sdl_sdlrenderer/build_win32.bat create mode 100644 examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj create mode 100644 examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters create mode 100644 examples/example_sdl_sdlrenderer/main.cpp diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index e5f99bcb..651fda51 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -251,6 +251,11 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) return true; } +bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window) +{ + return ImGui_ImplSDL2_Init(window); +} + bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { IM_UNUSED(sdl_gl_context); // Viewport branch will need this. diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h index 9b40a676..f85233af 100644 --- a/backends/imgui_impl_sdl.h +++ b/backends/imgui_impl_sdl.h @@ -21,6 +21,7 @@ struct SDL_Window; typedef union SDL_Event SDL_Event; +IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp new file mode 100644 index 00000000..a268deec --- /dev/null +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -0,0 +1,223 @@ +// dear imgui: Renderer Backend for SDL_Renderer, with Platform Backend SDL +// (Requires: SDL 2.0.17+) + +// Implemented features: + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// 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 + + +// CHANGELOG +// 2021-16-03: Creation + +#include "imgui.h" +#include "imgui_impl_sdlrenderer.h" +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif + +#include "SDL.h" + +#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 +# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +#endif + +struct ImGui_ImplSDLRenderer_Data +{ + SDL_Renderer *SDLRenderer; + SDL_Texture *FontTexture; + ImGui_ImplSDLRenderer_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplSDLRenderer_Data* ImGui_ImplSDLRenderer_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer_Data*)ImGui::GetIO().BackendRendererUserData : NULL; +} + +// Functions +bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer) +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + IM_ASSERT(renderer != NULL && "SDL_Renderer not initialized!"); + + // Setup backend capabilities flags + ImGui_ImplSDLRenderer_Data* bd = IM_NEW(ImGui_ImplSDLRenderer_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_SDLRenderer"; + + bd->SDLRenderer = renderer; + return true; +} + +void ImGui_ImplSDLRenderer_Shutdown() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + + ImGui_ImplSDLRenderer_DestroyDeviceObjects(); + + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + IM_DELETE(bd); +} + +static void ImGui_ImplSDLRenderer_SetupRenderState() +{ + ImGui_ImplSDLRenderer_Data *bd = ImGui_ImplSDLRenderer_GetBackendData(); + + // Clear out any viewports and cliprects set by the user + SDL_RenderSetViewport(bd->SDLRenderer, NULL); + SDL_RenderSetClipRect(bd->SDLRenderer, NULL); +} + +void ImGui_ImplSDLRenderer_NewFrame() +{ + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDLRenderer_Init()?"); + + if (!bd->FontTexture) { + ImGui_ImplSDLRenderer_CreateDeviceObjects(); + } +} + +void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) +{ + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + + // If there's a scale factor set by the user, use that instead + float rsX = 1.0f; + float rsY = 1.0f; + SDL_RenderGetScale(bd->SDLRenderer, &rsX, &rsY); + + ImVec2 renderScale; + // If the user has specified a scale factor to SDL_Renderer already (via SDL_RenderSetScale()), SDL will scale whatever we pass + // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. + renderScale.x = (rsX == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + renderScale.y = (rsY == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * renderScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * renderScale.y); + if (fb_width == 0 || fb_height == 0) + return; + + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = renderScale; + + ImGui_ImplSDLRenderer_SetupRenderState(); + + for (int n = 0; n < draw_data->CmdListsCount; n++) { + + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) { + ImGui_ImplSDLRenderer_SetupRenderState(); + } else { + pcmd->UserCallback(cmd_list, pcmd); + } + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec4 clip_rect; + clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + SDL_Rect r; + r.x = clip_rect.x; + r.y = clip_rect.y; + r.w = clip_rect.z - clip_rect.x; + r.h = clip_rect.w - clip_rect.y; + + SDL_RenderSetClipRect(bd->SDLRenderer, &r); + + + int xy_stride = sizeof(ImDrawVert); + float *xy = (float *)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)); + + int uv_stride = sizeof(ImDrawVert); + float *uv = (float*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)); + + int col_stride = sizeof(ImDrawVert); + int *color = (int*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)); + + SDL_Texture *tex = (SDL_Texture*)pcmd->TextureId; + + SDL_RenderGeometryRaw(bd->SDLRenderer, tex, + xy, xy_stride, color, + col_stride, + uv, uv_stride, + cmd_list->VtxBuffer.Size, + idx_buffer, pcmd->ElemCount, sizeof (ImDrawIdx)); + + } + } + idx_buffer += pcmd->ElemCount; + } + } +} + +// Called by Init/NewFrame/Shutdown +bool ImGui_ImplSDLRenderer_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); + if (bd->FontTexture == NULL) { + SDL_Log("error creating texture"); + return false; + } + SDL_UpdateTexture(bd->FontTexture, NULL, pixels, 4 * width); + SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); + // Store our identifier + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); + + return true; +} + +void ImGui_ImplSDLRenderer_DestroyFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + if (bd->FontTexture) { + io.Fonts->SetTexID(0); + SDL_DestroyTexture(bd->FontTexture); + bd->FontTexture = NULL; + } +} + +bool ImGui_ImplSDLRenderer_CreateDeviceObjects() +{ + return ImGui_ImplSDLRenderer_CreateFontsTexture(); +} + +void ImGui_ImplSDLRenderer_DestroyDeviceObjects() +{ + ImGui_ImplSDLRenderer_DestroyFontsTexture(); +} + diff --git a/backends/imgui_impl_sdlrenderer.h b/backends/imgui_impl_sdlrenderer.h new file mode 100644 index 00000000..7b439618 --- /dev/null +++ b/backends/imgui_impl_sdlrenderer.h @@ -0,0 +1,19 @@ +// dear imgui: Renderer Backend for SDL using SDL_Renderer +// (Requires: SDL 2.0.17+) + + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API + +struct SDL_Renderer; + +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLRenderer_DestroyDeviceObjects(); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 2ad719a9..bbd66854 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -75,6 +75,7 @@ List of Renderer Backends: imgui_impl_metal.mm ; Metal (with ObjC) imgui_impl_opengl2.cpp ; OpenGL 2 (legacy, fixed pipeline <- don't use with modern OpenGL context) imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2, OpenGL ES 3 (modern programmable pipeline) + imgui_impl_sdlrenderer.cpp; use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath imgui_impl_vulkan.cpp ; Vulkan imgui_impl_wgpu.cpp ; WebGPU diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 0fd7c250..69ecfa87 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -130,6 +130,11 @@ This uses more modern OpenGL calls and custom shaders.
This may actually also work with OpenGL 2.x contexts!
Prefer using that if you are using modern OpenGL in your application (anything with shaders). +[example_sdl_sdlrenderer/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_sdlrenderer/)
+Use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath
+= main.cpp + imgui_impl_sdl.cpp + imgui_impl_sdlrenderer.cpp
+It requires SDL 2.0.17+ + [example_glfw_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_vulkan/)
GLFW (Win32, Mac, Linux) + Vulkan example.
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp
diff --git a/docs/README.md b/docs/README.md index 25850973..9d6dc93f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -116,7 +116,7 @@ On most platforms and when using C++, **you should be able to use a combination Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/keyboard/gamepad inputs 2) uploading one texture to your GPU/render engine 3) providing a render function that can bind textures and render textured triangles. The [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder is populated with applications doing just that. If you are an experienced programmer at ease with those concepts, it should take you less than two hours to integrate Dear ImGui in your custom engine. **Make sure to spend time reading the [FAQ](https://www.dearimgui.org/faq), comments, and some of the examples/ application!** Officially maintained backends/bindings (in repository): -- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, Vulkan, WebGPU. +- Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU. - Platforms: GLFW, SDL2, Win32, Glut, OSX, Android. - Frameworks: Emscripten, Allegro5, Marmalade. diff --git a/examples/example_sdl_sdlrenderer/Makefile b/examples/example_sdl_sdlrenderer/Makefile new file mode 100644 index 00000000..5667789a --- /dev/null +++ b/examples/example_sdl_sdlrenderer/Makefile @@ -0,0 +1,79 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need SDL2 (http://www.libsdl.org): +# Linux: +# apt-get install libsdl2-dev +# Mac OS X: +# brew install sdl2 +# MSYS2: +# pacman -S mingw-w64-i686-SDL2 +# + +#CXX = g++ +#CXX = clang++ + +EXE = example_sdl_sdlrenderer +IMGUI_DIR = ../.. +SOURCES = main.cpp +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl.cpp $(IMGUI_DIR)/backends/imgui_impl_sdlrenderer.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) +UNAME_S := $(shell uname -s) + +CXXFLAGS = -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +CXXFLAGS += -g -Wall -Wformat +LIBS = + +##--------------------------------------------------------------------- +## BUILD FLAGS PER PLATFORM +##--------------------------------------------------------------------- + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS += -lGL -ldl `sdl2-config --libs` + + CXXFLAGS += `sdl2-config --cflags` + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs` + LIBS += -L/usr/local/lib -L/opt/local/lib + + CXXFLAGS += `sdl2-config --cflags` + CXXFLAGS += -I/usr/local/include -I/opt/local/include + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(OS), Windows_NT) + ECHO_MESSAGE = "MinGW" + LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2` + + CXXFLAGS += `pkg-config --cflags sdl2` + CFLAGS = $(CXXFLAGS) +endif + +##--------------------------------------------------------------------- +## BUILD RULES +##--------------------------------------------------------------------- + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:$(IMGUI_DIR)/backends/%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) diff --git a/examples/example_sdl_sdlrenderer/README.md b/examples/example_sdl_sdlrenderer/README.md new file mode 100644 index 00000000..5f431240 --- /dev/null +++ b/examples/example_sdl_sdlrenderer/README.md @@ -0,0 +1,25 @@ + +# How to Build + +- On Windows with Visual Studio's CLI + +``` +set SDL2_DIR=path_to_your_sdl2_folder +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib /subsystem:console +# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries +# or for 64-bit: +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL2.lib SDL2main.lib /subsystem:console +``` + +- On Linux and similar Unixes + +``` +c++ `sdl2-config --cflags` -I .. -I ../.. main.cpp ../../backends/imgui_impl_sdl.cpp ../../backends/imgui_impl_sdlrenderer.cpp ../../imgui*.cpp `sdl2-config --libs` -lGL +``` + +- On Mac OS X + +``` +brew install sdl2 +c++ `sdl2-config --cflags` -I .. -I ../.. main.cpp ../../backends/imgui_impl_sdl.cpp ../../backends/imgui_impl_sdlrenderer.cpp ../../imgui*.cpp `sdl2-config --libs` -framework OpenGl +``` diff --git a/examples/example_sdl_sdlrenderer/build_win32.bat b/examples/example_sdl_sdlrenderer/build_win32.bat new file mode 100644 index 00000000..6c1b5fde --- /dev/null +++ b/examples/example_sdl_sdlrenderer/build_win32.bat @@ -0,0 +1,8 @@ +@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. +@set OUT_DIR=Debug +@set OUT_EXE=example_sdl_sdlrenderer_ +@set INCLUDES=/I..\.. /I..\..\backends /I%SDL2_DIR%\include +@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp +@set LIBS=/LIBPATH:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib +mkdir %OUT_DIR% +cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj new file mode 100644 index 00000000..376a2275 --- /dev/null +++ b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj @@ -0,0 +1,181 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741} + example_sdl_sdlrenderer + 8.1 + + + + Application + true + MultiByte + v110 + + + Application + true + MultiByte + v110 + + + Application + false + true + MultiByte + v110 + + + Application + false + true + MultiByte + v110 + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + + + true + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + + + true + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + false + + + true + true + true + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + + + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) + false + + + true + true + true + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters new file mode 100644 index 00000000..01e98fcb --- /dev/null +++ b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj.filters @@ -0,0 +1,61 @@ + + + + + {20b90ce4-7fcb-4731-b9a0-075f875de82d} + + + {f18ab499-84e1-499f-8eff-9754361e0e52} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + imgui + + + imgui + + + imgui + + + sources + + + imgui + + + imgui + + + sources + + + sources + + + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + + sources + + + diff --git a/examples/example_sdl_sdlrenderer/main.cpp b/examples/example_sdl_sdlrenderer/main.cpp new file mode 100644 index 00000000..4fd04028 --- /dev/null +++ b/examples/example_sdl_sdlrenderer/main.cpp @@ -0,0 +1,168 @@ +// Dear ImGui: standalone example application for SDL2 + SDL_Renderer +// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) +// 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 + +// See imgui_impl_sdl.cpp for details. +// (Requires: SDL 2.0.17+) + +#include "imgui.h" +#include "imgui_impl_sdl.h" +#include "imgui_impl_sdlrenderer.h" +#include +#include + +#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 +# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +#endif + +// Main code +int main(int, char**) +{ + // Setup SDL + // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, + // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) + { + printf("Error: %s\n", SDL_GetError()); + return -1; + } + + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + + // Setup window + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Setup Platform/Renderer backends + ImGui_ImplSDL2_InitForSDLRenderer(window); + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + if (renderer == NULL) { + SDL_Log("Error creating SDL renderer"); + return false; + } else { + SDL_RendererInfo info; + SDL_GetRendererInfo(renderer, &info); + SDL_Log("Current SDL Renderer: %s", info.name); + } + + ImGui_ImplSDLRenderer_Init(renderer); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL2_ProcessEvent(&event); + if (event.type == SDL_QUIT) + done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + done = true; + } + + // Start the Dear ImGui frame + ImGui_ImplSDLRenderer_NewFrame(); + ImGui_ImplSDL2_NewFrame(window); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData()); + + SDL_RenderPresent(renderer); + } + + // Cleanup + ImGui_ImplSDLRenderer_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} From fba756176d659a8de8f711f68a4a04c675add942 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Sep 2021 19:29:39 +0200 Subject: [PATCH 078/332] Backends: SDL_Renderer: Amend 1d2d246, various tweaks, fixes, sync to latest. (#3926) --- backends/imgui_impl_sdl.cpp | 10 +- backends/imgui_impl_sdl.h | 2 +- backends/imgui_impl_sdlrenderer.cpp | 152 +++++++++--------- backends/imgui_impl_sdlrenderer.h | 12 +- docs/BACKENDS.md | 2 +- docs/CHANGELOG.txt | 2 + docs/EXAMPLES.md | 11 +- examples/example_sdl_sdlrenderer/README.md | 2 +- .../example_sdl_sdlrenderer.vcxproj | 12 +- examples/example_sdl_sdlrenderer/main.cpp | 45 +++--- 10 files changed, 126 insertions(+), 124 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 651fda51..b725e0ac 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -251,11 +251,6 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) return true; } -bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window) -{ - return ImGui_ImplSDL2_Init(window); -} - bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { IM_UNUSED(sdl_gl_context); // Viewport branch will need this. @@ -283,6 +278,11 @@ bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window) return ImGui_ImplSDL2_Init(window); } +bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window) +{ + return ImGui_ImplSDL2_Init(window); +} + void ImGui_ImplSDL2_Shutdown() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h index f85233af..d2439eb7 100644 --- a/backends/imgui_impl_sdl.h +++ b/backends/imgui_impl_sdl.h @@ -21,11 +21,11 @@ struct SDL_Window; typedef union SDL_Event SDL_Event; -IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window); IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index a268deec..4f564f47 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -1,15 +1,21 @@ -// dear imgui: Renderer Backend for SDL_Renderer, with Platform Backend SDL +// dear imgui: Renderer Backend for SDL_Renderer // (Requires: SDL 2.0.17+) +// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer +// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. + // Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// Missing features: +// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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 - // CHANGELOG -// 2021-16-03: Creation +// 2021-09-21: Initial version. #include "imgui.h" #include "imgui_impl_sdlrenderer.h" @@ -19,16 +25,17 @@ #include // intptr_t #endif -#include "SDL.h" - -#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 -# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +// SDL +#include +#if !SDL_VERSION_ATLEAST(2,0,17) +#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function #endif +// SDL_Renderer data struct ImGui_ImplSDLRenderer_Data { - SDL_Renderer *SDLRenderer; - SDL_Texture *FontTexture; + SDL_Renderer* SDLRenderer; + SDL_Texture* FontTexture; ImGui_ImplSDLRenderer_Data() { memset(this, 0, sizeof(*this)); } }; @@ -40,7 +47,7 @@ static ImGui_ImplSDLRenderer_Data* ImGui_ImplSDLRenderer_GetBackendData() } // Functions -bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer) +bool ImGui_ImplSDLRenderer_Init(SDL_Renderer* renderer) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); @@ -49,16 +56,18 @@ bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer) // Setup backend capabilities flags ImGui_ImplSDLRenderer_Data* bd = IM_NEW(ImGui_ImplSDLRenderer_Data)(); io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_SDLRenderer"; + io.BackendRendererName = "imgui_impl_sdlrenderer"; bd->SDLRenderer = renderer; + return true; } void ImGui_ImplSDLRenderer_Shutdown() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_DestroyDeviceObjects(); @@ -69,9 +78,10 @@ void ImGui_ImplSDLRenderer_Shutdown() static void ImGui_ImplSDLRenderer_SetupRenderState() { - ImGui_ImplSDLRenderer_Data *bd = ImGui_ImplSDLRenderer_GetBackendData(); + ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); - // Clear out any viewports and cliprects set by the user + // Clear out any viewports and cliprect set by the user + // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. SDL_RenderSetViewport(bd->SDLRenderer, NULL); SDL_RenderSetClipRect(bd->SDLRenderer, NULL); } @@ -81,9 +91,8 @@ void ImGui_ImplSDLRenderer_NewFrame() ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDLRenderer_Init()?"); - if (!bd->FontTexture) { + if (!bd->FontTexture) ImGui_ImplSDLRenderer_CreateDeviceObjects(); - } } void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) @@ -91,35 +100,33 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); // If there's a scale factor set by the user, use that instead - float rsX = 1.0f; - float rsY = 1.0f; - SDL_RenderGetScale(bd->SDLRenderer, &rsX, &rsY); - - ImVec2 renderScale; - // If the user has specified a scale factor to SDL_Renderer already (via SDL_RenderSetScale()), SDL will scale whatever we pass - // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. - renderScale.x = (rsX == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; - renderScale.y = (rsY == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; + // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass + // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here. + float rsx = 1.0f; + float rsy = 1.0f; + SDL_RenderGetScale(bd->SDLRenderer, &rsx, &rsy); + ImVec2 render_scale; + render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; + render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - int fb_width = (int)(draw_data->DisplaySize.x * renderScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * renderScale.y); + int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); + int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); if (fb_width == 0 || fb_height == 0) return; - // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports - ImVec2 clip_scale = renderScale; + ImVec2 clip_scale = render_scale; + // Render command lists ImGui_ImplSDLRenderer_SetupRenderState(); - - for (int n = 0; n < draw_data->CmdListsCount; n++) { - + for (int n = 0; n < draw_data->CmdListsCount; n++) + { const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; @@ -127,53 +134,39 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) { + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) ImGui_ImplSDLRenderer_SetupRenderState(); - } else { + else pcmd->UserCallback(cmd_list, pcmd); - } } else { // Project scissor/clipping rectangles into framebuffer space - ImVec4 clip_rect; - clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; - clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; - clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; - clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; - - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - SDL_Rect r; - r.x = clip_rect.x; - r.y = clip_rect.y; - r.w = clip_rect.z - clip_rect.x; - r.h = clip_rect.w - clip_rect.y; - - SDL_RenderSetClipRect(bd->SDLRenderer, &r); - - - int xy_stride = sizeof(ImDrawVert); - float *xy = (float *)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)); - - int uv_stride = sizeof(ImDrawVert); - float *uv = (float*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)); - - int col_stride = sizeof(ImDrawVert); - int *color = (int*)((char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)); - - SDL_Texture *tex = (SDL_Texture*)pcmd->TextureId; - - SDL_RenderGeometryRaw(bd->SDLRenderer, tex, - xy, xy_stride, color, - col_stride, - uv, uv_stride, - cmd_list->VtxBuffer.Size, - idx_buffer, pcmd->ElemCount, sizeof (ImDrawIdx)); - - } + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + continue; + + SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) }; + SDL_RenderSetClipRect(bd->SDLRenderer, &r); + + const float* xy = (const float*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)); + const float* uv = (const float*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)); + const int* color = (const int*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)); + + // Bind texture, Draw + SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + SDL_RenderGeometryRaw(bd->SDLRenderer, tex, + xy, (int)sizeof(ImDrawVert), + color, (int)sizeof(ImDrawVert), + uv, (int)sizeof(ImDrawVert), + cmd_list->VtxBuffer.Size, + idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx)); } - idx_buffer += pcmd->ElemCount; } } } @@ -181,19 +174,24 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) // Called by Init/NewFrame/Shutdown bool ImGui_ImplSDLRenderer_CreateFontsTexture() { - // Build texture atlas ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); + + // Build texture atlas unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); - if (bd->FontTexture == NULL) { + if (bd->FontTexture == NULL) + { SDL_Log("error creating texture"); return false; } SDL_UpdateTexture(bd->FontTexture, NULL, pixels, 4 * width); SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND); + // Store our identifier io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); @@ -204,7 +202,8 @@ void ImGui_ImplSDLRenderer_DestroyFontsTexture() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLRenderer_Data* bd = ImGui_ImplSDLRenderer_GetBackendData(); - if (bd->FontTexture) { + if (bd->FontTexture) + { io.Fonts->SetTexID(0); SDL_DestroyTexture(bd->FontTexture); bd->FontTexture = NULL; @@ -220,4 +219,3 @@ void ImGui_ImplSDLRenderer_DestroyDeviceObjects() { ImGui_ImplSDLRenderer_DestroyFontsTexture(); } - diff --git a/backends/imgui_impl_sdlrenderer.h b/backends/imgui_impl_sdlrenderer.h index 7b439618..8c67fa3a 100644 --- a/backends/imgui_impl_sdlrenderer.h +++ b/backends/imgui_impl_sdlrenderer.h @@ -1,13 +1,21 @@ -// dear imgui: Renderer Backend for SDL using SDL_Renderer +// dear imgui: Renderer Backend for SDL_Renderer // (Requires: SDL 2.0.17+) +// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer +// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. + +// Implemented features: +// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// Missing features: +// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. #pragma once #include "imgui.h" // IMGUI_IMPL_API struct SDL_Renderer; -IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer *renderer); +IMGUI_IMPL_API bool ImGui_ImplSDLRenderer_Init(SDL_Renderer* renderer); IMGUI_IMPL_API void ImGui_ImplSDLRenderer_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer_NewFrame(); IMGUI_IMPL_API void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index bbd66854..c878132b 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -75,7 +75,7 @@ List of Renderer Backends: imgui_impl_metal.mm ; Metal (with ObjC) imgui_impl_opengl2.cpp ; OpenGL 2 (legacy, fixed pipeline <- don't use with modern OpenGL context) imgui_impl_opengl3.cpp ; OpenGL 3/4, OpenGL ES 2, OpenGL ES 3 (modern programmable pipeline) - imgui_impl_sdlrenderer.cpp; use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath + imgui_impl_sdlrenderer.cpp; SDL_Renderer (optional component of SDL2 available from SDL 2.0.18+) imgui_impl_vulkan.cpp ; Vulkan imgui_impl_wgpu.cpp ; WebGPU diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b12ce98f..16334834 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -84,10 +84,12 @@ Other Changes: - Backends: Added more implicit asserts to detect invalid/redundant calls to Shutdown functions. (#4562) - Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] - Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] +- Backends: SDL_Renderer: Added SDL_Renderer backend compatible with upcoming SDL 2.0.18. (#3926) [@1bsyl] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via a direct unclipped PushClipRect() call. (#4464) - Backends: OSX: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. (#4557, #4563) [@lfnoise] - Backends: All renderers: Normalize clipping rect handling across backends. (#4464) +- Examples: Added SDL + SDL_Renderer example in "examples/example_sdl_sdlrenderer/" folder. (#3926) [@1bsyl] ----------------------------------------------------------------------- diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 69ecfa87..63b64d2e 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -130,11 +130,6 @@ This uses more modern OpenGL calls and custom shaders.
This may actually also work with OpenGL 2.x contexts!
Prefer using that if you are using modern OpenGL in your application (anything with shaders). -[example_sdl_sdlrenderer/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_sdlrenderer/)
-Use SDL2 and SDL_Renderer, any SDL backend (opengl, gles, gles2, d3d9, d3d11, metal or software) can be used underneath
-= main.cpp + imgui_impl_sdl.cpp + imgui_impl_sdlrenderer.cpp
-It requires SDL 2.0.17+ - [example_glfw_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_vulkan/)
GLFW (Win32, Mac, Linux) + Vulkan example.
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp
@@ -180,6 +175,12 @@ SDL2 (Win32, Mac, Linux, etc.) + OpenGL3+/ES2/ES3 example.
This uses more modern OpenGL calls and custom shaders.
This may actually also work with OpenGL 2.x contexts!
+[example_sdl_sdlrenderer/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_sdlrenderer/)
+SDL2 (Win32, Mac, Linux, etc.) + SDL_Renderer (most graphics backends are supported underneath)
+= main.cpp + imgui_impl_sdl.cpp + imgui_impl_sdlrenderer.cpp
+This requires SDL 2.0.17+ (expected to release November 2021)
+We do not really recommend using SDL_Renderer as it is a rather primitive API. + [example_sdl_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl_vulkan/)
SDL2 (Win32, Mac, Linux, etc.) + Vulkan example.
= main.cpp + imgui_impl_sdl.cpp + imgui_impl_vulkan.cpp
diff --git a/examples/example_sdl_sdlrenderer/README.md b/examples/example_sdl_sdlrenderer/README.md index 5f431240..209f15ac 100644 --- a/examples/example_sdl_sdlrenderer/README.md +++ b/examples/example_sdl_sdlrenderer/README.md @@ -6,7 +6,7 @@ ``` set SDL2_DIR=path_to_your_sdl2_folder cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib /subsystem:console -# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries +# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries # or for 64-bit: cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_sdlrenderer.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_sdlrenderer.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL2.lib SDL2main.lib /subsystem:console ``` diff --git a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj index 376a2275..4d502ab6 100644 --- a/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj +++ b/examples/example_sdl_sdlrenderer/example_sdl_sdlrenderer.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -28,27 +28,27 @@ Application true MultiByte - v110 + v140 Application true MultiByte - v110 + v140 Application false true MultiByte - v110 + v140 Application false true MultiByte - v110 + v140 @@ -178,4 +178,4 @@ - + \ No newline at end of file diff --git a/examples/example_sdl_sdlrenderer/main.cpp b/examples/example_sdl_sdlrenderer/main.cpp index 4fd04028..aee4a785 100644 --- a/examples/example_sdl_sdlrenderer/main.cpp +++ b/examples/example_sdl_sdlrenderer/main.cpp @@ -3,8 +3,9 @@ // 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 -// See imgui_impl_sdl.cpp for details. -// (Requires: SDL 2.0.17+) +// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer +// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. #include "imgui.h" #include "imgui_impl_sdl.h" @@ -12,8 +13,8 @@ #include #include -#if SDL_MAJOR_VERSION < 2 || SDL_MINOR_VERSION < 0 || SDL_PATCHLEVEL < 17 -# error Requires: SDL 2.0.17+ because of SDL_RenderGeometry function +#if !SDL_VERSION_ATLEAST(2,0,17) +#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function #endif // Main code @@ -28,18 +29,20 @@ int main(int, char**) return -1; } - // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2"); - // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); - // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); - // Setup window - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+SDL_Renderer example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); + + // Setup SDL_Renderer instance + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + if (renderer == NULL) + { + SDL_Log("Error creating SDL_Renderer!"); + return false; + } + //SDL_RendererInfo info; + //SDL_GetRendererInfo(renderer, &info); + //SDL_Log("Current SDL_Renderer: %s", info.name); // Setup Dear ImGui context IMGUI_CHECKVERSION(); @@ -54,17 +57,6 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForSDLRenderer(window); - - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); - if (renderer == NULL) { - SDL_Log("Error creating SDL renderer"); - return false; - } else { - SDL_RendererInfo info; - SDL_GetRendererInfo(renderer, &info); - SDL_Log("Current SDL Renderer: %s", info.name); - } - ImGui_ImplSDLRenderer_Init(renderer); // Load Fonts @@ -150,8 +142,9 @@ int main(int, char**) // Rendering ImGui::Render(); + SDL_SetRenderDrawColor(renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255)); + SDL_RenderClear(renderer); ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData()); - SDL_RenderPresent(renderer); } From ca097c2c681f45da0aba8e7da87731ebe95d0f3f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 22 Sep 2021 14:05:21 +0200 Subject: [PATCH 079/332] Docking: Fixed IsItemHovered() and functions depending on it (e.g. BeginPopupContextItem()) when called after Begin() on a docked window (#3851) Fix ee643b2a --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8f1d8151..4ec27526 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -154,6 +154,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. - Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) [@PathogenDavid] +- Docking: Fixed IsItemHovered() and functions depending on it (e.g. BeginPopupContextItem()) when + called after Begin() on a docked window (broken 2021/03/04). (#3851) - Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. The algorithm is still not handling the repartition of size idealy for nested sibling, but it got better. - Docking: Fixed settings load issue when mouse wheeling. (#4310) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 621ed2e0..d939fdda 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8158,6 +8158,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, TabBarCloseTab(tab_bar, tab); } + // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) + // That state is copied to window->DockTabItemStatusFlags by our caller. + if (docked_window && (hovered || g.HoveredId == close_button_id)) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + // Restore main window position so user can draw there if (want_clip_rect) PopClipRect(); From bbb95a5e0640825d97b017e55c72a690c96ddec5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Aug 2021 17:35:27 +0200 Subject: [PATCH 080/332] IO: modify io.AddFocusEvent() to tolerate in/out for multi-viewports. Amend 2f40be6. (merged from docking) (#3532) --- imgui.cpp | 22 ++++++++++++++++------ imgui.h | 4 +++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3a012e3b..a8140862 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1185,13 +1185,8 @@ void ImGuiIO::ClearInputCharacters() InputQueueCharacters.resize(0); } -void ImGuiIO::AddFocusEvent(bool focused) +void ImGuiIO::ClearInputKeys() { - if (focused) - return; - - // Clear buttons state when focus is lost - // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) memset(KeysDown, 0, sizeof(KeysDown)); for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++) KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f; @@ -1201,6 +1196,13 @@ void ImGuiIO::AddFocusEvent(bool focused) NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; } +void ImGuiIO::AddFocusEvent(bool focused) +{ + // We intentionally overwrite this and process in NewFrame(), in order to give a chance + // to multi-viewports backends to queue AddFocusEvent(false),AddFocusEvent(true) in same frame. + AppFocusLost = !focused; +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- @@ -4070,6 +4072,14 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Clear buttons state when focus is lost + // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + if (g.IO.AppFocusLost) + { + g.IO.ClearInputKeys(); + g.IO.AppFocusLost = false; + } + // Update keyboard input state // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools g.IO.KeyMods = GetMergedKeyModFlags(); diff --git a/imgui.h b/imgui.h index df08ea64..9ec667d1 100644 --- a/imgui.h +++ b/imgui.h @@ -1892,8 +1892,9 @@ struct ImGuiIO IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string - IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually IMGUI_API void AddFocusEvent(bool focused); // Notifies Dear ImGui when hosting platform windows lose or gain input focus + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -1941,6 +1942,7 @@ struct ImGuiIO float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + bool AppFocusLost; ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. From 24a77824f267ff61cb76a51241a4b470f8d9a4d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 22 Sep 2021 15:50:40 +0200 Subject: [PATCH 081/332] Added ClosePopupsExceptModals() helper, unused for now (aimed at user being able to close popups on app focus loss, not necessarily a suitable default) --- imgui.cpp | 19 +++++++++++++++++++ imgui_internal.h | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index a8140862..4968187f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4072,6 +4072,10 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Close popups on focus lost (currently wip/opt-in) + //if (g.IO.AppFocusLost) + // ClosePopupsExceptModals(); + // Clear buttons state when focus is lost // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) if (g.IO.AppFocusLost) @@ -8361,6 +8365,21 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } } +void ImGui::ClosePopupsExceptModals() +{ + ImGuiContext& g = *GImGui; + + int popup_count_to_keep; + for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) + { + ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; + if (!window || window->Flags & ImGuiWindowFlags_Modal) + break; + } + if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below + ClosePopupToLevel(popup_count_to_keep, true); +} + void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index 01a41953..9e2da8e8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2447,6 +2447,7 @@ namespace ImGui 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); + IMGUI_API void ClosePopupsExceptModals(); IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags); @@ -2454,9 +2455,9 @@ namespace ImGui IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); - IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); // Menus + IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); From fc4988ffb048f7d3ebcf9cb96bf7727878ad9d0e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Sep 2021 14:51:14 +0200 Subject: [PATCH 082/332] Added ImGuiFocusedFlags_NoPopupHierarchy and ImGuiHoveredFlags_NoPopupHierarchy (followup #4527) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 32 +++++++++++++++++++++++--------- imgui.h | 16 +++++++++------- imgui_demo.cpp | 35 ++++++++++++++++++++++++++--------- imgui_internal.h | 7 ++++--- 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 16334834..20e6caca 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,10 @@ Breaking Changes: Other Changes: - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) +- IsWindowFocused: Added ImGuiFocusedFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. +- IsWindowHovered: Added ImGuiHoveredFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. - InputText: Allow activating/cancelling/validating input with gamepad nav events. (#2321, #4552) diff --git a/imgui.cpp b/imgui.cpp index 4968187f..c385a154 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3888,7 +3888,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window, true)) clear_hovered_windows = true; // Disabled mouse? @@ -5778,9 +5778,11 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) window->RootWindow = parent_window->RootWindow; + if (parent_window && (flags & ImGuiWindowFlags_Popup)) + window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -6726,14 +6728,25 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) { - if (window->RootWindow == potential_parent) + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + return window; +} + +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy) +{ + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy); + if (window_root == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; + if (window == window_root) // end of chain + return false; window = window->ParentWindow; } return false; @@ -6765,13 +6778,13 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { IM_ASSERT(cur_window); // Not inside a Begin()/End() - + const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); bool result; if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window); + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy); else result = (ref_window == cur_window); if (!result) @@ -6798,11 +6811,12 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) return true; IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window); + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); else return (ref_window == cur_window); } diff --git a/imgui.h b/imgui.h index 9ec667d1..f679762e 100644 --- a/imgui.h +++ b/imgui.h @@ -1267,7 +1267,8 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! - //ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1280,12 +1281,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - //ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled + ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + 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, // Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3cc50a8f..febca565 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2186,7 +2186,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)")) + if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { // Select an item type const char* item_names[] = @@ -2272,43 +2272,63 @@ static void ShowDemoWindowWidgets() if (item_disabled) ImGui::EndDisabled(); + char buf[1] = ""; + ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) + { static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); // Testing IsWindowFocused() function with its various flags. - // Note that the ImGuiFocusedFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. - // Note that the ImGuiHoveredFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowHovered() = %d\n" "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); ImGui::BeginChild("child", ImVec2(0, 50), true); @@ -2317,9 +2337,6 @@ static void ShowDemoWindowWidgets() if (embed_all_inside_a_child_window) ImGui::EndChild(); - static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above."; - ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly); - // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu associated to the title bar of a window. static bool test_window = false; diff --git a/imgui_internal.h b/imgui_internal.h index 9e2da8e8..2fa71314 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1950,8 +1950,9 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window == Top-level window. + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. + 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. @@ -2337,7 +2338,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); From 65ad63de84bea27207721a79d93665f44d7fd150 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Sep 2021 13:58:00 +0200 Subject: [PATCH 083/332] Added ImGuiFocusedFlags_NoPopupHierarchy and ImGuiHoveredFlags_NoPopupHierarchy (followup #4527) IsWindowFocused: fix flag usage (amend 6b1e094c) was technically harmless because of light typing. --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 40 ++++++++++++++++++++++++++-------------- imgui.h | 16 +++++++++------- imgui_demo.cpp | 43 ++++++++++++++++++++++++++++++++----------- imgui_internal.h | 7 ++++--- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4ec27526..20dc4e6e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -116,6 +116,10 @@ Breaking Changes: Other Changes: - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) +- IsWindowFocused: Added ImGuiFocusedFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. +- IsWindowHovered: Added ImGuiHoveredFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. - InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing diff --git a/imgui.cpp b/imgui.cpp index 765e291b..f4f4964b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4031,7 +4031,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true)) + if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true, true)) clear_hovered_windows = true; // Disabled mouse? @@ -6137,13 +6137,15 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) { window->RootWindowDockTree = parent_window->RootWindowDockTree; if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) window->RootWindow = parent_window->RootWindow; } + if (parent_window && (flags & ImGuiWindowFlags_Popup)) + window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -7261,19 +7263,27 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy) { - if ((dock_hierarchy ? window->RootWindowDockTree : window->RootWindow) == potential_parent) + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + if (dock_hierarchy) + window = window->RootWindowDockTree; + return window; +} + +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy) +{ + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy); + if (window_root == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; - // 2021-09-11: we broke the unexpressed contract that this function (prior to 6b1e094, #4527) - // would follow through popup parents as well. Restoring this for now. May want to add a ImGuiFocusedFlags_PopupHierarchy flag later. - if ((window->Flags & ImGuiWindowFlags_Popup) == 0) - if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) - return false; + if (window == window_root) // end of chain + return false; window = window->ParentWindow; } return false; @@ -7305,14 +7315,15 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); bool result; if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window, dock_hierarchy); + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else result = (ref_window == cur_window); if (!result) @@ -7339,13 +7350,14 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) return true; IM_ASSERT(cur_window); // Not inside a Begin()/End() - const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; + const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; + const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window, dock_hierarchy); + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else return (ref_window == cur_window); } diff --git a/imgui.h b/imgui.h index e3e29e3c..cb49e1eb 100644 --- a/imgui.h +++ b/imgui.h @@ -1312,7 +1312,8 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! - ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1325,12 +1326,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled + ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + 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, // Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3165c42d..716d9834 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2238,7 +2238,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)")) + if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { // Select an item type const char* item_names[] = @@ -2324,51 +2324,75 @@ static void ShowDemoWindowWidgets() if (item_disabled) ImGui::EndDisabled(); + char buf[1] = ""; + ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) + { static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); // Testing IsWindowFocused() function with its various flags. - // Note that the ImGuiFocusedFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" + "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" - "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), - ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. - // Note that the ImGuiHoveredFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowHovered() = %d\n" "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" + "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); ImGui::BeginChild("child", ImVec2(0, 50), true); @@ -2377,9 +2401,6 @@ static void ShowDemoWindowWidgets() if (embed_all_inside_a_child_window) ImGui::EndChild(); - static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above."; - ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly); - // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu associated to the title bar of a window. // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties). diff --git a/imgui_internal.h b/imgui_internal.h index ae0c4efb..34a6924f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2153,8 +2153,9 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. + ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowDockTree; // Point to ourself or first ancestor that is not a child window. Cross through dock nodes. 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. @@ -2559,7 +2560,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); From 8f495e5543faa17e2e7946b306cf1bb1851d3773 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 16 Sep 2021 20:24:47 +0200 Subject: [PATCH 084/332] Internal: added ScrollToItem() upcoming replacement to ScrollToBringRectIntoView() + ImGuiScrollFlags (WIP) (#3692, #3208, #2812, #4242, #2900) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 100 ++++++++++++++++++++++++++++++++++----------- imgui.h | 2 +- imgui_internal.h | 33 +++++++++++++-- imgui_widgets.cpp | 2 +- 5 files changed, 108 insertions(+), 30 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 20e6caca..81b6c27b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,7 @@ Other Changes: the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. - Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) [@PathogenDavid] +- Nav: Improve scrolling behavior when navigating to an item larger than view. - TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to the TreePush(const char*) and TreePush(const void*) functions would use an hardcoded replacement. The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) diff --git a/imgui.cpp b/imgui.cpp index c385a154..0fb49420 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8008,32 +8008,80 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) return scroll; } +void ImGui::ScrollToItem(ImGuiScrollFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ScrollToRectEx(window, g.LastItemData.NavRect, flags); +} + +void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) +{ + ScrollToRectEx(window, item_rect, flags); +} + // Scroll to keep newly navigated item fully into view -ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect) +ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] - ImVec2 delta_scroll; - if (!window_rect.Contains(item_rect)) + // Check that only one behavior is selected per axis + IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); + IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_)); + + // Defaults + ImGuiScrollFlags in_flags = flags; + if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX) + flags |= ImGuiScrollFlags_KeepVisibleEdgeX; + if ((flags & ImGuiScrollFlags_MaskY_) == 0) + flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; + + const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; + const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; + const bool can_be_fully_visible_x = item_rect.GetWidth() <= window_rect.GetWidth(); + const bool can_be_fully_visible_y = item_rect.GetHeight() <= window_rect.GetHeight(); + + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) { - if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) - SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f); - else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) - SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); - if (item_rect.Min.y < window_rect.Min.y) - SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f); - else if (item_rect.Max.y >= window_rect.Max.y) - SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f); + if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x) + SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); + else if (item_rect.Max.x >= window_rect.Max.x) + SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); + } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) + { + float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x; + SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f); + } - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - delta_scroll = next_scroll - window->Scroll; + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) + { + if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y) + SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); + else if (item_rect.Max.y >= window_rect.Max.y) + SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) + { + float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y; + SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f); + } + + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + ImVec2 delta_scroll = next_scroll - window->Scroll; // Also scroll parent window to keep us into view if necessary - if (window->Flags & ImGuiWindowFlags_ChildWindow) - delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll)); + if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow)) + { + // FIXME-SCROLL: May be an option? + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX; + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY; + delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags); + } return delta_scroll; } @@ -9026,7 +9074,7 @@ bool ImGui::NavMoveRequestButNoResultYet() } // FIXME: ScoringRect is not set -void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags) +void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); @@ -9035,6 +9083,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM g.NavMoveDirForDebug = move_dir; g.NavMoveClipDir = clip_dir; g.NavMoveFlags = move_flags; + g.NavMoveScrollFlags = scroll_flags; g.NavMoveForwardToNextFrame = false; g.NavMoveKeyMods = g.IO.KeyMods; g.NavMoveResultLocal.Clear(); @@ -9051,7 +9100,7 @@ void ImGui::NavMoveRequestCancel() } // Forward will reuse the move request again on the next frame (generally with modifications done to it) -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags) +void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavMoveForwardToNextFrame == false); @@ -9060,6 +9109,7 @@ void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNav g.NavMoveDir = move_dir; g.NavMoveClipDir = clip_dir; g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded; + g.NavMoveScrollFlags = scroll_flags; } // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire @@ -9422,6 +9472,7 @@ void ImGui::NavUpdateCreateMoveRequest() // Initiate directional inputs request g.NavMoveDir = ImGuiDir_None; g.NavMoveFlags = ImGuiNavMoveFlags_None; + g.NavMoveScrollFlags = ImGuiScrollFlags_None; if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; @@ -9454,7 +9505,7 @@ void ImGui::NavUpdateCreateMoveRequest() // Submit g.NavMoveForwardToNextFrame = false; if (g.NavMoveDir != ImGuiDir_None) - NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags); + NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) if (g.NavMoveSubmitted && g.NavId == 0) @@ -9534,8 +9585,9 @@ void ImGui::NavMoveRequestApplyResult() if (g.NavLayer == ImGuiNavLayer_Main) { ImVec2 delta_scroll; - if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdge) + if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { + // FIXME: Should remove this float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; delta_scroll.y = result->Window->Scroll.y - scroll_target; SetScrollY(result->Window, scroll_target); @@ -9543,7 +9595,7 @@ void ImGui::NavMoveRequestApplyResult() else { ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); - delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs); + delta_scroll = ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); } // Offset our result position so mouse position can be applied immediately after in NavUpdate() @@ -9670,13 +9722,13 @@ static float ImGui::NavUpdatePageUpPageDown() else if (home_pressed) { // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y - // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. + // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. // Preserve current horizontal position if we have any. nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Down; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; // FIXME-NAV: MoveClipDir left to _None, intentional? } else if (end_pressed) @@ -9685,7 +9737,7 @@ static float ImGui::NavUpdatePageUpPageDown() if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Up; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; // FIXME-NAV: MoveClipDir left to _None, intentional? } return nav_scoring_rect_offset_y; @@ -9756,7 +9808,7 @@ static void ImGui::NavEndFrame() if (do_forward) { window->NavRectRel[g.NavLayer] = bb_rel; - NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags); + NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } } } diff --git a/imgui.h b/imgui.h index f679762e..5a34d225 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18417 +#define IMGUI_VERSION_NUM 18418 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 2fa71314..3f6c48c9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -152,6 +152,7 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // F typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions +typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() @@ -1175,6 +1176,21 @@ enum ImGuiActivateFlags_ ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) }; +// Early work-in-progress API for ScrollToItem() +enum ImGuiScrollFlags_ +{ + ImGuiScrollFlags_None = 0, + ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis] + ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible] + ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used] + ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis + ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used] + ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) + ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). + ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, + ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY +}; + enum ImGuiNavHighlightFlags_ { ImGuiNavHighlightFlags_None = 0, @@ -1201,7 +1217,7 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) - ImGuiNavMoveFlags_ScrollToEdge = 1 << 6, + 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 }; @@ -1536,6 +1552,7 @@ struct ImGuiContext bool NavMoveScoringItems; // Move request submitted, still scoring incoming items bool NavMoveForwardToNextFrame; ImGuiNavMoveFlags NavMoveFlags; + ImGuiScrollFlags NavMoveScrollFlags; ImGuiKeyModFlags NavMoveKeyMods; ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) ImGuiDir NavMoveDirForDebug; @@ -1742,6 +1759,7 @@ struct ImGuiContext NavMoveScoringItems = false; NavMoveForwardToNextFrame = false; NavMoveFlags = ImGuiNavMoveFlags_None; + NavMoveScrollFlags = ImGuiScrollFlags_None; NavMoveKeyMods = ImGuiKeyModFlags_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavScoringDebugCount = 0; @@ -2390,7 +2408,14 @@ namespace ImGui IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); - IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect); + + // Early work-in-progress API (ScrollToItem() will become public) + IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0); + IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); + IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); +//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); } +//#endif // Basic Accessors inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) @@ -2471,8 +2496,8 @@ namespace ImGui IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); IMGUI_API void NavInitRequestApplyResult(); IMGUI_API bool NavMoveRequestButNoResultYet(); - IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); - IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags); + 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 NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9e045e9c..155ebcc9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6716,7 +6716,7 @@ void ImGui::EndMenuBar() SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. g.NavDisableMouseHover = g.NavMousePosDirty = true; - NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags); // Repeat + NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat } } From 677fe33990ac02d925da3d5a929bbbb6f01800bd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 30 Sep 2021 11:53:20 +0200 Subject: [PATCH 085/332] Backends: DX12: use dxgiformat.h to fix using forward declared DXGI_FORMAT (#4594) --- backends/imgui_impl_dx12.h | 14 ++------------ docs/FONTS.md | 6 +++--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index cd35584c..899c8229 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -9,20 +9,15 @@ // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. // This define is set in the example .vcxproj file and need to be replicated in your app or by adding it to your imconfig.h file. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs #pragma once #include "imgui.h" // IMGUI_IMPL_API +#include // DXGI_FORMAT -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4471) // a forward declaration of an unscoped enumeration must have an underlying type -#endif - -enum DXGI_FORMAT; struct ID3D12Device; struct ID3D12DescriptorHeap; struct ID3D12GraphicsCommandList; @@ -42,8 +37,3 @@ IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3 // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); - -#ifdef _MSC_VER -#pragma warning (pop) -#endif - diff --git a/docs/FONTS.md b/docs/FONTS.md index 760877e8..09c1cb9a 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -27,11 +27,11 @@ In the [misc/fonts/](https://github.com/ocornut/imgui/tree/master/misc/fonts) fo --------------------------------------- ## Readme First -- All loaded fonts glyphs are rendered into a single texture atlas ahead of time. Calling either of `io.Fonts->GetTexDataAsAlpha8()`, `io.Fonts->GetTexDataAsRGBA32()` or `io.Fonts->Build()` will build the atlas. +- You can use the `Metrics/Debugger` window (available in `Demo>Tools`) to browse your fonts and understand what's going on if you have an issue. You can also reach it in `Demo->Tools->Style Editor->Fonts`. The same information are also available in the Style Editor under Fonts. -- You can use the style editor `ImGui::ShowStyleEditor()` in the "Fonts" section to browse your fonts and understand what's going on if you have an issue. You can also reach it in `Demo->Tools->Style Editor->Fonts`: +![imgui_capture_0008](https://user-images.githubusercontent.com/8225057/135429892-0e41ef8d-33c5-4991-bcf6-f997a0bcfd6b.png) -![imgui_capture_0008](https://user-images.githubusercontent.com/8225057/84162822-1a731f00-aa71-11ea-9b6b-89c2332aa161.png) +- All loaded fonts glyphs are rendered into a single texture atlas ahead of time. Calling either of `io.Fonts->GetTexDataAsAlpha8()`, `io.Fonts->GetTexDataAsRGBA32()` or `io.Fonts->Build()` will build the atlas. - Make sure your font ranges data are persistent (available during the calls to `GetTexDataAsAlpha8()`/`GetTexDataAsRGBA32()/`Build()`. From 164a86d3b0d7af4afb6092e580838ec1b4694733 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 5 Oct 2021 22:06:12 +0200 Subject: [PATCH 086/332] Moved !IsActiveIdUsingKey(ImGuiKey_Tab) check to UpdateTabFocus(). Added asserts to IsItemHovered() and IsWindowHovered(). --- imgui.cpp | 8 ++++---- imgui.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0fb49420..a15559ea 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3199,7 +3199,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function // Test if we are hovering the right window (our window could be behind another window) // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) @@ -3317,7 +3317,7 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. // (Note that we can always TAB out of a widget that doesn't allow tabbing in) - if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL) + if (g.ActiveId == id && g.TabFocusPressed && g.TabFocusRequestNextWindow == NULL) { g.TabFocusRequestNextWindow = window; g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. @@ -3839,7 +3839,7 @@ void ImGui::UpdateTabFocus() ImGuiContext& g = *GImGui; // Pressing TAB activate widget focus - g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); + g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && !IsActiveIdUsingKey(ImGuiKey_Tab)); if (g.ActiveId == 0 && g.TabFocusPressed) { // - This path is only taken when no widget are active/tabbed-into yet. @@ -6768,7 +6768,7 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function + IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; ImGuiWindow* ref_window = g.HoveredWindow; ImGuiWindow* cur_window = g.CurrentWindow; diff --git a/imgui.h b/imgui.h index 5a34d225..11a40961 100644 --- a/imgui.h +++ b/imgui.h @@ -1286,8 +1286,8 @@ 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, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // Return true even if the item is disabled + 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_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; From 30db6741471294eeef414c32e7fe33783dad36db Mon Sep 17 00:00:00 2001 From: Guus Waals <_@guusw.nl> Date: Fri, 17 Sep 2021 18:14:13 +0200 Subject: [PATCH 087/332] Fix printf format warnings on mingw clang (#4626, #4183, #3592) --- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 81b6c27b..ef9f4f00 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -80,6 +80,7 @@ Other Changes: - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] - Misc: Added asserts for missing PopItemFlag() calls. +- Misc: Fixed printf-style format checks on Clang+MinGW. (#4626, #4183, #3592) [@guusw] - IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) This allows apps to receive the click on void when that click is used to close popup (by default, clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). diff --git a/imgui.h b/imgui.h index 11a40961..c2fdad8b 100644 --- a/imgui.h +++ b/imgui.h @@ -92,7 +92,7 @@ Index of this file: #endif // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) +#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))) #elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) From e443ea139ddcee75fea326375198dda1e85ce8ff Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 Oct 2021 13:41:29 +0200 Subject: [PATCH 088/332] Backends: SDL_Renderer: Backup and restore modified ClipRect/Viewport. (#3926, #4582) --- backends/imgui_impl_sdlrenderer.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index 4f564f47..ff163113 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -15,6 +15,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG +// 2021-10-06: Backup and restore modified ClipRect/Viewport. // 2021-09-21: Initial version. #include "imgui.h" @@ -115,6 +116,16 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) if (fb_width == 0 || fb_height == 0) return; + // Backup SDL_Renderer state that will be modified to restore it afterwards + struct BackupSDLRendererState + { + SDL_Rect Viewport; + SDL_Rect ClipRect; + }; + BackupSDLRendererState old = {}; + SDL_RenderGetViewport(bd->SDLRenderer, &old.Viewport); + SDL_RenderGetClipRect(bd->SDLRenderer, &old.ClipRect); + // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports ImVec2 clip_scale = render_scale; @@ -169,6 +180,10 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) } } } + + // Restore modified SDL_Renderer state + SDL_RenderSetViewport(bd->SDLRenderer, &old.Viewport); + SDL_RenderSetClipRect(bd->SDLRenderer, &old.ClipRect); } // Called by Init/NewFrame/Shutdown From 2de96c4bd581fbe3d8892864f7e3c360309869f9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 30 Sep 2021 12:15:34 +0200 Subject: [PATCH 089/332] Stack Tool: Added Stack Tool (ShowStackToolWindow() function and available from Demo and Metrics window). (#4631) --- docs/CHANGELOG.txt | 4 + imconfig.h | 2 +- imgui.cpp | 290 +++++++++++++++++++++++++++++++++++---------- imgui.h | 3 +- imgui_demo.cpp | 5 + imgui_internal.h | 41 +++++-- imgui_tables.cpp | 1 + imgui_widgets.cpp | 4 +- 8 files changed, 274 insertions(+), 76 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ef9f4f00..4fea7c3c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,6 +42,10 @@ Breaking Changes: Other Changes: +- Debug: Stack Tool: Added "Stack Tool" available in "Demo->Examples->Stack Tool", "Metrics->Tools", + or by calling the ShowStackToolWindow() function. The tool run queries on hovered id to display + details about individual components that were hashed to create an ID. It helps understanding + the ID stack system and debugging potential ID collisions. (#4631) [@ocornut, @rokups] - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) - IsWindowFocused: Added ImGuiFocusedFlags_NoPopupHierarchy flag allowing to exclude child popups from the tested windows when combined with _ChildWindows. diff --git a/imconfig.h b/imconfig.h index a0c86caa..7082c550 100644 --- a/imconfig.h +++ b/imconfig.h @@ -33,7 +33,7 @@ // It is very strongly recommended to NOT disable the demo windows during development. 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. Not recommended. -//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty. +//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() 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) diff --git a/imgui.cpp b/imgui.cpp index a15559ea..eaa96969 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -82,6 +82,7 @@ CODE // [SECTION] VIEWPORTS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) */ @@ -924,16 +925,17 @@ static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); static int FindWindowFocusIndex(ImGuiWindow* window); -// Error Checking +// Error Checking and Debug Tools static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); +static void UpdateDebugToolItemPicker(); +static void UpdateDebugToolStackQueries(); // Misc static void UpdateSettings(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateTabFocus(); -static void UpdateDebugToolItemPicker(); 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 void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); @@ -2967,10 +2969,9 @@ 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); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2979,10 +2980,9 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2991,10 +2991,9 @@ ImGuiID ImGuiWindow::GetID(int n) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -3002,10 +3001,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -3013,10 +3011,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -3024,10 +3021,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -4159,8 +4155,9 @@ void ImGui::NewFrame() g.ItemFlagsStack.push_back(ImGuiItemFlags_None); g.GroupStack.resize(0); - // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. + // [DEBUG] Update debug features UpdateDebugToolItemPicker(); + UpdateDebugToolStackQueries(); // 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. @@ -4173,31 +4170,6 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. -void ImGui::UpdateDebugToolItemPicker() -{ - ImGuiContext& g = *GImGui; - g.DebugItemPickerBreakId = 0; - if (g.DebugItemPickerActive) - { - const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - SetMouseCursor(ImGuiMouseCursor_Hand); - if (IsKeyPressedMap(ImGuiKey_Escape)) - g.DebugItemPickerActive = false; - if (IsMouseClicked(0) && hovered_id) - { - g.DebugItemPickerBreakId = hovered_id; - g.DebugItemPickerActive = false; - } - SetNextWindowBgAlpha(0.60f); - BeginTooltip(); - Text("HoveredId: 0x%08X", hovered_id); - Text("Press ESC to abort picking."); - TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); - EndTooltip(); - } -} - void ImGui::Initialize(ImGuiContext* context) { ImGuiContext& g = *context; @@ -7184,6 +7156,8 @@ void ImGui::PushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); window->IDStack.push_back(id); } @@ -7193,11 +7167,10 @@ void ImGui::PushOverrideID(ImGuiID id) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE + KeepAliveID(id); ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -11226,22 +11199,23 @@ namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } void ImGui::ShowMetricsWindow(bool* p_open) { + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; + if (cfg->ShowStackTool) + ShowStackToolWindow(&cfg->ShowStackTool); + if (!Begin("Dear ImGui Metrics/Debugger", p_open)) { End(); return; } - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - // 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 active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); - Text("%d active allocations", io.MetricsActiveAllocations); + Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations); //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } Separator(); @@ -11295,11 +11269,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Tools if (TreeNode("Tools")) { - // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. - if (Button("Item Picker..")) - DebugStartItemPicker(); + // Stack Tool is your best friend! + Checkbox("Show stack tool", &cfg->ShowStackTool); 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."); + MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); @@ -11317,8 +11290,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } Unindent(); } - Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); - Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); Checkbox("Show tables rectangles", &cfg->ShowTablesRects); SameLine(); @@ -11365,6 +11336,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } + // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. + if (Button("Item Picker..")) + DebugStartItemPicker(); + 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."); + TreePop(); } @@ -11378,6 +11355,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); 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]; @@ -12002,6 +11981,186 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la TreePop(); } +//----------------------------------------------------------------------------- +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) +//----------------------------------------------------------------------------- + +// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. +void ImGui::UpdateDebugToolItemPicker() +{ + ImGuiContext& g = *GImGui; + g.DebugItemPickerBreakId = 0; + if (!g.DebugItemPickerActive) + return; + + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + SetMouseCursor(ImGuiMouseCursor_Hand); + if (IsKeyPressedMap(ImGuiKey_Escape)) + g.DebugItemPickerActive = false; + if (IsMouseClicked(0) && hovered_id) + { + g.DebugItemPickerBreakId = hovered_id; + g.DebugItemPickerActive = false; + } + SetNextWindowBgAlpha(0.60f); + BeginTooltip(); + Text("HoveredId: 0x%08X", hovered_id); + Text("Press ESC to abort picking."); + TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + EndTooltip(); +} + +// [DEBUG] Stack Tool: update queries. Called by NewFrame() +void ImGui::UpdateDebugToolStackQueries() +{ + ImGuiContext& g = *GImGui; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Clear hook when stack tool is not visible + g.DebugHookIdInfo = 0; + if (g.FrameCount != tool->LastActiveFrame + 1) + return; + + // Update queries. The steps are: -1: query Stack, >= 0: query each stack item + // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time + const ImGuiID query_id = g.ActiveId ? g.ActiveId : g.HoveredIdPreviousFrame; + if (tool->QueryId != query_id) + { + tool->QueryId = query_id; + tool->StackLevel = -1; + tool->Results.resize(0); + } + if (query_id == 0) + return; + + // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) + int stack_level = tool->StackLevel; + if (stack_level >= 0 && stack_level < tool->Results.Size) + if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2) + tool->StackLevel++; + + // Update hook + stack_level = tool->StackLevel; + if (stack_level == -1) + g.DebugHookIdInfo = query_id; + if (stack_level >= 0 && stack_level < tool->Results.Size) + { + g.DebugHookIdInfo = tool->Results[stack_level].ID; + tool->Results[stack_level].QueryFrameCount++; + } +} + +// [DEBUG] 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; + + // Step 0: stack query + // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget. + if (tool->StackLevel == -1) + { + tool->StackLevel++; + tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); + for (int n = 0; n < window->IDStack.Size + 1; n++) + tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; + return; + } + + // Step 1+: query for individual level + IM_ASSERT(tool->StackLevel >= 0); + if (tool->StackLevel != window->IDStack.Size) + return; + ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel]; + IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); + + int data_len; + switch (data_type) + { + case ImGuiDataType_S32: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id); + break; + case ImGuiDataType_String: + data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id); + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id); + break; + case ImGuiDataType_Pointer: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id); + break; + case ImGuiDataType_ID: + if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id); + break; + default: + IM_ASSERT(0); + } + info->QuerySuccess = true; +} + +// Stack Tool: Display UI +void ImGui::ShowStackToolWindow(bool* p_open) +{ + if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) + { + End(); + return; + } + + // Display hovered/active status + ImGuiContext& g = *GImGui; + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + const ImGuiID active_id = g.ActiveId; +#ifdef IMGUI_ENABLE_TEST_ENGINE + Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : ""); +#else + Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id); +#endif + SameLine(); + MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); + + // Display decorated stack + ImGuiStackTool* tool = &g.DebugStackTool; + tool->LastActiveFrame = g.FrameCount; + if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) + { + const float id_width = CalcTextSize("0xDDDDDDDD").x; + TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); + TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); + TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); + TableHeadersRow(); + for (int n = 0; n < tool->Results.Size; n++) + { + ImGuiStackLevelInfo* info = &tool->Results[n]; + TableNextColumn(); + Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); + + TableNextColumn(); + ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL; + if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) + Text("\"%s\" [window]", window->Name); + else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) + TextUnformatted(info->Desc); + else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. + { +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo() + Text("??? \"%s\"", label); + else +#endif + TextUnformatted("???"); + } + + TableNextColumn(); + Text("0x%08X", info->ID); + if (n == tool->Results.Size - 1) + TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); + } + EndTable(); + } + End(); +} + #else void ImGui::ShowMetricsWindow(bool*) {} @@ -12017,7 +12176,12 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} void ImGui::DebugNodeViewport(ImGuiViewportP*) {} -#endif +void ImGui::ShowStackToolWindow(bool*) {} +void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} +void ImGui::UpdateDebugToolItemPicker() {} +void ImGui::UpdateDebugToolStackQueries() {} + +#endif // #ifndef IMGUI_DISABLE_METRICS_WINDOW //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index c2fdad8b..f66fd95b 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18418 +#define IMGUI_VERSION_NUM 18419 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -307,6 +307,7 @@ namespace ImGui // Demo, Debug, Information 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 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 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. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index febca565..69779574 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -294,10 +294,12 @@ void ImGui::ShowDemoWindow(bool* p_open) // Dear ImGui Apps (accessible from the "Tools" menu) static bool show_app_metrics = false; + static bool show_app_stack_tool = false; static bool show_app_style_editor = false; static bool show_app_about = false; if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } + if (show_app_stack_tool) { ImGui::ShowStackToolWindow(&show_app_stack_tool); } if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); } if (show_app_style_editor) { @@ -382,7 +384,10 @@ void ImGui::ShowDemoWindow(bool* p_open) //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! if (ImGui::BeginMenu("Tools")) { +#ifndef IMGUI_DISABLE_METRICS_WINDOW ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); + ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool); +#endif ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); ImGui::EndMenu(); diff --git a/imgui_internal.h b/imgui_internal.h index 3f6c48c9..1149da65 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -24,7 +24,7 @@ Index of this file: // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow @@ -1382,11 +1382,12 @@ struct ImGuiSettingsHandler }; //----------------------------------------------------------------------------- -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- struct ImGuiMetricsConfig { + bool ShowStackTool; bool ShowWindowsRects; bool ShowWindowsBeginOrder; bool ShowTablesRects; @@ -1397,6 +1398,7 @@ struct ImGuiMetricsConfig ImGuiMetricsConfig() { + ShowStackTool = false; ShowWindowsRects = false; ShowWindowsBeginOrder = false; ShowTablesRects = false; @@ -1407,6 +1409,27 @@ struct ImGuiMetricsConfig } }; +struct ImGuiStackLevelInfo +{ + ImGuiID ID; + ImS8 QueryFrameCount; // >= 1: Query in progress + bool QuerySuccess; // Obtained result from DebugHookIdInfo() + char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) + + ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } +}; + +// State for Stack tool queries +struct ImGuiStackTool +{ + int LastActiveFrame; + int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level + ImGuiID QueryId; // ID to query details for + ImVector Results; + + ImGuiStackTool() { memset(this, 0, sizeof(*this)); } +}; + //----------------------------------------------------------------------------- // [SECTION] Generic context hooks //----------------------------------------------------------------------------- @@ -1448,7 +1471,6 @@ struct ImGuiContext bool WithinEndChild; // Set within EndChild() bool GcCompactAll; // Request full GC bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() - ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID() void* TestEngine; // Test engine user data // Windows state @@ -1468,6 +1490,7 @@ struct ImGuiContext float WheelingWindowTimer; // 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 HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; @@ -1671,8 +1694,9 @@ struct ImGuiContext // Debug Tools bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) - ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id + ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; + ImGuiStackTool DebugStackTool; // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. @@ -1697,7 +1721,6 @@ struct ImGuiContext WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; GcCompactAll = false; TestEngineHookItems = false; - TestEngineHookIdInfo = 0; TestEngine = NULL; WindowsActiveCount = 0; @@ -1708,6 +1731,7 @@ struct ImGuiContext WheelingWindow = NULL; WheelingWindowTimer = 0.0f; + DebugHookIdInfo = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdAllowOverlap = false; HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; @@ -2713,6 +2737,7 @@ namespace ImGui inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } 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 DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); @@ -2758,14 +2783,12 @@ IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table #ifdef IMGUI_ENABLE_TEST_ENGINE extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end); extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); + #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); #else #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0) #endif diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 945e739a..f9ed5ce2 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -564,6 +564,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables) // + 1 (for table->RawData allocated below) // + 1 (for table->ColumnsNames, if names are used) +// Shared allocations per number of nested tables // + 1 (for table->Splitter._Channels) // + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) // Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 155ebcc9..c4f1ceb5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1799,7 +1799,7 @@ 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++) { - PushID((void*)(intptr_t)i); + PushID(i); const bool item_selected = (i == *current_item); const char* item_text; if (!items_getter(data, i, &item_text)) @@ -6017,7 +6017,7 @@ void ImGui::TreePushOverrideID(ImGuiID id) ImGuiWindow* window = g.CurrentWindow; Indent(); window->DC.TreeDepth++; - window->IDStack.push_back(id); + PushOverrideID(id); } void ImGui::TreePop() From 25332fa5ac77ae29de8572d2c16b1dbccd2a3ca7 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Sun, 5 Sep 2021 23:40:01 +0200 Subject: [PATCH 090/332] Backends: Emscripten: Update the webgpu API to be compatible with chrome 4633 (#4512) --- backends/imgui_impl_wgpu.cpp | 10 +++++----- docs/CHANGELOG.txt | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index d3bf28ba..20b6bb22 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -281,7 +281,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(uint32_ static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture) { - WGPUBindGroupEntry image_bg_entries[] = { { 0, 0, 0, 0, 0, texture } }; + WGPUBindGroupEntry image_bg_entries[] = { { nullptr, 0, 0, 0, 0, 0, texture } }; WGPUBindGroupDescriptor image_bg_descriptor = {}; image_bg_descriptor.layout = layout; @@ -478,7 +478,7 @@ static void ImGui_ImplWGPU_CreateFontsTexture() tex_desc.sampleCount = 1; tex_desc.format = WGPUTextureFormat_RGBA8Unorm; tex_desc.mipLevelCount = 1; - tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_Sampled; + tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding; g_resources.FontTexture = wgpuDeviceCreateTexture(g_wgpuDevice, &tex_desc); WGPUTextureViewDescriptor tex_view_desc = {}; @@ -571,7 +571,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() WGPUVertexBufferLayout buffer_layouts[1]; buffer_layouts[0].arrayStride = sizeof(ImDrawVert); - buffer_layouts[0].stepMode = WGPUInputStepMode_Vertex; + buffer_layouts[0].stepMode = WGPUVertexStepMode_Vertex; buffer_layouts[0].attributeCount = 3; buffer_layouts[0].attributes = attribute_desc; @@ -624,8 +624,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() WGPUBindGroupEntry common_bg_entries[] = { - { 0, g_resources.Uniforms, 0, sizeof(Uniforms), 0, 0 }, - { 1, 0, 0, 0, g_resources.Sampler, 0 }, + { nullptr, 0, g_resources.Uniforms, 0, sizeof(Uniforms), 0, 0 }, + { nullptr, 1, 0, 0, 0, g_resources.Sampler, 0 }, }; WGPUBindGroupDescriptor common_bg_descriptor = {}; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4fea7c3c..63a6e344 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -93,7 +93,7 @@ Other Changes: - Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) - Backends: Added more implicit asserts to detect invalid/redundant calls to Shutdown functions. (#4562) - Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] -- Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] +- Backends: WebGPU: Fixed for latest specs. (#4472, #4512) [@Kangz, @bfierz] - Backends: SDL_Renderer: Added SDL_Renderer backend compatible with upcoming SDL 2.0.18. (#3926) [@1bsyl] - Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via a direct unclipped PushClipRect() call. (#4464) From fbe78b1a3a71b90bfaed656a0cfff29886f11f7c Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 Oct 2021 18:16:43 +0200 Subject: [PATCH 091/332] Stack Tool: Docs (#4631) + also prevent Metrics window from displaying twice. --- docs/CHANGELOG.txt | 2 +- docs/FAQ.md | 14 ++++++++++---- imgui.cpp | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 63a6e344..7fd044c2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,7 +42,7 @@ Breaking Changes: Other Changes: -- Debug: Stack Tool: Added "Stack Tool" available in "Demo->Examples->Stack Tool", "Metrics->Tools", +- Debug: Stack Tool: Added "Stack Tool" available in "Demo->Tools->Stack Tool", "Metrics->Tools", or by calling the ShowStackToolWindow() function. The tool run queries on hovered id to display details about individual components that were hashed to create an ID. It helps understanding the ID stack system and debugging potential ID collisions. (#4631) [@ocornut, @rokups] diff --git a/docs/FAQ.md b/docs/FAQ.md index ec90ed46..479bce06 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -23,7 +23,7 @@ or view this file with any Markdown viewer. | [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-clipping-or-disappearing-when-i-move-windows-around) | | [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) | | **Q&A: Usage** | -| **[Why is my widget not reacting when I click on it?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?](#q-why-is-my-widget-not-reacting-when-i-click-on-it)** | +| **[About the ID Stack system..
Why is my widget not reacting when I click on it?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?](#q-about-the-id-stack-system)** | | [How can I display an image? What is ImTextureID, how does it work?](#q-how-can-i-display-an-image-what-is-imtextureid-how-does-it-work)| | [How can I use my own math types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-math-types-instead-of-imvec2imvec4) | | [How can I interact with standard C++ types (such as std::string and std::vector)?](#q-how-can-i-interact-with-standard-c-types-such-as-stdstring-and-stdvector) | @@ -186,17 +186,23 @@ Refer to rendering backends in the [examples/](https://github.com/ocornut/imgui/ # Q&A: Usage +### Q: About the ID Stack system... ### Q: Why is my widget not reacting when I click on it? ### Q: How can I have widgets with an empty label? ### Q: How can I have multiple widgets with the same label? A primer on labels and the ID Stack... -Dear ImGui internally need to uniquely identify UI elements. +Dear ImGui internally needs to uniquely identify UI elements. Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. Interactive widgets (such as calls to Button buttons) need a unique ID. -Unique ID are used internally to track active widgets and occasionally associate state to widgets. -Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element. + +**Unique ID are used internally to track active widgets and occasionally associate state to widgets.
+Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.** + +Since Dear ImGui 1.85 you can use `Demo>Tools>Stack Tool` or call `ImGui::ShowStackToolWindow()`. The tool display intermediate values leading to the creation of a unique ID, making things easier to debug and understand. + +![Stack tool](https://user-images.githubusercontent.com/8225057/136235657-a0ea5665-dcd1-423f-9be6-dc3f8ced8f12.png) - Unique ID are often derived from a string label and at minimum scoped within their host window: ```cpp diff --git a/imgui.cpp b/imgui.cpp index eaa96969..3a5cb95c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11205,7 +11205,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (cfg->ShowStackTool) ShowStackToolWindow(&cfg->ShowStackTool); - if (!Begin("Dear ImGui Metrics/Debugger", p_open)) + if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; From 31d033c9d8d64fb9047cdcd731890f1fb5f85db2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 Oct 2021 18:33:01 +0200 Subject: [PATCH 092/332] Nav: refactor SetKeyboardFocusHere() into using Nav facility. Fix it for clipped items. (#343, #4079, #2352, #432) + Removed references to counter used by previous implementation of SetKeyboardFocus functions (the TabStop ones will be removed after) --- docs/CHANGELOG.txt | 7 ++++ imgui.cpp | 96 +++++++++++++++++++++++++++++++++------------- imgui.h | 2 +- imgui_internal.h | 28 +++++++------- imgui_widgets.cpp | 37 ++++++++++-------- 5 files changed, 113 insertions(+), 57 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7fd044c2..6c3b9a32 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -57,6 +57,13 @@ Other Changes: - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) +- Nav: Fixed using SetKeyboardFocusHere() on non-visible/clipped items. It now works and will scroll + toward the item. When called during a frame where the parent window is appearing, scrolling will + aim to center the item in the window. When calling during a frame where the parent window is already + visible, scrolling will aim to scroll as little as possible to make the item visible. We will later + expose scroll functions and flags in public API to select those behaviors. (#343, #4079, #2352) +- Nav: Fixed using SetKeyboardFocusHere() from activating a different item on the next frame if + submitted items have changed during that frame. (#432) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when the NavEnableSetMousePos config flag is set. - Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when diff --git a/imgui.cpp b/imgui.cpp index 3a5cb95c..ffda92e3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3303,7 +3303,6 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) // Increment counters // FIXME: ImGuiItemFlags_Disabled should disable more. const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->DC.FocusCounterRegular++; if (is_tab_stop) { window->DC.FocusCounterTabStop++; @@ -3322,11 +3321,6 @@ void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) // Handle focus requests if (g.TabFocusRequestCurrWindow == window) { - if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular) - { - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByCode; - return; - } if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) { g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor @@ -3843,7 +3837,6 @@ void ImGui::UpdateTabFocus() // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also // manipulate the Next fields here even though they will be turned into Curr fields below. g.TabFocusRequestNextWindow = g.NavWindow; - g.TabFocusRequestNextCounterRegular = INT_MAX; if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0); else @@ -3852,17 +3845,15 @@ void ImGui::UpdateTabFocus() // Turn queued focus request into current one g.TabFocusRequestCurrWindow = NULL; - g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX; + g.TabFocusRequestCurrCounterTabStop = INT_MAX; if (g.TabFocusRequestNextWindow != NULL) { ImGuiWindow* window = g.TabFocusRequestNextWindow; g.TabFocusRequestCurrWindow = window; - if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) - g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); g.TabFocusRequestNextWindow = NULL; - g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX; + g.TabFocusRequestNextCounterTabStop = INT_MAX; } g.NavIdTabCounter = INT_MAX; @@ -6316,7 +6307,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrentColumns = NULL; window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; + window->DC.FocusCounterTabStop = -1; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -7082,12 +7073,16 @@ void ImGui::PopFocusScope() void ImGui::SetKeyboardFocusHere(int offset) { - IM_ASSERT(offset >= -1); // -1 is allowed but not below ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - g.TabFocusRequestNextWindow = window; - g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; - g.TabFocusRequestNextCounterTabStop = INT_MAX; + IM_ASSERT(offset >= -1); // -1 is allowed but not below + g.NavWindow = window; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_None, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + if (offset == -1) + NavMoveRequestResolveWithLastItem(); + else + g.NavTabbingInputableRemaining = offset + 1; } void ImGui::SetItemDefaultFocus() @@ -8980,6 +8975,7 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) result->Window = window; result->ID = g.LastItemData.ID; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; + result->InFlags = g.LastItemData.InFlags; result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); } @@ -9014,18 +9010,30 @@ 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) { + if (item_flags & ImGuiItemFlags_Inputable) + g.NavTabbingInputableRemaining--; + if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) { 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); + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + { + if (g.NavTabbingInputableRemaining == 0) + NavMoveRequestResolveWithLastItem(); + } + else + { + 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); + } } } @@ -9051,6 +9059,10 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); + + if (move_flags & ImGuiNavMoveFlags_Tabbing) + move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; + g.NavMoveSubmitted = g.NavMoveScoringItems = true; g.NavMoveDir = move_dir; g.NavMoveDirForDebug = move_dir; @@ -9059,12 +9071,21 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM g.NavMoveScrollFlags = scroll_flags; g.NavMoveForwardToNextFrame = false; g.NavMoveKeyMods = g.IO.KeyMods; + g.NavTabbingInputableRemaining = 0; g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultOther.Clear(); NavUpdateAnyRequestFlag(); } +void ImGui::NavMoveRequestResolveWithLastItem() +{ + ImGuiContext& g = *GImGui; + g.NavMoveScoringItems = false; // Ensure request doesn't need more processing + NavApplyItemToResult(&g.NavMoveResultLocal); + NavUpdateAnyRequestFlag(); +} + void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; @@ -9284,6 +9305,7 @@ static void ImGui::NavUpdate() // Process navigation move request if (g.NavMoveSubmitted) NavMoveRequestApplyResult(); + g.NavTabbingInputableRemaining = 0; g.NavMoveSubmitted = g.NavMoveScoringItems = false; // Apply application mouse position movement, after we had a chance to process move request result. @@ -9535,7 +9557,9 @@ void ImGui::NavMoveRequestApplyResult() // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) if (result == NULL) { - if (g.NavId != 0) + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) { g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -9590,9 +9614,27 @@ 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); SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); + // 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; + } + + // Activate + if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_None; + } + // Enable nav highlight - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; + if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + { + g.NavDisableHighlight = false; + g.NavDisableMouseHover = g.NavMousePosDirty = true; + } } // Process NavCancel input (to close a popup, get back to parent, clear focus) diff --git a/imgui.h b/imgui.h index f66fd95b..246e8e28 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18419 +#define IMGUI_VERSION_NUM 18420 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 1149da65..d1a8258d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -759,9 +759,7 @@ 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_FocusedByCode = 1 << 8, // Set when the Focusable item just got focused from code. - ImGuiItemStatusFlags_FocusedByTabbing = 1 << 9, // Set when the Focusable item just got focused by Tabbing. - ImGuiItemStatusFlags_Focused = ImGuiItemStatusFlags_FocusedByCode | ImGuiItemStatusFlags_FocusedByTabbing + ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] @@ -1219,7 +1217,10 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) 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 + ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result + ImGuiNavMoveFlags_Tabbing = 1 << 9, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_Activate = 1 << 10, + ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 11 // Do not alter the visible state of keyboard vs mouse nav highlight }; enum ImGuiNavLayer @@ -1235,12 +1236,13 @@ struct ImGuiNavItemData ImGuiID ID; // Init,Move // Best candidate item ID ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags 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 ImGuiNavItemData() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; RectRel = ImRect(); DistBox = DistCenter = DistAxial = FLT_MAX; } + void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; } }; //----------------------------------------------------------------------------- @@ -1582,6 +1584,7 @@ struct ImGuiContext ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. int NavScoringDebugCount; // Metrics for debugging + int NavTabbingInputableRemaining; // >0 when counting items for tabbing ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) @@ -1597,9 +1600,7 @@ struct ImGuiContext // Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!) ImGuiWindow* TabFocusRequestCurrWindow; // ImGuiWindow* TabFocusRequestNextWindow; // - int TabFocusRequestCurrCounterRegular; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index - int TabFocusRequestNextCounterRegular; // Stored for next frame int TabFocusRequestNextCounterTabStop; // " bool TabFocusPressed; // Set in NewFrame() when user pressed Tab @@ -1787,14 +1788,15 @@ struct ImGuiContext NavMoveKeyMods = ImGuiKeyModFlags_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavScoringDebugCount = 0; + NavTabbingInputableRemaining = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL; - TabFocusRequestCurrCounterRegular = TabFocusRequestCurrCounterTabStop = INT_MAX; - TabFocusRequestNextCounterRegular = TabFocusRequestNextCounterTabStop = INT_MAX; + TabFocusRequestCurrCounterTabStop = INT_MAX; + TabFocusRequestNextCounterTabStop = INT_MAX; TabFocusPressed = false; DimBgRatio = 0.0f; @@ -1901,7 +1903,6 @@ 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() - int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase when ImGuiItemFlags_Inputable (FIXME-NAV: Needs redesign) int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // Local parameters stacks @@ -2478,9 +2479,9 @@ namespace ImGui #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Currently refactoring focus/nav/tabbing system // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): - // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool focused = FocusableItemRegister(...)' - // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' - // (New) IMGUI_VERSION_NUM >= 18411: using 'ItemAdd(..., ImGuiItemAddFlags_Inputable)' and 'bool focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (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.NavActivateInputId == id' (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 @@ -2522,6 +2523,7 @@ namespace ImGui IMGUI_API bool NavMoveRequestButNoResultYet(); 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(); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c4f1ceb5..af62ccd4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2407,17 +2407,17 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); - if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) + if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); if (temp_input_allowed) - if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) temp_input_is_active = true; } @@ -3025,15 +3025,15 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0; + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (focus_requested || clicked || g.NavActivateId == id || g.NavActivateInputId == id) + if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) + if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) temp_input_is_active = true; } } @@ -4024,21 +4024,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); - const bool focus_requested_by_code = (item_status_flags & ImGuiItemStatusFlags_FocusedByCode) != 0; - const bool focus_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && (g.NavActivateInputId == id || g.NavActivateId == id); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); - bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + bool select_all = false; float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (user_clicked || user_scroll_finish || user_nav_input_start || focus_requested_by_code || focus_requested_by_tabbing); + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing); const bool init_state = (init_make_active || user_scroll_active); if ((init_state && g.ActiveId != id) || init_changed_specs) { @@ -4074,13 +4072,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->ID = id; state->ScrollX = 0.0f; stb_textedit_initialize_state(&state->Stb, !is_multiline); - if (!is_multiline && focus_requested_by_code) + } + + 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)) select_all = true; } + if (flags & ImGuiInputTextFlags_AlwaysOverwrite) state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) - if (!is_multiline && (focus_requested_by_tabbing || (user_clicked && io.KeyCtrl))) - select_all = true; } if (g.ActiveId != id && init_make_active) @@ -4214,7 +4219,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // 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. if (io.InputQueueCharacters.Size > 0) { - if (!ignore_char_inputs && !is_readonly && !user_nav_input_start) + if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering From 30a6873a44a0d2d2c833d9edfa247f16a71657e3 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Thu, 7 Oct 2021 14:58:09 +0200 Subject: [PATCH 093/332] Backends: WebGPU: Fix missing initialization of local variable (#4634) --- backends/imgui_impl_wgpu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 20b6bb22..2f621ed8 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -270,7 +270,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(uint32_ spirv_desc.codeSize = binary_data_size; spirv_desc.code = binary_data; - WGPUShaderModuleDescriptor desc; + WGPUShaderModuleDescriptor desc = {}; desc.nextInChain = reinterpret_cast(&spirv_desc); WGPUProgrammableStageDescriptor stage_desc = {}; From dde31030e9fea67a848180f328986f2db79464f7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 Oct 2021 15:07:43 +0200 Subject: [PATCH 094/332] Backends: SDL_Renderer: use SDL_RenderIsClipEnabled() to pass NULL to SDL_RenderSetClipRect (#3926, #4582, #4633) --- backends/imgui_impl_sdlrenderer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index ff163113..bfc2b676 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -120,9 +120,11 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) struct BackupSDLRendererState { SDL_Rect Viewport; + bool ClipEnabled; SDL_Rect ClipRect; }; BackupSDLRendererState old = {}; + old.ClipEnabled = SDL_RenderIsClipEnabled(bd->SDLRenderer); SDL_RenderGetViewport(bd->SDLRenderer, &old.Viewport); SDL_RenderGetClipRect(bd->SDLRenderer, &old.ClipRect); @@ -183,7 +185,7 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) // Restore modified SDL_Renderer state SDL_RenderSetViewport(bd->SDLRenderer, &old.Viewport); - SDL_RenderSetClipRect(bd->SDLRenderer, &old.ClipRect); + SDL_RenderSetClipRect(bd->SDLRenderer, old.ClipEnabled ? &old.ClipRect : NULL); } // Called by Init/NewFrame/Shutdown From ec6ca06898d380142c77b3e73da06e5c082a0f60 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 8 Oct 2021 13:00:47 +0200 Subject: [PATCH 095/332] InputText: Fixed selection rectangle appearing one frame late when selecting all. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6c3b9a32..8ed1a40e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,6 +54,7 @@ Other Changes: - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. - InputText: Allow activating/cancelling/validating input with gamepad nav events. (#2321, #4552) +- InputText: Fixed selection rectangle appearing one frame late when selecting all. - TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) - Disabled: Added assert guard for mismatching BeginDisabled()/EndDisabled() blocks. (#211) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index af62ccd4..585820cb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4118,7 +4118,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Lock the decision of whether we are going to take the path displaying the cursor or selection const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); - bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; bool enter_pressed = false; From 44f801186fd9e8e9c3ed87358eadae0f19032c85 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 Oct 2021 18:25:29 +0200 Subject: [PATCH 096/332] SetItemDefaultFocus() use ScrollToRectEx(), don't tab when Alt is held either, TabItemEx() uses ItemAdd's extra_flags, misc comments. --- docs/TODO.txt | 3 +-- imgui.cpp | 27 ++++++++++++++++----------- imgui_internal.h | 2 +- imgui_widgets.cpp | 8 ++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index dff2d24d..95a9326f 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -326,6 +326,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: some features such as PageUp/Down/Home/End should probably work without ImGuiConfigFlags_NavEnableKeyboard? (where do we draw the line?) - nav: configuration flag to disable global shortcuts (currently only CTRL-Tab) ? ! nav: never clear NavId on some setup (e.g. gamepad centric) + - nav: scroll up/down if possible when move request fails - nav: there's currently no way to completely clear focus with the keyboard. depending on patterns used by the application to dispatch inputs, it may be desirable. - nav: code to focus child-window on restoring NavId appears to have issue: e.g. when focus change is implicit because of window closure. - nav: Home/End behavior when navigable item is not fully visible at the edge of scrolling? should be backtrack to keep item into view? @@ -353,8 +354,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav/windowing: when CTRL-Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering. - nav/windowing: Resizing window will currently fail with certain types of resizing constraints/callback applied - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787, #343) - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) diff --git a/imgui.cpp b/imgui.cpp index ffda92e3..efabebc2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3829,7 +3829,10 @@ void ImGui::UpdateTabFocus() ImGuiContext& g = *GImGui; // Pressing TAB activate widget focus - g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && !IsActiveIdUsingKey(ImGuiKey_Tab)); + g.TabFocusPressed = false; + if (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + if (!g.IO.KeyCtrl && !g.IO.KeyAlt && IsKeyPressedMap(ImGuiKey_Tab) && !IsActiveIdUsingKey(ImGuiKey_Tab)) + g.TabFocusPressed = true; if (g.ActiveId == 0 && g.TabFocusPressed) { // - This path is only taken when no widget are active/tabbed-into yet. @@ -7091,15 +7094,17 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent) - { - g.NavInitRequest = false; - g.NavInitResultId = g.LastItemData.ID; - g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos); - NavUpdateAnyRequestFlag(); - if (!IsItemVisible()) - SetScrollHereY(); - } + if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent) + return; + + g.NavInitRequest = false; + g.NavInitResultId = g.LastItemData.ID; + g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos); + NavUpdateAnyRequestFlag(); + + // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) + if (!IsItemVisible()) + ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); } void ImGui::SetStateStorage(ImGuiStorage* tree) @@ -9323,7 +9328,7 @@ static void ImGui::NavUpdate() } g.NavIdIsAlive = false; g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) diff --git a/imgui_internal.h b/imgui_internal.h index d1a8258d..291fcaec 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -744,7 +744,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window 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_Inputable = 1 << 8 // false // [WIP] Auto-activate item when focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. }; // Storage for LastItem data diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 585820cb..c138b8cf 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7879,9 +7879,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if (p_open && !*p_open) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); return false; } @@ -7948,9 +7946,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. if (tab_appearing && (!tab_bar_appearing || tab_is_new)) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); if (is_tab_button) return false; return tab_contents_visible; From ba5c105c0194e667f80194c7bc8f0f5c78eaa8a9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 Oct 2021 11:37:31 +0200 Subject: [PATCH 097/332] Menus: Fixed an assertion happening in some situations when closing nested menus (broken in 1.83). (#4640) Broken since 936f5322 Weirdly chain-reaction caused by the fact following #4640 repro, the SourceWindow assignment in OpenPopupEx() picks Menu_04 before its closure. Value of SourceWindow since bda2cde6 #2517 --- docs/CHANGELOG.txt | 6 ++++-- imgui.cpp | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8ed1a40e..19d4c1fb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -86,9 +86,11 @@ Other Changes: RGB<>HSV round trips every frames. [@rokups] - ColorPicker4: Fixed picker being unable to select exact 1.0f color when dragging toward the edges of the SV square (previously picked 0.999989986f). (#3517) [@rokups] -- Menus: Fixed vertical alignments of MenuItem() calls within a menu bar. (broken in 1.84). (#4538) -- Menus: Adjust closing logic to accomodate for varying font size and dpi. +- Menus: Fixed vertical alignments of MenuItem() calls within a menu bar (broken in 1.84). (#4538) +- Menus: Improve closing logic when moving diagonally in empty between between parent and child menus to + accomodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). +- Menus: Fixed an assertion happening in some situations when closing nested menus (broken in 1.83). (#4640) - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) - PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] - Misc: Added asserts for missing PopItemFlag() calls. diff --git a/imgui.cpp b/imgui.cpp index efabebc2..38fcb930 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6551,8 +6551,18 @@ void ImGui::FocusWindow(ImGuiWindow* window) void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - - const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1; + int start_idx = g.WindowsFocusOrder.Size - 1; + if (under_this_window != NULL) + { + // Aim at root window behind us, if we are in a child window that's our own root (see #4640) + int offset = -1; + while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow) + { + under_this_window = under_this_window->ParentWindow; + offset = 0; + } + start_idx = FindWindowFocusIndex(under_this_window) + offset; + } for (int i = start_idx; i >= 0; i--) { // 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. @@ -9838,6 +9848,7 @@ static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) ImGuiContext& g = *GImGui; IM_UNUSED(g); int order = window->FocusOrder; + IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); IM_ASSERT(g.WindowsFocusOrder[order] == window); return order; } From 75c54e63844d262b1005865eba370ad487808b1f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 Oct 2021 14:40:42 +0200 Subject: [PATCH 098/332] Nav: Fixed vertical scoring offset when wrapping on Y in a decorated window. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 19d4c1fb..cd9a40cf 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -74,6 +74,7 @@ Other Changes: the PressedOnClick/PressedOnDoubleClick/PressedOnRelease button policy. - Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) [@PathogenDavid] +- Nav: Fixed vertical scoring offset when wrapping on Y in a decorated window. - Nav: Improve scrolling behavior when navigating to an item larger than view. - TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to the TreePush(const char*) and TreePush(const void*) functions would use an hardcoded replacement. diff --git a/imgui.cpp b/imgui.cpp index 38fcb930..de0fb746 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9814,10 +9814,11 @@ static void ImGui::NavEndFrame() } do_forward = true; } + const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) { bb_rel.Min.y = bb_rel.Max.y = - ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; + ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y + decoration_up_height; if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); @@ -9827,7 +9828,7 @@ static void ImGui::NavEndFrame() } if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; + bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y + decoration_up_height; if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); From 55d35d8387c15bf0cfd71861df67af8cfbda7456 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 Oct 2021 19:03:37 +0200 Subject: [PATCH 099/332] Version 1.85 --- docs/CHANGELOG.txt | 16 +++++++++++----- imgui.cpp | 6 +++--- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cd9a40cf..0bb5855f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -31,14 +31,20 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.85 WIP (In Progress) + VERSION 1.85 (Released 2021-10-12) ----------------------------------------------------------------------- +This is the last release officially supporting C++03 and Visual Studio 2008/2010. (#4537) +We expect that the next release will require a subset of the C++11 language (VS 2012~, GCC 4.8.1, Clang 3.3). +We may use some C++11 language features but we will not use any C++ library headers. +If you are stuck on ancient compiler you may need to stay at this version onward. + Breaking Changes: - Removed GetWindowContentRegionWidth() function. keep inline redirection helper. Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not very useful in practice, and the only use of it in the demo was illfit. + Using 'GetContentRegionAvail().x' is generally a better choice. Other Changes: @@ -66,7 +72,7 @@ Other Changes: - Nav: Fixed using SetKeyboardFocusHere() from activating a different item on the next frame if submitted items have changed during that frame. (#432) - Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when - the NavEnableSetMousePos config flag is set. + the ImGuiConfigFlags_NavEnableSetMousePos config flag is set. - Nav: Fixed a few widgets from not setting reference keyboard/gamepad navigation ID when activated with mouse. More specifically: BeginTabItem(), the scrolling arrows of BeginTabBar(), the arrow section of TreeNode(), the +/- buttons of InputInt()/InputFloat(), Selectable() with @@ -77,9 +83,9 @@ Other Changes: - Nav: Fixed vertical scoring offset when wrapping on Y in a decorated window. - Nav: Improve scrolling behavior when navigating to an item larger than view. - TreePush(): removed unnecessary/inconsistent legacy behavior where passing a NULL value to - the TreePush(const char*) and TreePush(const void*) functions would use an hardcoded replacement. + the TreePush(const char*) and TreePush(const void*) functions would use an hard-coded replacement. The only situation where that change would make a meaningful difference is TreePush((const char*)NULL) - (_explicitely_ casting a null pointer to const char*), which is unlikely and will now crash. + (_explicitly_ casting a null pointer to const char*), which is unlikely and will now crash. You may replace it with anything else. - ColorEdit4: Fixed not being able to change hue when saturation is 0. (#4014) [@rokups] - ColorEdit4: Fixed hue resetting to 0 when it is set to 255. [@rokups] @@ -89,7 +95,7 @@ Other Changes: of the SV square (previously picked 0.999989986f). (#3517) [@rokups] - Menus: Fixed vertical alignments of MenuItem() calls within a menu bar (broken in 1.84). (#4538) - Menus: Improve closing logic when moving diagonally in empty between between parent and child menus to - accomodate for varying font size and dpi. + accommodate for varying font size and dpi. - Menus: Fixed crash when navigating left inside a child window inside a sub-menu. (#4510). - Menus: Fixed an assertion happening in some situations when closing nested menus (broken in 1.83). (#4640) - Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) diff --git a/imgui.cpp b/imgui.cpp index de0fb746..e6f206e7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.85 // (main code and documentation) // Help: @@ -380,7 +380,7 @@ 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. - - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead. + - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder @@ -9849,7 +9849,7 @@ static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) ImGuiContext& g = *GImGui; IM_UNUSED(g); int order = window->FocusOrder; - IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); + IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking) IM_ASSERT(g.WindowsFocusOrder[order] == window); return order; } diff --git a/imgui.h b/imgui.h index 246e8e28..f2606398 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.85 // (headers) // Help: @@ -63,8 +63,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.85 WIP" -#define IMGUI_VERSION_NUM 18420 +#define IMGUI_VERSION "1.85" +#define IMGUI_VERSION_NUM 18500 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 69779574..d4bac484 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.85 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 0abc75f3..30b100bb 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.85 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 291fcaec..fa5dec3e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.85 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! diff --git a/imgui_tables.cpp b/imgui_tables.cpp index f9ed5ce2..56056ae3 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.85 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c138b8cf..7da069b6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 WIP +// dear imgui, v1.85 // (widgets code) /* From be03b8ff93ab9c9f9524af056fc0fc73ba26ff66 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 12 Oct 2021 17:23:39 +0300 Subject: [PATCH 100/332] Misc: Fix build with MinGW when using IMGUI_API. --- imgui.h | 2 +- imgui_internal.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.h b/imgui.h index f2606398..496e1430 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85" -#define IMGUI_VERSION_NUM 18500 +#define IMGUI_VERSION_NUM 18501 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index fa5dec3e..bd3edad5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2192,7 +2192,7 @@ struct ImGuiTableCellData }; // FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData -struct ImGuiTable +struct IMGUI_API ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; @@ -2298,14 +2298,14 @@ struct ImGuiTable bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis - IMGUI_API ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } - IMGUI_API ~ImGuiTable() { IM_FREE(RawData); } + ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } + ~ImGuiTable() { IM_FREE(RawData); } }; // 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. -struct ImGuiTableTempData +struct IMGUI_API ImGuiTableTempData { int TableIndex; // Index in g.Tables.Buf[] pool float LastTimeActive; // Last timestamp this structure was used @@ -2322,7 +2322,7 @@ struct ImGuiTableTempData float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() - IMGUI_API ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } + ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } }; // sizeof() ~ 12 From 29653273c1c6cec453be68d9cbe625f5d5c69fe8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 13 Oct 2021 16:45:43 +0200 Subject: [PATCH 101/332] Docking: reinstate io.ConfigDockingWithShift option. (#4643) This more or less reverts commit 3ed07a8f0b18c5dfc94a997bde2417ee0453b0fd. --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 11 ++++++----- imgui.h | 6 ++++-- imgui_demo.cpp | 15 ++++++++++++--- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d2fd846d..9600efa9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,15 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.86 WIP (In Progress) +----------------------------------------------------------------------- + +Docking+Viewports Branch: + +- Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) + + ----------------------------------------------------------------------- VERSION 1.85 (Released 2021-10-12) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 9e0cbae9..ee110c13 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1124,6 +1124,7 @@ ImGuiIO::ImGuiIO() // Docking options (when ImGuiConfigFlags_DockingEnable is set) ConfigDockingNoSplit = false; + ConfigDockingWithShift = false; ConfigDockingAlwaysTabBar = false; ConfigDockingTransparentPayload = false; @@ -6836,7 +6837,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it. - if (g.MovingWindow == window && g.IO.KeyShift == false) + if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) if ((window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0) BeginDockableDragDropSource(window); @@ -14781,9 +14782,9 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN } } - // We only allow and preview docking when hovering over a drop rect or over the title bar + // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); - if (!is_explicit_target && !data->IsSplitDirExplicit) + if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift) data->IsDropAllowed = false; // Calculate split area @@ -16078,7 +16079,7 @@ void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) g.LastItemData.ID = window->MoveId; window = window->RootWindowDockTree; IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); - bool is_drag_docking = ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit + bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) { SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); @@ -16139,7 +16140,7 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) } const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); - const bool is_explicit_target = IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); + const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. diff --git a/imgui.h b/imgui.h index d04b5e8e..604b3c2a 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85" -#define IMGUI_VERSION_NUM 18500 +#define IMGUI_VERSION_NUM 18501 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch @@ -805,8 +805,9 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! - // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. + // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking/undocking. // - Drag from window menu button (upper-left button) to undock an entire node (all windows). + // - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to _enable_ docking/undocking. // About dockspaces: // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. // - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. @@ -1922,6 +1923,7 @@ struct ImGuiIO // Docking options (when ImGuiConfigFlags_DockingEnable is set) bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. + bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigDockingAlwaysTabBar; // = false // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node. bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1ee3d7cc..da82bcc6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -465,12 +465,18 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable); - ImGui::SameLine(); HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); + ImGui::SameLine(); + if (io.ConfigDockingWithShift) + HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to enable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); + else + HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGui::Indent(); ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit); ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars."); + ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); + ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)"); ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar); ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows."); ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload); @@ -5848,6 +5854,7 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent"); if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit"); + if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar"); if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); @@ -7475,6 +7482,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. // - Drag from window menu button (upper-left button) to undock an entire node (all windows). +// - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to _enable_ docking/undocking. // About dockspaces: // - Use DockSpace() to create an explicit dock node _within_ an existing window. // - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. @@ -7579,8 +7587,9 @@ void ShowExampleAppDockSpace(bool* p_open) "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n" "- Drag from window title bar or their tab to dock/undock." "\n" "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n" - "- Hold SHIFT to disable docking." "\n" - "This demo app has nothing to do with it!" "\n\n" + "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n" + "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)" "\n" + "This demo app has nothing to do with enabling docking!" "\n\n" "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window." "\n\n" "Read comments in ShowExampleAppDockSpace() for more details."); From 13cdf2ff0e554d2097841e742a26180440c69173 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 12 Oct 2021 18:49:24 +0300 Subject: [PATCH 102/332] CI: Add MinGW DLL builds, --- .github/workflows/build.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f01bff87..fc8cbba7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,6 +51,20 @@ jobs: - name: Build example_null (extra warnings, mingw 64-bit) run: mingw32-make -C examples/example_null WITH_EXTRA_WARNINGS=1 + - name: Build example_null (mingw 64-bit, as DLL) + shell: bash + run: | + echo '#ifdef _EXPORT' > example_single_file.cpp + echo '# define IMGUI_API __declspec(dllexport)' >> example_single_file.cpp + echo '#else' >> example_single_file.cpp + echo '# define IMGUI_API __declspec(dllimport)' >> example_single_file.cpp + echo '#endif' >> example_single_file.cpp + echo '#define IMGUI_IMPLEMENTATION' >> example_single_file.cpp + echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp + g++ -I. -Wall -Wformat -D_EXPORT -shared -o libimgui.dll -Wl,--out-implib,libimgui.a example_single_file.cpp -limm32 + g++ -I. -Wall -Wformat -o example_null.exe examples/example_null/main.cpp -L. -limgui + rm -f example_null.exe libimgui.* example_single_file.* + - name: Build example_null (extra warnings, msvc 64-bit) shell: cmd run: | From e3bd9434b1a7255040bbfa104a3697e71bc649af Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 14 Oct 2021 16:58:14 +0200 Subject: [PATCH 103/332] 1.86 WIP + internals: tweaks table temp data code. --- docs/CHANGELOG.txt | 12 ++++++++++++ imgui.cpp | 10 +++++----- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 10 +++++----- imgui_tables.cpp | 14 ++++++-------- imgui_widgets.cpp | 2 +- 8 files changed, 34 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0bb5855f..2ff3373a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -30,6 +30,18 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.86 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +Other Changes: + +- Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] +- CI: Add MinGW DLL build to test suite. [@rokups] + + ----------------------------------------------------------------------- VERSION 1.85 (Released 2021-10-12) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index e6f206e7..63682668 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 +// dear imgui, v1.86 WIP // (main code and documentation) // Help: @@ -4130,9 +4130,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.TablesTempDataStack.Size; i++) - if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time) - TableGcCompactTransientBuffers(&g.TablesTempDataStack[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]); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; @@ -4246,7 +4246,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.ShrinkWidthBuffer.clear(); g.Tables.Clear(); - g.TablesTempDataStack.clear_destruct(); + g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); g.ClipboardHandlerData.clear(); diff --git a/imgui.h b/imgui.h index 496e1430..cd41d540 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.85 +// dear imgui, v1.86 WIP // (headers) // Help: @@ -63,8 +63,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.85" -#define IMGUI_VERSION_NUM 18501 +#define IMGUI_VERSION "1.86 WIP" +#define IMGUI_VERSION_NUM 18502 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d4bac484..6af064af 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 +// dear imgui, v1.86 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 30b100bb..86bc0f2b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 +// dear imgui, v1.86 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index bd3edad5..0447b5aa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.85 +// dear imgui, v1.86 WIP // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -1629,9 +1629,9 @@ struct ImGuiContext // Table ImGuiTable* CurrentTable; - int CurrentTableStackIdx; - ImPool Tables; - ImVector TablesTempDataStack; + 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 ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; @@ -1815,7 +1815,7 @@ struct ImGuiContext memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); CurrentTable = NULL; - CurrentTableStackIdx = -1; + TablesTempDataStacked = 0; CurrentTabBar = NULL; TempInputId = 0; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 56056ae3..1c611809 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 +// dear imgui, v1.86 WIP // (tables and columns code) /* @@ -340,10 +340,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // Acquire temporary buffers const int table_idx = g.Tables.GetIndex(table); - g.CurrentTableStackIdx++; - if (g.CurrentTableStackIdx + 1 > g.TablesTempDataStack.Size) - g.TablesTempDataStack.resize(g.CurrentTableStackIdx + 1, ImGuiTableTempData()); - ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempDataStack[g.CurrentTableStackIdx]; + if (++g.TablesTempDataStacked > g.TablesTempData.Size) + g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); + ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; temp_data->TableIndex = table_idx; table->DrawSplitter = &table->TempData->DrawSplitter; table->DrawSplitter->Clear(); @@ -1382,9 +1381,8 @@ void ImGui::EndTable() // Clear or restore current table, if any IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); - IM_ASSERT(g.CurrentTableStackIdx >= 0); - g.CurrentTableStackIdx--; - temp_data = g.CurrentTableStackIdx >= 0 ? &g.TablesTempDataStack[g.CurrentTableStackIdx] : NULL; + IM_ASSERT(g.TablesTempDataStacked > 0); + temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; if (g.CurrentTable) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7da069b6..0ad4f9a1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.85 +// dear imgui, v1.86 WIP // (widgets code) /* From 05877c14df36d300366644cf1a1106a2513f8751 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 Oct 2021 11:51:01 +0200 Subject: [PATCH 104/332] Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cfcfd931..86679cdb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -119,6 +119,10 @@ Other Changes: - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] - CI: Add MinGW DLL build to test suite. [@rokups] +Docking+Viewports Branch: + +- Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) + ----------------------------------------------------------------------- VERSION 1.85 (Released 2021-10-12) diff --git a/imgui.cpp b/imgui.cpp index 8104f7b6..3088739f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7187,7 +7187,6 @@ void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; - g.DisabledAlphaBackup = g.Style.Alpha; if (!was_disabled && disabled) { g.DisabledAlphaBackup = g.Style.Alpha; From bbd5119a267adc8cd082df9a3f1ef372f5c44d01 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 Oct 2021 12:01:35 +0200 Subject: [PATCH 105/332] Backends: Vulkan: comments about user using VK_DYNAMIC_STATE_VIEWPORT / VK_DYNAMIC_STATE_SCISSOR and not calling vkCmdSetViewport() / vkCmdSetScissor(). (#4644) --- backends/imgui_impl_vulkan.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 296304fa..280edea7 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -537,6 +537,14 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm global_idx_offset += cmd_list->IdxBuffer.Size; global_vtx_offset += cmd_list->VtxBuffer.Size; } + + // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. + // Our last values will leak into user/application rendering IF: + // - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state + // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitely set that state. + // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering. + // In theory we should aim to backup/restore those values but I am not sure this is possible. + // If we add a call to vkCmdSetScissor() to cover the full viewport that would fix things for majority of users BUT would made it harder to notice your bug. (See github #4644) } bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) From 19c3773cc09fffdf950c3e88273f0a15adad1176 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 Oct 2021 12:11:11 +0200 Subject: [PATCH 106/332] Backends: Vulkan: Call vkCmdSetScissor() with a full-viewport at end of render. (#4644) --- backends/imgui_impl_vulkan.cpp | 5 ++++- docs/CHANGELOG.txt | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 280edea7..e4f12f2b 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. // 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. @@ -544,7 +545,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitely set that state. // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering. // In theory we should aim to backup/restore those values but I am not sure this is possible. - // If we add a call to vkCmdSetScissor() to cover the full viewport that would fix things for majority of users BUT would made it harder to notice your bug. (See github #4644) + // We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644) + VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } }; + vkCmdSetScissor(command_buffer, 0, 1, &scissor); } bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2ff3373a..7d58039c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,9 @@ Breaking Changes: Other Changes: +- Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce + likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling + vkCmdSetScissor() explicitly every frame. (#4644) - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] - CI: Add MinGW DLL build to test suite. [@rokups] From bac748fa95ac003c7b354139980f8b4b7f6ac5da Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 Oct 2021 17:16:26 +0200 Subject: [PATCH 107/332] Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) --- backends/imgui_impl_glfw.cpp | 1 + backends/imgui_impl_sdl.cpp | 1 + backends/imgui_impl_win32.cpp | 1 + docs/CHANGELOG.txt | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 5d7a915b..018b537c 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -961,4 +961,5 @@ static void ImGui_ImplGlfw_InitPlatformInterface() static void ImGui_ImplGlfw_ShutdownPlatformInterface() { + ImGui::DestroyPlatformWindows(); } diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 42b06a57..f87393f5 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -776,4 +776,5 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g static void ImGui_ImplSDL2_ShutdownPlatformInterface() { + ImGui::DestroyPlatformWindows(); } diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 3d584c87..f18553b3 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -1006,6 +1006,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() static void ImGui_ImplWin32_ShutdownPlatformInterface() { ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL)); + ImGui::DestroyPlatformWindows(); } //--------------------------------------------------------------------------------------------------------- diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 86679cdb..152b0b10 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -105,7 +105,8 @@ Other changes: Docking+Viewports Branch: -- Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) +- Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) +- Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) ----------------------------------------------------------------------- From ad3f54ff1a8e380fc79079d657ef108fa1cf8e2d Mon Sep 17 00:00:00 2001 From: liam Date: Tue, 19 Oct 2021 14:13:44 +0200 Subject: [PATCH 108/332] Examples: Emscripten comments (#4650, #4662), standardize readme --- examples/example_emscripten_opengl3/README.md | 6 +++++- examples/example_emscripten_wgpu/README.md | 20 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/examples/example_emscripten_opengl3/README.md b/examples/example_emscripten_opengl3/README.md index fddd9229..e2b19e44 100644 --- a/examples/example_emscripten_opengl3/README.md +++ b/examples/example_emscripten_opengl3/README.md @@ -1,8 +1,11 @@ ## How to Build - You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions -- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup. + - Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools. + +- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup. + - Then build using `make` while in the `example_emscripten_opengl3/` directory. ## How to Run @@ -15,6 +18,7 @@ _"Unfortunately several browsers (including Chrome, Safari, and Internet Explore - Emscripten SDK has a handy `emrun` command: `emrun web/example_emscripten_opengl3.html --browser firefox` which will spawn a temporary local webserver (in Firefox). See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details. - You may use Python 3 builtin webserver: `python -m http.server -d web` (this is what `make serve` uses). - You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`. + - If you are accessing the files over a network, certain browsers, such as Firefox, will restrict Gamepad API access to secure contexts only (e.g. https only). ## Obsolete features: diff --git a/examples/example_emscripten_wgpu/README.md b/examples/example_emscripten_wgpu/README.md index 73fabddc..c4c4dec7 100644 --- a/examples/example_emscripten_wgpu/README.md +++ b/examples/example_emscripten_wgpu/README.md @@ -1,10 +1,24 @@ - -# How to Build +## How to Build - You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions - Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools. +- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup. + - Then build using `make` while in the `example_emscripten_wgpu/` directory. -- Requires Emscripten 2.0.10 (December 2020) due to GLFW adaptations +- Requires recent Emscripten as WGPU is still a work-in-progress API. + +## How to Run + +To run on a local machine: +- Make sure your browse supports WGPU and it is enabled. WGPU is still WIP not enabled by default in most browser. +- `make serve` will use Python3 to spawn a local webserver, you can then browse http://localhost:8000 to access your build. +- Otherwise, generally you will need a local webserver: + - Quoting [https://emscripten.org/docs/getting_started](https://emscripten.org/docs/getting_started/Tutorial.html#generating-html):
+_"Unfortunately several browsers (including Chrome, Safari, and Internet Explorer) do not support file:// [XHR](https://emscripten.org/docs/site/glossary.html#term-xhr) requests, and can’t load extra files needed by the HTML (like a .wasm file, or packaged file data as mentioned lower down). For these browsers you’ll need to serve the files using a [local webserver](https://emscripten.org/docs/getting_started/FAQ.html#faq-local-webserver) and then open http://localhost:8000/hello.html."_ + - Emscripten SDK has a handy `emrun` command: `emrun web/example_emscripten_opengl3.html --browser firefox` which will spawn a temporary local webserver (in Firefox). See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details. + - You may use Python 3 builtin webserver: `python -m http.server -d web` (this is what `make serve` uses). + - You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`. + - If you are accessing the files over a network, certain browsers, such as Firefox, will restrict Gamepad API access to secure contexts only (e.g. https only). From 39b99072fb0184cef373b2164ede1d15ca038a9a Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 22 Oct 2021 14:52:13 +0200 Subject: [PATCH 109/332] ImVector: fix Clang warning (implicit conversion changes signedness: 'const ptrdiff_t' (aka 'const long long') to 'unsigned long long') --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index cd41d540..584ced66 100644 --- a/imgui.h +++ b/imgui.h @@ -1742,7 +1742,7 @@ struct ImVector inline void pop_back() { IM_ASSERT(Size > 0); Size--; } inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } - inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; } + inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; } inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } 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; } From 2318c764cff691804cedd66e7a1194e8fc53ec3d Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Tue, 22 Jun 2021 15:00:46 +0200 Subject: [PATCH 110/332] Inputs: Mouse: Add support for tracking multiple clicks more than just double (#3229) --- imgui.cpp | 30 +++++++++++++++++++++--------- imgui.h | 6 ++++-- imgui_demo.cpp | 10 ++++++---- imgui_widgets.cpp | 12 ++++++------ 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 63682668..10ded4b8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3695,22 +3695,26 @@ static void ImGui::UpdateMouseInputs() g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseDoubleClicked[i] = false; + g.IO.MouseMultiClickCount[i] = 0; if (g.IO.MouseClicked[i]) { if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) { ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseDoubleClicked[i] = true; - g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click + g.IO.MouseMultiClickTracker[i]++; + else + g.IO.MouseMultiClickTracker[i] = 1; } else { - g.IO.MouseClickedTime[i] = g.Time; + g.IO.MouseMultiClickTracker[i] = 1; } + + g.IO.MouseClickedTime[i] = g.Time; g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; + g.IO.MouseMultiClickCount[i] = g.IO.MouseMultiClickTracker[i]; + g.IO.MouseDownMultiClickCount[i] = g.IO.MouseMultiClickTracker[i]; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; } @@ -3722,8 +3726,9 @@ static void ImGui::UpdateMouseInputs() g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } + if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) - g.IO.MouseDownWasDoubleClick[i] = false; + g.IO.MouseDownMultiClickCount[i] = 0; if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation g.NavDisableMouseHover = false; } @@ -4765,7 +4770,14 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDoubleClicked[button]; + return g.IO.MouseMultiClickCount[button] == 2; +} + +bool ImGui::IsMouseTripleClicked(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseMultiClickCount[button] == 3; } // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. @@ -5445,7 +5457,7 @@ 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.MouseDoubleClicked[0] && resize_grip_n == 0) + if (held && g.IO.MouseMultiClickCount[0] == 2 && resize_grip_n == 0) { // Manual auto-fit when double-clicking size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); @@ -5980,7 +5992,7 @@ 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.MouseDoubleClicked[0]) + if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseMultiClickCount[0] == 2) window->WantCollapseToggle = true; if (window->WantCollapseToggle) { diff --git a/imgui.h b/imgui.h index 584ced66..f641ddbf 100644 --- a/imgui.h +++ b/imgui.h @@ -900,6 +900,7 @@ namespace ImGui IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down) IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseTripleClicked(ImGuiMouseButton button); // did mouse button triple-clicked? (note that a triple-click will also report IsMouseClicked() == true) IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? @@ -1931,11 +1932,12 @@ struct ImGuiIO ImVec2 MouseClickedPos[5]; // Position at time of clicking double MouseClickedTime[5]; // Time of last click (used to figure out double-click) bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + char MouseMultiClickTracker[5]; // Track multiple clicks over multiple frames + char MouseMultiClickCount[5]; // Has mouse button been clicked multiple times in a row? bool MouseReleased[5]; // Mouse button went from Down to !Down 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 MouseDownWasDoubleClick[5]; // Track if button down was a double-click + char MouseDownMultiClickCount[5]; // Track number of mouse down clicks in a row 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 ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6af064af..13e92478 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5518,10 +5518,12 @@ static void ShowDemoWindowMisc() else ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse tripleclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseTripleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse clickcount:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseMultiClickTracker[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseMultiClickTracker[i]); } + ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused ImGui::TreePop(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0ad4f9a1..c83f064c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -564,7 +564,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[mouse_button_clicked])) + if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseMultiClickCount[mouse_button_clicked] == 2)) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveId) @@ -641,7 +641,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((release_in || release_anywhere) && !g.DragDropActive) { // Report as pressed when releasing the mouse (this is the most common path) - bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownWasDoubleClick[mouse_button]; + bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownMultiClickCount[mouse_button] == 2; bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps if (!is_double_click_release && !is_repeating_already) pressed = true; @@ -2409,7 +2409,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, { const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); + const bool double_clicked = (hovered && g.IO.MouseMultiClickCount[0] == 2); if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); @@ -4176,12 +4176,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) + if (select_all || (hovered && !is_osx && io.MouseMultiClickCount[0] == 2)) { state->SelectAll(); state->SelectedAllMouseLock = true; } - else if (hovered && is_osx && io.MouseDoubleClicked[0]) + else if (hovered && is_osx && io.MouseMultiClickCount[0] == 2) { // Double-click select a word only, OS X style (by simulating keystrokes) state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); @@ -5919,7 +5919,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job - if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseDoubleClicked[0]) + if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseMultiClickCount[0] == 2) toggled = true; } else if (pressed && g.DragDropHoldJustPressedId == id) From 9c78fc928a18ffa4693d3a1fddcf65fb2ea9878d Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Jun 2021 15:27:30 +0200 Subject: [PATCH 111/332] Inputs: Mouse: Amend c8e3a01 for tracking multiple clicks, renaming. (#3229) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 33 ++++++++++++++++----------------- imgui.h | 8 ++++---- imgui_demo.cpp | 16 ++++++++++------ imgui_widgets.cpp | 12 ++++++------ 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7d58039c..57e660a4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,7 @@ Breaking Changes: Other Changes: +- Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) diff --git a/imgui.cpp b/imgui.cpp index 10ded4b8..2a7433ef 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3692,29 +3692,26 @@ static void ImGui::UpdateMouseInputs() for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; + g.IO.MouseClickedCount[i] = 0; // Will be filled below g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseMultiClickCount[i] = 0; if (g.IO.MouseClicked[i]) { + bool is_repeated_click = false; if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) { ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseMultiClickTracker[i]++; - else - g.IO.MouseMultiClickTracker[i] = 1; + is_repeated_click = true; } + if (is_repeated_click) + g.IO.MouseClickedLastCount[i]++; else - { - g.IO.MouseMultiClickTracker[i] = 1; - } - + g.IO.MouseClickedLastCount[i] = 1; g.IO.MouseClickedTime[i] = g.Time; g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseMultiClickCount[i] = g.IO.MouseMultiClickTracker[i]; - g.IO.MouseDownMultiClickCount[i] = g.IO.MouseMultiClickTracker[i]; + g.IO.MouseClickedCount[i] = g.IO.MouseClickedLastCount[i]; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; } @@ -3727,9 +3724,11 @@ static void ImGui::UpdateMouseInputs() g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } - if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) - g.IO.MouseDownMultiClickCount[i] = 0; - if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + // We provide io.MouseDoubleClicked[] as a legacy service + g.IO.MouseDoubleClicked[i] = (g.IO.MouseClickedCount[i] == 2); + + // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (g.IO.MouseClicked[i]) g.NavDisableMouseHover = false; } } @@ -4770,14 +4769,14 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseMultiClickCount[button] == 2; + return g.IO.MouseClickedCount[button] == 2; } bool ImGui::IsMouseTripleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseMultiClickCount[button] == 3; + return g.IO.MouseClickedCount[button] == 3; } // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. @@ -5457,7 +5456,7 @@ 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.MouseMultiClickCount[0] == 2 && resize_grip_n == 0) + if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0) { // Manual auto-fit when double-clicking size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); @@ -5992,7 +5991,7 @@ 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.MouseMultiClickCount[0] == 2) + 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 (window->WantCollapseToggle) { diff --git a/imgui.h b/imgui.h index f641ddbf..53c9971b 100644 --- a/imgui.h +++ b/imgui.h @@ -1931,13 +1931,13 @@ struct ImGuiIO ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) ImVec2 MouseClickedPos[5]; // Position at time of clicking double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - char MouseMultiClickTracker[5]; // Track multiple clicks over multiple frames - char MouseMultiClickCount[5]; // Has mouse button been clicked multiple times in a row? + bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) + ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down + ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. bool MouseReleased[5]; // Mouse button went from Down to !Down 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. - char MouseDownMultiClickCount[5]; // Track number of mouse down clicks in a row 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 ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 13e92478..195d89d2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5518,12 +5518,16 @@ static void ShowDemoWindowMisc() else ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse tripleclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseTripleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse clickcount:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseMultiClickTracker[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseMultiClickTracker[i]); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + + int count = IM_ARRAYSIZE(io.MouseDown); + ImGui::Text("Mouse down:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text(" - clicked double:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text(" - clicked triple:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseTripleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text(" - clicked count:"); for (int i = 0; i < count; i++) if (io.MouseClickedCount[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseClickedCount[i]); } + //ImGui::Text(" - last count:"); for (int i = 0; i < count; i++) if (io.MouseClickedLastCount[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseClickedLastCount[i]); } + + ImGui::Text("Mouse released:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused ImGui::TreePop(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c83f064c..687baa8e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -564,7 +564,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseMultiClickCount[mouse_button_clicked] == 2)) + if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveId) @@ -641,7 +641,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((release_in || release_anywhere) && !g.DragDropActive) { // Report as pressed when releasing the mouse (this is the most common path) - bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownMultiClickCount[mouse_button] == 2; + 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 if (!is_double_click_release && !is_repeating_already) pressed = true; @@ -2409,7 +2409,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, { const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseMultiClickCount[0] == 2); + const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2); if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); @@ -4176,12 +4176,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseMultiClickCount[0] == 2)) + if (select_all || (hovered && !is_osx && io.MouseClickedCount[0] == 2)) { state->SelectAll(); state->SelectedAllMouseLock = true; } - else if (hovered && is_osx && io.MouseMultiClickCount[0] == 2) + else if (hovered && is_osx && io.MouseClickedCount[0] == 2) { // Double-click select a word only, OS X style (by simulating keystrokes) state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); @@ -5919,7 +5919,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job - if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseMultiClickCount[0] == 2) + if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) toggled = true; } else if (pressed && g.DragDropHoldJustPressedId == id) From 126a6f894fd50dcfc1f6707048892ccb281cc420 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Oct 2021 16:34:46 +0200 Subject: [PATCH 112/332] InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 48 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 57e660a4..90033f79 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,8 @@ Breaking Changes: Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] +- InputText: made double-click select word, triple-line select line. Word delimitation logic differs + slightly from the one used by CTRL+arrows. (#2244) - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 687baa8e..71fca734 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3679,17 +3679,18 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob } // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } +static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC #else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN #endif -#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { @@ -3881,11 +3882,12 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Generic named filters if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))) { - // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf. + // 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; + // 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; @@ -4176,19 +4178,41 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseClickedCount[0] == 2)) + if (select_all) { state->SelectAll(); state->SelectedAllMouseLock = true; } - else if (hovered && is_osx && io.MouseClickedCount[0] == 2) + else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) { - // Double-click select a word only, OS X style (by simulating keystrokes) - state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + const int multiclick_count = (io.MouseClickedCount[0] - 2); + if ((multiclick_count % 2) == 0) + { + // Double-click: Select word + // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: + // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) + const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n'; + if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + if (!STB_TEXT_HAS_SELECTION(&state->Stb)) + ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb); + state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor); + state->Stb.select_end = state->Stb.cursor; + ImStb::stb_textedit_clamp(state, &state->Stb); + } + else + { + // Triple-click: Select line + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); + state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); + } + state->CursorAnimReset(); } else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { + // FIXME: unselect on late click could be done release? if (hovered) { stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); From 99552420a259076e6a622ec3f9a1a1be14853198 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Oct 2021 17:13:49 +0200 Subject: [PATCH 113/332] InputText: triple-click avoid scroll + include end of line character. (#2244) --- imgui_widgets.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 71fca734..784e1fc3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4205,8 +4205,16 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else { // Triple-click: Select line + const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n'; state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); + if (!is_eol && is_multiline) + { + ImSwap(state->Stb.select_start, state->Stb.select_end); + state->Stb.cursor = state->Stb.select_end; + } + state->CursorFollow = false; } state->CursorAnimReset(); } From 0f2898ea884eb54d257c7826ef2231ce0211ef54 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Oct 2021 18:31:16 +0200 Subject: [PATCH 114/332] Demo: Tree selection demo use !IsItemToggledOpen() (#1896) + update FAQ (#4366) --- docs/FAQ.md | 3 ++- imgui.cpp | 8 +++++--- imgui_demo.cpp | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 479bce06..8f67d4e7 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -23,7 +23,7 @@ or view this file with any Markdown viewer. | [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-clipping-or-disappearing-when-i-move-windows-around) | | [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) | | **Q&A: Usage** | -| **[About the ID Stack system..
Why is my widget not reacting when I click on it?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?](#q-about-the-id-stack-system)** | +| **[About the ID Stack system..
Why is my widget not reacting when I click on it?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?]
How can I have multiple windows with the same label?](#q-about-the-id-stack-system)** | | [How can I display an image? What is ImTextureID, how does it work?](#q-how-can-i-display-an-image-what-is-imtextureid-how-does-it-work)| | [How can I use my own math types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-math-types-instead-of-imvec2imvec4) | | [How can I interact with standard C++ types (such as std::string and std::vector)?](#q-how-can-i-interact-with-standard-c-types-such-as-stdstring-and-stdvector) | @@ -190,6 +190,7 @@ Refer to rendering backends in the [examples/](https://github.com/ocornut/imgui/ ### Q: Why is my widget not reacting when I click on it? ### Q: How can I have widgets with an empty label? ### Q: How can I have multiple widgets with the same label? +### Q: How can I have multiple windows with the same label? A primer on labels and the ID Stack... diff --git a/imgui.cpp b/imgui.cpp index 2a7433ef..880c9334 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -723,9 +723,11 @@ CODE Q&A: Usage ---------- - Q: Why is my widget not reacting when I click on it? - Q: How can I have widgets with an empty label? - Q: How can I have multiple widgets with the same label? + Q: About the ID Stack system.. + - Why is my widget not reacting when I click on it? + - How can I have widgets with an empty label? + - 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 works? Q: How can I use my own math types instead of ImVec2/ImVec4? Q: How can I interact with standard C++ types (such as std::string and std::vector)? diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 195d89d2..29bcf132 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -786,6 +786,7 @@ static void ShowDemoWindowWidgets() for (int i = 0; i < 6; i++) { // Disable the default "open on single-click behavior" + set Selected flag according to our selection. + // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. ImGuiTreeNodeFlags node_flags = base_flags; const bool is_selected = (selection_mask & (1 << i)) != 0; if (is_selected) @@ -794,7 +795,7 @@ static void ShowDemoWindowWidgets() { // Items 0..2 are Tree Node bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { @@ -815,7 +816,7 @@ static void ShowDemoWindowWidgets() // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { From c363b6df2b4d4aeb7978b71b03ae5002bd5ae6ca Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 Oct 2021 12:02:42 +0200 Subject: [PATCH 115/332] Modals: fixed issue hovering popups inside a child inside a modal. Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676)(#4676, #4527) Amend/fix 6b1e094f, fc4988ffb (Sep 24) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 90033f79..0ae0d0b8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,8 @@ Breaking Changes: Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] +- Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) +- Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce diff --git a/imgui.cpp b/imgui.cpp index 880c9334..912d6c9d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6719,9 +6719,14 @@ void ImGui::PopTextWrapPos() static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) { - window = window->RootWindow; - if (popup_hierarchy) - window = window->RootWindowPopupTree; + ImGuiWindow* last_window = NULL; + while (last_window != window) + { + last_window = window; + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + } return window; } From 19c72cd52a47a5184159c640b9cffe67063aa3f3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 Oct 2021 12:28:01 +0200 Subject: [PATCH 116/332] Nav: fixed absolute mouse position (with NavEnableSetMousePos config flag) when using Home/End leads to scrolling. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- imgui.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0ae0d0b8..e1eaca44 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,8 @@ Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) +- Nav: fixed absolute mouse position (with NavEnableSetMousePos config flag) when using Home/End + leads to scrolling. - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce diff --git a/imgui.cpp b/imgui.cpp index 912d6c9d..4841b472 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9619,7 +9619,7 @@ void ImGui::NavMoveRequestApplyResult() { // FIXME: Should remove this float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; - delta_scroll.y = result->Window->Scroll.y - scroll_target; + delta_scroll.y = scroll_target - result->Window->Scroll.y; SetScrollY(result->Window, scroll_target); } else diff --git a/imgui.h b/imgui.h index 53c9971b..db1d96cb 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18502 +#define IMGUI_VERSION_NUM 18503 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 8361ed1f543a74c1d5fba1df4a2c5084f889a237 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 26 Oct 2021 19:23:04 +0200 Subject: [PATCH 117/332] Nav: wrap abs<>rel rectangle conversions before we attempt to switch the reference point from window->Pos to window->DC.CursorStartPos. This commit should have no effect. Current point makes rectangle invalid right after a scroll, for interactive actions it's rarely a problem but e.g. clipper will want to use g.NavID rect rel while scrolling. (#3841) --- imgui.cpp | 26 +++++++++++++------------- imgui_internal.h | 2 ++ imgui_widgets.cpp | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4841b472..045ac2a7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2268,7 +2268,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items if (g.NavMoveScoringItems) unclipped_rect.Add(g.NavScoringRect); if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); // Could store and use NavJustMovedToRectRel + unclipped_rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel const ImVec2 pos = window->DC.CursorPos; int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); @@ -7127,7 +7127,7 @@ void ImGui::SetItemDefaultFocus() g.NavInitRequest = false; g.NavInitResultId = g.LastItemData.ID; - g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos); + g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); NavUpdateAnyRequestFlag(); // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) @@ -8829,7 +8829,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) - window->NavRectRel[nav_layer] = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); + window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; @@ -9009,7 +9009,7 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) result->ID = g.LastItemData.ID; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; result->InFlags = g.LastItemData.InFlags; - result->RectRel = ImRect(g.LastItemData.NavRect.Min - window->Pos, g.LastItemData.NavRect.Max - window->Pos); + result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) @@ -9030,7 +9030,7 @@ static void ImGui::NavProcessItem() if (candidate_for_nav_default_focus || g.NavInitResultId == 0) { g.NavInitResultId = id; - g.NavInitResultRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + g.NavInitResultRectRel = WindowRectAbsToRel(window, nav_bb); } if (candidate_for_nav_default_focus) { @@ -9077,7 +9077,7 @@ static void ImGui::NavProcessItem() g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; - window->NavRectRel[window->DC.NavLayerCurrent] = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); // Store item bounding box (relative to window position) + window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) } } @@ -9242,8 +9242,8 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() else { // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->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())); + ImRect rect_rel = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[g.NavLayer]); + 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())); 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. } @@ -9456,7 +9456,7 @@ static void ImGui::NavUpdate() if (g.NavWindow) { ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } // [DEBUG] if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } } #endif @@ -9549,7 +9549,7 @@ void ImGui::NavUpdateCreateMoveRequest() // (can't focus a visible object like we can with the mouse). if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) { - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); + ImRect window_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); @@ -9565,7 +9565,7 @@ void ImGui::NavUpdateCreateMoveRequest() if (window != NULL) { ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - scoring_rect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max); + scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); scoring_rect.TranslateY(scoring_rect_offset_y); scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); scoring_rect.Max.x = scoring_rect.Min.x; @@ -9624,7 +9624,7 @@ void ImGui::NavMoveRequestApplyResult() } else { - ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); + ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); delta_scroll = ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); } @@ -9699,7 +9699,7 @@ static void ImGui::NavUpdateCancelRequest() IM_ASSERT(child_window->ChildId != 0); ImRect child_rect = child_window->Rect(); FocusWindow(parent_window); - SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos)); + SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); } else if (g.OpenPopupStack.Size > 0) { diff --git a/imgui_internal.h b/imgui_internal.h index 0447b5aa..cb41487e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2388,6 +2388,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); + inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->Pos; 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->Pos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 784e1fc3..96c0e2cb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6258,7 +6258,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); // (bb == NavRect) + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect) g.NavDisableHighlight = true; } } From 8d361c47fbd99d1e9d827c221210608ed3fa335b Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 Oct 2021 12:41:49 +0200 Subject: [PATCH 118/332] Nav: rework NavEnableSetMousePos application so scrolling is applied more consistently in NavCalcPreferredRefPos() + moved at end of NavUpdate() after the last SetScroll have been done. --- imgui.cpp | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 045ac2a7..0554b347 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9232,7 +9232,8 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + ImGuiWindow* window = g.NavWindow; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) if (IsMousePosValid(&g.IO.MousePos)) @@ -9241,8 +9242,14 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() } else { - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. - ImRect rect_rel = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[g.NavLayer]); + // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + // 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); + } 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())); 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. @@ -9341,19 +9348,12 @@ static void ImGui::NavUpdate() g.NavTabbingInputableRemaining = 0; g.NavMoveSubmitted = g.NavMoveScoringItems = false; - // Apply application mouse position movement, after we had a chance to process move request result. + // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) + bool set_mouse_pos = false; if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the navigated item position from last frame - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - { - io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); - io.WantSetMousePos = true; - //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); - } - g.NavMousePosDirty = false; - } + if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) + set_mouse_pos = true; + g.NavMousePosDirty = false; g.NavIdIsAlive = false; g.NavJustTabbedId = 0; IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); @@ -9450,6 +9450,15 @@ static void ImGui::NavUpdate() g.NavDisableMouseHover = g.NavMousePosDirty = false; } + // 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("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); + } + // [DEBUG] g.NavScoringDebugCount = 0; #if IMGUI_DEBUG_NAV_RECTS @@ -9614,23 +9623,17 @@ void ImGui::NavMoveRequestApplyResult() // Scroll to keep newly navigated item fully into view. if (g.NavLayer == ImGuiNavLayer_Main) { - ImVec2 delta_scroll; if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { // FIXME: Should remove this float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; - delta_scroll.y = scroll_target - result->Window->Scroll.y; SetScrollY(result->Window, scroll_target); } else { ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); - delta_scroll = ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); + ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); } - - // Offset our result position so mouse position can be applied immediately after in NavUpdate() - result->RectRel.TranslateX(-delta_scroll.x); - result->RectRel.TranslateY(-delta_scroll.y); } ClearActiveID(); From a67f7dce3143d970f6ab8b1aa94470de67e43d7a Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 27 Oct 2021 14:07:04 +0200 Subject: [PATCH 119/332] Nav: relative rectangle positions are now stored relative to CursorStartPos to be independent of scrolling. Will facilitate rework of clipper (#3841) + Extracted code into NavUpdateCreateWrappingRequest() + Fix for PVS in NavUpdate() assigning g.NavMousePosDirty twice. --- imgui.cpp | 101 ++++++++++++++++++++++++----------------------- imgui.h | 2 +- imgui_internal.h | 4 +- 3 files changed, 55 insertions(+), 52 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0554b347..344a804f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -917,6 +917,7 @@ static void NavUpdateCancelRequest(); static void NavUpdateCreateMoveRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); +static void NavUpdateCreateWrappingRequest(); static void NavEndFrame(); static bool NavScoreItem(ImGuiNavItemData* result); static void NavApplyItemToResult(ImGuiNavItemData* result); @@ -9447,7 +9448,7 @@ static void ImGui::NavUpdate() if (!nav_keyboard_active && !nav_gamepad_active) { g.NavDisableHighlight = true; - g.NavDisableMouseHover = g.NavMousePosDirty = false; + g.NavDisableMouseHover = set_mouse_pos = false; } // Update mouse position if requested @@ -9465,7 +9466,7 @@ static void ImGui::NavUpdate() if (g.NavWindow) { ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG] if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } } #endif @@ -9775,7 +9776,7 @@ static float ImGui::NavUpdatePageUpPageDown() // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. // Preserve current horizontal position if we have any. - nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; + nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f; if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Down; @@ -9784,7 +9785,7 @@ static float ImGui::NavUpdatePageUpPageDown() } else if (end_pressed) { - nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; + nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y; if (nav_rect_rel.IsInverted()) nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; g.NavMoveDir = ImGuiDir_Up; @@ -9805,64 +9806,66 @@ static void ImGui::NavEndFrame() NavUpdateWindowingOverlay(); // Perform wrap-around in menus + // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. + const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; + if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + NavUpdateCreateWrappingRequest(); +} + +static void ImGui::NavUpdateCreateWrappingRequest() +{ + ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; + + bool do_forward = false; + ImRect bb_rel = window->NavRectRel[g.NavLayer]; + ImGuiDir clip_dir = g.NavMoveDir; const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; - const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; - if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { - bool do_forward = false; - ImRect bb_rel = window->NavRectRel[g.NavLayer]; - ImGuiDir clip_dir = g.NavMoveDir; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { - bb_rel.Min.x = bb_rel.Max.x = - ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(-bb_rel.GetHeight()); - clip_dir = ImGuiDir_Up; - } - do_forward = true; - } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(+bb_rel.GetHeight()); - clip_dir = ImGuiDir_Down; - } - do_forward = true; + bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row + clip_dir = ImGuiDir_Up; } - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { - bb_rel.Min.y = bb_rel.Max.y = - ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y + decoration_up_height; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(-bb_rel.GetWidth()); - clip_dir = ImGuiDir_Left; - } - do_forward = true; + bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row + clip_dir = ImGuiDir_Down; } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y + decoration_up_height; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(+bb_rel.GetWidth()); - clip_dir = ImGuiDir_Right; - } - do_forward = true; + bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column + clip_dir = ImGuiDir_Left; } - if (do_forward) + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { - window->NavRectRel[g.NavLayer] = bb_rel; - NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); + bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column + clip_dir = ImGuiDir_Right; } + do_forward = true; } + if (!do_forward) + return; + window->NavRectRel[g.NavLayer] = bb_rel; + NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) diff --git a/imgui.h b/imgui.h index db1d96cb..e3d2b41e 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18503 +#define IMGUI_VERSION_NUM 18504 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index cb41487e..3373db06 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2388,8 +2388,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); - inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->Pos; 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->Pos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } + 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); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); From b1a68d899ce33d7eb20a1a641512c86ee6eeee2a Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Tue, 2 Nov 2021 04:51:22 -0700 Subject: [PATCH 120/332] Fix misplaced backtick in FAQ.md (#4669) --- docs/FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 8f67d4e7..46c1556d 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -475,7 +475,7 @@ ImGui::End(); - Math operators: if you have setup `IM_VEC2_CLASS_EXTRA` in `imconfig.h` to bind your own math types, you can use your own math types and their natural operators instead of ImVec2. ImVec2 by default doesn't export any math operators in the public API. You may use `#define IMGUI_DEFINE_MATH_OPERATORS` `#include "imgui_internal.h"` to use the internally defined math operators, but instead prefer using your own math library and set it up in `imconfig.h`. - You can use `ImGui::GetBackgroundDrawList()` or `ImGui::GetForegroundDrawList()` to access draw lists which will be displayed behind and over every other dear imgui windows (one bg/fg drawlist per viewport). This is very convenient if you need to quickly display something on the screen that is not associated to a dear imgui window. - You can also create your own empty window and draw inside it. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags (The `ImGuiWindowFlags_NoDecoration` flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse). Then you can retrieve the ImDrawList* via `GetWindowDrawList()` and draw to it in any way you like. -`- You can create your own ImDrawList instance. You'll need to initialize them with `ImGui::GetDrawListSharedData()`, or create your own instancing ImDrawListSharedData`, and then call your renderer function with your own ImDrawList or ImDrawData data. +- You can create your own ImDrawList instance. You'll need to initialize them with `ImGui::GetDrawListSharedData()`, or create your own instancing `ImDrawListSharedData`, and then call your renderer function with your own ImDrawList or ImDrawData data. - Looking for fun? The [ImDrawList coding party 2020](https://github.com/ocornut/imgui/issues/3606) thread is full of "don't do this at home" extreme uses of the ImDrawList API. ##### [Return to Index](#index) From eb654f1badd03454e69c196086e6ac3773db4cfc Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 2 Nov 2021 12:57:05 +0100 Subject: [PATCH 121/332] Typo --- docs/FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 46c1556d..009027c1 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -23,7 +23,7 @@ or view this file with any Markdown viewer. | [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-clipping-or-disappearing-when-i-move-windows-around) | | [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) | | **Q&A: Usage** | -| **[About the ID Stack system..
Why is my widget not reacting when I click on it?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?]
How can I have multiple windows with the same label?](#q-about-the-id-stack-system)** | +| **[About the ID Stack system..
Why is my widget not reacting when I click on it?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?
How can I have multiple windows with the same label?](#q-about-the-id-stack-system)** | | [How can I display an image? What is ImTextureID, how does it work?](#q-how-can-i-display-an-image-what-is-imtextureid-how-does-it-work)| | [How can I use my own math types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-math-types-instead-of-imvec2imvec4) | | [How can I interact with standard C++ types (such as std::string and std::vector)?](#q-how-can-i-interact-with-standard-c-types-such-as-stdstring-and-stdvector) | From d1eb7b57576eefa30b9ebf675cd259f636fdd6f9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Nov 2021 16:16:58 +0100 Subject: [PATCH 122/332] ScrollToItem: amend fully visible tests to take account of spacing used (fix nav in one axis scrolling back and forth between axises when space is tight by just < ItemSpacing*2) (#3692, #2812, #4242, #2900) Amend 8f495e55 --- imgui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 344a804f..3ba7219e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6164,7 +6164,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Inner rectangle // Not affected by window border size. Used by: // - InnerClipRect - // - ScrollToBringRectIntoView() + // - ScrollToRectEx() // - NavUpdatePageUpPageDown() // - Scrollbar() window->InnerRect.Min.x = window->Pos.x; @@ -8042,8 +8042,8 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; - const bool can_be_fully_visible_x = item_rect.GetWidth() <= window_rect.GetWidth(); - const bool can_be_fully_visible_y = item_rect.GetHeight() <= window_rect.GetHeight(); + const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= window_rect.GetWidth(); + const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= window_rect.GetHeight(); if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) { From 978598b174e6cb0ddc0e1c38023d11cacc2a554e Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Nov 2021 16:29:21 +0100 Subject: [PATCH 123/332] Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. + fixed resizing window from borders setting navigation to Menu layer. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e1eaca44..56795d54 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Other Changes: - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) - Nav: fixed absolute mouse position (with NavEnableSetMousePos config flag) when using Home/End leads to scrolling. +- Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. +- Nav: fixed resizing window from borders setting navigation to Menu layer. - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce diff --git a/imgui.cpp b/imgui.cpp index 3ba7219e..165b4579 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5489,7 +5489,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() - ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren); + 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) { @@ -5678,6 +5678,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) + // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref? const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; @@ -9730,7 +9731,7 @@ static float ImGui::NavUpdatePageUpPageDown() ImGuiIO& io = g.IO; ImGuiWindow* window = g.NavWindow; - if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) + if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); @@ -9740,6 +9741,9 @@ static float ImGui::NavUpdatePageUpPageDown() if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out return 0.0f; + if (g.NavLayer != ImGuiNavLayer_Main) + NavRestoreLayer(ImGuiNavLayer_Main); + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) { // Fallback manual-scroll when window has no navigable item From 14466a6d19ca97f4dcf07688ebe4411aeb51958a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Nov 2021 16:59:48 +0100 Subject: [PATCH 124/332] Nav: Page/Home/End doesn't immediately restore highlight (before results comes) + pressing Esc to exit a child window re-enable the Nav highlight if it was disabled by mouse. Also fix a move request fail restoring highlight from not moving mouse cursor. Add NavRestoreHighlightAfterMove() helper a little bit of extra sanity. --- docs/CHANGELOG.txt | 7 +++++-- imgui.cpp | 29 +++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 56795d54..59cb8d87 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,10 +41,13 @@ Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) -- Nav: fixed absolute mouse position (with NavEnableSetMousePos config flag) when using Home/End - leads to scrolling. - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: fixed resizing window from borders setting navigation to Menu layer. +- Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse. +- Nav: (with ImGuiConfigFlags_NavEnableSetMousePos): fixed absolute mouse position when using Home/End + leads to scrolling. +- Nav: (with ImGuiConfigFlags_NavEnableSetMousePos): fixed not setting mouse position when a failed + move request (e.g. already at edge) reactivate the navigation highlight. - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce diff --git a/imgui.cpp b/imgui.cpp index 165b4579..95a799df 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -926,6 +926,7 @@ 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 @@ -8811,8 +8812,6 @@ void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id g.NavFocusScopeId = focus_scope_id; g.NavWindow->NavLastIds[nav_layer] = id; g.NavWindow->NavRectRel[nav_layer] = rect_rel; - //g.NavDisableHighlight = false; - //g.NavDisableMouseHover = g.NavMousePosDirty = true; } void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) @@ -9187,6 +9186,11 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) g.NavLayer = layer; NavInitWindow(window, true); } +} + +void ImGui::NavRestoreHighlightAfterMove() +{ + ImGuiContext& g = *GImGui; g.NavDisableHighlight = false; g.NavDisableMouseHover = g.NavMousePosDirty = true; } @@ -9486,10 +9490,7 @@ void ImGui::NavInitRequestApplyResult() SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result if (g.NavInitRequestFromMove) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; - } + NavRestoreHighlightAfterMove(); } void ImGui::NavUpdateCreateMoveRequest() @@ -9604,10 +9605,7 @@ void ImGui::NavMoveRequestApplyResult() if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - } + NavRestoreHighlightAfterMove(); return; } @@ -9669,10 +9667,7 @@ void ImGui::NavMoveRequestApplyResult() // Enable nav highlight if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; - } + NavRestoreHighlightAfterMove(); } // Process NavCancel input (to close a popup, get back to parent, clear focus) @@ -9695,6 +9690,7 @@ static void ImGui::NavUpdateCancelRequest() { // Leave the "menu" layer NavRestoreLayer(ImGuiNavLayer_Main); + NavRestoreHighlightAfterMove(); } else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) { @@ -9705,6 +9701,7 @@ static void ImGui::NavUpdateCancelRequest() ImRect child_rect = child_window->Rect(); FocusWindow(parent_window); SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); + NavRestoreHighlightAfterMove(); } else if (g.OpenPopupStack.Size > 0) { @@ -10031,8 +10028,7 @@ static void ImGui::NavUpdateWindowing() if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { ClearActiveID(); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; + NavRestoreHighlightAfterMove(); apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window, false); FocusWindow(apply_focus_window); @@ -10079,6 +10075,7 @@ static void ImGui::NavUpdateWindowing() if (new_nav_layer == ImGuiNavLayer_Menu) g.NavWindow->NavLastIds[new_nav_layer] = 0; NavRestoreLayer(new_nav_layer); + NavRestoreHighlightAfterMove(); } } } From b409df34dbe88411261771719c81373953d06d59 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Nov 2021 17:00:40 +0100 Subject: [PATCH 125/332] Clipper: Fixed content height declaration slightly mismatching the one of when not using a clipper. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- imgui.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 59cb8d87..052915d2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,8 @@ Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) +- Clipper: Fixed content height declaration slightly mismatching the one of when not using a clipper. + (an additional ItemSpacing.y was declared, affecting scrollbar range). - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: fixed resizing window from borders setting navigation to Menu layer. - Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse. diff --git a/imgui.cpp b/imgui.cpp index 95a799df..8f9a4f0c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2297,7 +2297,7 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) ImGuiWindow* window = g.CurrentWindow; float off_y = pos_y - window->DC.CursorPos.y; window->DC.CursorPos.y = pos_y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y); window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. if (ImGuiOldColumns* columns = window->DC.CurrentColumns) diff --git a/imgui.h b/imgui.h index e3d2b41e..a95c814f 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18504 +#define IMGUI_VERSION_NUM 18505 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From cd1b5f7883fa9f1faf550a051abd32d5fd9dc218 Mon Sep 17 00:00:00 2001 From: GamingMinds-DanielC Date: Mon, 11 Oct 2021 15:57:29 +0200 Subject: [PATCH 126/332] Clipper: enhanced ImGuiListClipper (#3841) --- imgui.cpp | 151 ++++++++++++++++++++++++++++++++++++++++++------------ imgui.h | 10 ++++ 2 files changed, 127 insertions(+), 34 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8f9a4f0c..d77c8ff3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2288,6 +2288,42 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items *out_items_display_end = end; } +static int SortAndFuseRanges(int* range_start, int* range_end, int range_count) +{ + // Helper to order ranges and fuse them together if possible. + // First sort both rangeStart and rangeEnd by rangeStart. Since this helper will just sort 2 or 3 entries, a bubble sort will do fine. + for (int sort_end = range_count - 1; sort_end > 0; --sort_end) + { + for (int i = 0; i < sort_end; ++i) + { + if (range_start[i] > range_start[i + 1]) + { + ImSwap(range_start[i], range_start[i + 1]); + ImSwap(range_end[i], range_end[i + 1]); + } + } + } + + // Now fuse ranges together as much as possible. + for (int i = 1; i < range_count;) + { + if (range_end[i - 1] >= range_start[i]) + { + range_end[i - 1] = ImMax(range_end[i - 1], range_end[i]); + range_count--; + for (int j = i; j < range_count; ++j) + { + range_start[j] = range_start[j + 1]; + range_end[j] = range_end[j + 1]; + } + } + else + i++; + } + + return range_count; +} + static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) { // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. @@ -2354,7 +2390,27 @@ void ImGuiListClipper::End() if (ItemsCount < INT_MAX && DisplayStart >= 0) SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); ItemsCount = -1; - StepNo = 3; + StepNo = RangeCount; +} + +void ImGuiListClipper::ForceDisplayRange(int item_start, int item_end) +{ + if (DisplayStart < 0 && RangeCount + YRangeCount < 1) // Only allowed after Begin() and if there has not been a specified range yet. + { + RangeStart[RangeCount] = item_start; + RangeEnd[RangeCount] = item_end; + RangeCount++; + } +} + +void ImGuiListClipper::ForceDisplayYRange(float y_min, float y_max) +{ + if (DisplayStart < 0 && RangeCount + YRangeCount < 1) // Only allowed after Begin() and if there has not been a specified range yet. + { + YRangeMin[YRangeCount] = y_min; + YRangeMax[YRangeCount] = y_max; + YRangeCount++; + } } bool ImGuiListClipper::Step() @@ -2373,6 +2429,8 @@ bool ImGuiListClipper::Step() return false; } + bool calc_clipping = false; + // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) if (StepNo == 0) { @@ -2389,22 +2447,24 @@ bool ImGuiListClipper::Step() StartPosY = window->DC.CursorPos.y; if (ItemsHeight <= 0.0f) { - // Submit the first item so we can measure its height (generally it is 0..1) - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; + // Submit the first item (or range) so we can measure its height (generally it is 0..1) + RangeStart[RangeCount] = ItemsFrozen; + RangeEnd[RangeCount] = ItemsFrozen + 1; + if (++RangeCount > 1) + RangeCount = SortAndFuseRanges(RangeStart, RangeEnd, RangeCount); + DisplayStart = ImMax(RangeStart[0], ItemsFrozen); + DisplayEnd = ImMin(RangeEnd[0], ItemsCount); StepNo = 1; return true; } - // Already has item height (given by user in Begin): skip to calculating step - DisplayStart = DisplayEnd; - StepNo = 2; + calc_clipping = true; // If on the first step with known item height, calculate clipping. } - // Step 1: the clipper infer height from first element - if (StepNo == 1) + // Step 1: Let the clipper infer height from first range + if (ItemsHeight <= 0.0f) { - IM_ASSERT(ItemsHeight <= 0.0f); + IM_ASSERT(StepNo == 1); if (table) { const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row @@ -2414,49 +2474,72 @@ bool ImGuiListClipper::Step() } else { - ItemsHeight = window->DC.CursorPos.y - StartPosY; + ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); } IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); - StepNo = 2; - } - // Reached end of list - if (DisplayEnd >= ItemsCount) - { - End(); - return false; + calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } - // Step 2: calculate the actual range of elements to display, and position the cursor before the first element - if (StepNo == 2) + // Step 0 or 1: Calculate the actual range of visible elements. + if (calc_clipping) { IM_ASSERT(ItemsHeight > 0.0f); int already_submitted = DisplayEnd; - ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); - DisplayStart += already_submitted; - DisplayEnd += already_submitted; + ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &RangeStart[RangeCount], &RangeEnd[RangeCount]); + + // Only add another range if it hasn't been handled by the initial range. + if (RangeStart[RangeCount] < RangeEnd[RangeCount]) + { + RangeStart[RangeCount] += already_submitted; + RangeEnd[RangeCount] += already_submitted; + RangeCount++; + } + + // Convert specified y ranges to item index ranges. + for (int i = 0; i < YRangeCount; ++i) + { + int start = already_submitted + (int)((YRangeMin[i] - window->DC.CursorPos.y) / ItemsHeight); + int end = already_submitted + (int)((YRangeMax[i] - window->DC.CursorPos.y) / ItemsHeight) + 1; + + start = ImMax(start, already_submitted); + end = ImMin(end, ItemsCount); + + if (start < end) + { + RangeStart[RangeCount] = start; + RangeEnd[RangeCount] = end; + RangeCount++; + } + } + + // Try to sort and fuse only if there is more than 1 range remaining. + if (RangeCount > StepNo + 1) + RangeCount = StepNo + SortAndFuseRanges(&RangeStart[StepNo], &RangeEnd[StepNo], RangeCount - StepNo); + } + + // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. + if (StepNo < RangeCount) + { + int already_submitted = DisplayEnd; + DisplayStart = ImMax(RangeStart[StepNo], already_submitted); + DisplayEnd = ImMin(RangeEnd[StepNo], ItemsCount); // Seek cursor if (DisplayStart > already_submitted) SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight); - StepNo = 3; + StepNo++; return true; } - // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), + // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), // Advance the cursor to the end of the list and then returns 'false' to end the loop. - if (StepNo == 3) - { - // Seek cursor - if (ItemsCount < INT_MAX) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor - ItemsCount = -1; - return false; - } + if (ItemsCount < INT_MAX) + SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor + ItemsCount = -1; - IM_ASSERT(0); return false; } diff --git a/imgui.h b/imgui.h index a95c814f..132375e6 100644 --- a/imgui.h +++ b/imgui.h @@ -2179,6 +2179,7 @@ struct ImGuiStorage // Usage: // ImGuiListClipper clipper; // clipper.Begin(1000); // We have 1000 elements, evenly spaced. +// clipper.ForceDisplay(42); // Optional, force element with given index to be displayed (use f.e. if you need to update a tooltip for a drag&drop source) // while (clipper.Step()) // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) // ImGui::Text("line number %d", i); @@ -2195,6 +2196,12 @@ struct ImGuiListClipper // [Internal] int ItemsCount; + int RangeStart[4]; // 1 for the user, rest for internal use + int RangeEnd[4]; + int RangeCount; + int YRangeMin[1]; + int YRangeMax[1]; + int YRangeCount; int StepNo; int ItemsFrozen; float ItemsHeight; @@ -2207,6 +2214,9 @@ struct ImGuiListClipper // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. + IMGUI_API void ForceDisplayRange(int item_start, int item_end); // Optionally call before the first call to Step() if you need a range of items to be displayed regardless of visibility. + inline void ForceDisplay(int item_start, int item_count = 1) { ForceDisplayRange(item_start, item_start + item_count); } // Like ForceDisplayRange, but with a number instead of an end index. + IMGUI_API void ForceDisplayYRange(float y_min, float y_max); // Like ForceDisplayRange, but with y coordinates instead of item indices. IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS From 93cccd27f6eb0cd53ea952dcf14dd87b1e52cedd Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 Oct 2021 17:19:20 +0200 Subject: [PATCH 127/332] Clipper: bunch of rework. (#3841, #1725) - Focused/NavId now always included in display range. - Any number of steps (while preserving zero-alloc policy). - Non contiguous ranges for nav processing - Moved new fields internally (+ moved StepNo away from sight so it doesn't get missused). - Generally tweaks/refactors. --- docs/CHANGELOG.txt | 7 +- imgui.cpp | 249 ++++++++++++++++++++++++--------------------- imgui.h | 49 ++++----- imgui_internal.h | 36 +++++++ 4 files changed, 194 insertions(+), 147 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 052915d2..83618619 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,7 +41,12 @@ Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) -- Clipper: Fixed content height declaration slightly mismatching the one of when not using a clipper. +- Clipper: currently focused item is automatically included in clipper range. + Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is + still submitted. (#3841, #1725) [@GamingMinds-DanielC, @ocornut] +- Clipper: rework so gamepad/keyboard navigation doesn't create spikes in number of items requested + by the clipper to display. (#3841) +- Clipper: Fixed content height declaration slightly mismatching the value of when not using a clipper. (an additional ItemSpacing.y was declared, affecting scrollbar range). - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: fixed resizing window from borders setting navigation to Menu layer. diff --git a/imgui.cpp b/imgui.cpp index d77c8ff3..d7c7385e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2248,6 +2248,8 @@ static bool GetSkipItemForListClipping() // Helper to calculate coarse clipping of large list of evenly sized items. // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX +// FIXME: This legacy API is not ideal because it assume we will return a single contiguous rectangle. +// Prefer using ImGuiListClipper which returns disconnected ranges. void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) { ImGuiContext& g = *GImGui; @@ -2266,15 +2268,16 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items } // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect - ImRect unclipped_rect = window->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) - unclipped_rect.Add(g.NavScoringRect); + rect.Add(g.NavScoringNoClipRect); if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - unclipped_rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel + rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel const ImVec2 pos = window->DC.CursorPos; - int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); - int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); + 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 if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up) @@ -2288,47 +2291,35 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items *out_items_display_end = end; } -static int SortAndFuseRanges(int* range_start, int* range_end, int range_count) +static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) { - // Helper to order ranges and fuse them together if possible. - // First sort both rangeStart and rangeEnd by rangeStart. Since this helper will just sort 2 or 3 entries, a bubble sort will do fine. - for (int sort_end = range_count - 1; sort_end > 0; --sort_end) - { - for (int i = 0; i < sort_end; ++i) - { - if (range_start[i] > range_start[i + 1]) - { - ImSwap(range_start[i], range_start[i + 1]); - ImSwap(range_end[i], range_end[i + 1]); - } - } - } + if (ranges.Size - offset <= 1) + return; + + // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries) + for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end) + for (int i = offset; i < sort_end + offset; ++i) + if (ranges[i].Min > ranges[i + 1].Min) + ImSwap(ranges[i], ranges[i + 1]); // Now fuse ranges together as much as possible. - for (int i = 1; i < range_count;) + for (int i = 1 + offset; i < ranges.Size; i++) { - if (range_end[i - 1] >= range_start[i]) - { - range_end[i - 1] = ImMax(range_end[i - 1], range_end[i]); - range_count--; - for (int j = i; j < range_count; ++j) - { - range_start[j] = range_start[j + 1]; - range_end[j] = range_end[j + 1]; - } - } - else - i++; + IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert); + if (ranges[i - 1].Max < ranges[i].Min) + continue; + ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min); + ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max); + ranges.erase(ranges.Data + i); + i--; } - - return range_count; } -static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) +static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height) { // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. - // The clipper should probably have a 4th step to display the last item in a regular manner. + // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek? ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float off_y = pos_y - window->DC.CursorPos.y; @@ -2349,6 +2340,14 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) } } +static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) +{ + // StartPosY starts from ItemsFrozen hence the subtraction + ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; + float pos_y = clipper->StartPosY + (item_n - data->ItemsFrozen) * clipper->ItemsHeight; + ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); +} + ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); @@ -2375,48 +2374,60 @@ void ImGuiListClipper::Begin(int items_count, float items_height) StartPosY = window->DC.CursorPos.y; ItemsHeight = items_height; ItemsCount = items_count; - ItemsFrozen = 0; - StepNo = 0; DisplayStart = -1; DisplayEnd = 0; + + // Acquire temporary buffer + if (++g.ClipperTempDataStacked > g.ClipperTempData.Size) + g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData()); + ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->Reset(this); + TempData = data; } void ImGuiListClipper::End() { + ImGuiContext& g = *GImGui; if (ItemsCount < 0) // Already ended return; - // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. + // 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. + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; if (ItemsCount < INT_MAX && DisplayStart >= 0) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); ItemsCount = -1; - StepNo = RangeCount; + data->StepNo = data->Ranges.Size; + + // Restore temporary buffer and fix back pointers which may be invalidated when nesting + IM_ASSERT(g.ClipperTempDataStacked > 0); + data = (--g.ClipperTempDataStacked > 0) ? &g.ClipperTempData[g.ClipperTempDataStacked - 1] : NULL; + if (data) + data->ListClipper->TempData = data; } -void ImGuiListClipper::ForceDisplayRange(int item_start, int item_end) +void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) { - if (DisplayStart < 0 && RangeCount + YRangeCount < 1) // Only allowed after Begin() and if there has not been a specified range yet. - { - RangeStart[RangeCount] = item_start; - RangeEnd[RangeCount] = item_end; - RangeCount++; - } + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; + IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. + IM_ASSERT(item_min <= item_max); + if (item_min < item_max) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); } -void ImGuiListClipper::ForceDisplayYRange(float y_min, float y_max) +void ImGuiListClipper::ForceDisplayRangeByPositions(float y_min, float y_max) { - if (DisplayStart < 0 && RangeCount + YRangeCount < 1) // Only allowed after Begin() and if there has not been a specified range yet. - { - YRangeMin[YRangeCount] = y_min; - YRangeMax[YRangeCount] = y_max; - YRangeCount++; - } + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; + IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. + IM_ASSERT(y_min <= y_max); + if (y_min < y_max) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(y_min, y_max, 0, 0)); } bool ImGuiListClipper::Step() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; ImGuiTable* table = g.CurrentTable; if (table && table->IsInsideRow) @@ -2429,46 +2440,41 @@ bool ImGuiListClipper::Step() return false; } - bool calc_clipping = false; + // While we are in frozen row state, keep displaying items one by one, unclipped + // FIXME: Could be stored as a table-agnostic state. + if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows) + { + DisplayStart = data->ItemsFrozen; + DisplayEnd = data->ItemsFrozen + 1; + data->ItemsFrozen++; + return true; + } // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) - if (StepNo == 0) + bool calc_clipping = false; + if (data->StepNo == 0) { - // While we are in frozen row state, keep displaying items one by one, unclipped - // FIXME: Could be stored as a table-agnostic state. - if (table != NULL && !table->IsUnfrozenRows) - { - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; - ItemsFrozen++; - return true; - } - StartPosY = window->DC.CursorPos.y; if (ItemsHeight <= 0.0f) { - // Submit the first item (or range) so we can measure its height (generally it is 0..1) - RangeStart[RangeCount] = ItemsFrozen; - RangeEnd[RangeCount] = ItemsFrozen + 1; - if (++RangeCount > 1) - RangeCount = SortAndFuseRanges(RangeStart, RangeEnd, RangeCount); - DisplayStart = ImMax(RangeStart[0], ItemsFrozen); - DisplayEnd = ImMin(RangeEnd[0], ItemsCount); - StepNo = 1; + // Submit the first item (or range) so we can measure its height (generally the first range is 0..1) + data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); + DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); + DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount); + data->StepNo = 1; return true; } - calc_clipping = true; // If on the first step with known item height, calculate clipping. } // Step 1: Let the clipper infer height from first range if (ItemsHeight <= 0.0f) { - IM_ASSERT(StepNo == 1); + IM_ASSERT(data->StepNo == 1); if (table) { - const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row - const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell. + const float pos_y1 = table->RowPosY1; // Using RowPosY1 instead of StartPosY to handle clipper straddling the frozen row + const float pos_y2 = table->RowPosY2; // Using RowPosY2 instead of CursorPos.y to take account of tallest cell. ItemsHeight = pos_y2 - pos_y1; window->DC.CursorPos.y = pos_y2; } @@ -2477,67 +2483,64 @@ bool ImGuiListClipper::Step() ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); } IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); - calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } - // Step 0 or 1: Calculate the actual range of visible elements. + // Step 0 or 1: Calculate the actual ranges of visible elements. + const int already_submitted = DisplayEnd; if (calc_clipping) { - IM_ASSERT(ItemsHeight > 0.0f); - - int already_submitted = DisplayEnd; - ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &RangeStart[RangeCount], &RangeEnd[RangeCount]); - - // Only add another range if it hasn't been handled by the initial range. - if (RangeStart[RangeCount] < RangeEnd[RangeCount]) + if (g.LogEnabled) { - RangeStart[RangeCount] += already_submitted; - RangeEnd[RangeCount] += already_submitted; - RangeCount++; + // If logging is active, do not perform any clipping + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, ItemsCount)); } - - // Convert specified y ranges to item index ranges. - for (int i = 0; i < YRangeCount; ++i) + else { - int start = already_submitted + (int)((YRangeMin[i] - window->DC.CursorPos.y) / ItemsHeight); - int end = already_submitted + (int)((YRangeMax[i] - window->DC.CursorPos.y) / ItemsHeight) + 1; + // Add range selected to be included for navigation + if (g.NavMoveScoringItems) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); - start = ImMax(start, already_submitted); - end = ImMin(end, ItemsCount); + // Add focused/active item + ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); + if (g.NavId != 0 && window->NavLastIds[0] == g.NavId) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); - if (start < end) - { - RangeStart[RangeCount] = start; - RangeEnd[RangeCount] = end; - RangeCount++; - } + // Add visible range + const int off_min = (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; + const int off_max = (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max)); } - // Try to sort and fuse only if there is more than 1 range remaining. - if (RangeCount > StepNo + 1) - RangeCount = StepNo + SortAndFuseRanges(&RangeStart[StepNo], &RangeEnd[StepNo], RangeCount - StepNo); + // Convert position ranges to item index ranges + // - 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) + { + data->Ranges[i].Min = ImClamp(already_submitted + (int)ImFloor((data->Ranges[i].Min - window->DC.CursorPos.y) / ItemsHeight) + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); + data->Ranges[i].Max = ImClamp(already_submitted + (int)ImCeil((data->Ranges[i].Max - window->DC.CursorPos.y) / ItemsHeight) + 0 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); + data->Ranges[i].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 (StepNo < RangeCount) + if (data->StepNo < data->Ranges.Size) { - int already_submitted = DisplayEnd; - DisplayStart = ImMax(RangeStart[StepNo], already_submitted); - DisplayEnd = ImMin(RangeEnd[StepNo], ItemsCount); - - // Seek cursor - if (DisplayStart > already_submitted) - SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight); - - StepNo++; + DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); + DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, ItemsCount); + if (DisplayStart > already_submitted) //-V1051 + ImGuiListClipper_SeekCursorForItem(this, DisplayStart); + data->StepNo++; return true; } // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), // Advance the cursor to the end of the list and then returns 'false' to end the loop. if (ItemsCount < INT_MAX) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); ItemsCount = -1; return false; @@ -4336,6 +4339,8 @@ void ImGui::Shutdown(ImGuiContext* context) g.CurrentTabBarStack.clear(); g.ShrinkWidthBuffer.clear(); + g.ClipperTempData.clear_destruct(); + g.Tables.Clear(); g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); @@ -9605,6 +9610,7 @@ void ImGui::NavUpdateCreateMoveRequest() if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } } g.NavMoveClipDir = g.NavMoveDir; + g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); } // Update PageUp/PageDown/Home/End scroll @@ -9613,6 +9619,11 @@ void ImGui::NavUpdateCreateMoveRequest() float scoring_rect_offset_y = 0.0f; if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) scoring_rect_offset_y = NavUpdatePageUpPageDown(); + if (scoring_rect_offset_y != 0.0f) + { + g.NavScoringNoClipRect = window->InnerRect; + g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y); + } // [DEBUG] Always send a request #if IMGUI_DEBUG_NAV_SCORING @@ -9666,8 +9677,10 @@ void ImGui::NavUpdateCreateMoveRequest() scoring_rect.Max.x = scoring_rect.Min.x; IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows 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] } g.NavScoringRect = scoring_rect; + g.NavScoringNoClipRect.Add(scoring_rect); } // Apply result from previous frame navigation directional move request. Always called from NavUpdate() diff --git a/imgui.h b/imgui.h index 132375e6..cfc813e8 100644 --- a/imgui.h +++ b/imgui.h @@ -2172,52 +2172,45 @@ struct ImGuiStorage }; // Helper: Manually clip large list of items. -// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse -// clipping based on visibility to save yourself from processing those items at all. +// If you have lots evenly spaced items and you have a random access to the list, you can perform coarse +// clipping based on visibility to only submit items that are in view. // The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. -// (Dear ImGui already clip items based on their bounds but it needs to measure text size to do so, whereas manual coarse clipping before submission makes this cost and your own data fetching/submission cost almost null) +// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally +// fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily +// scale using lists with tens of thousands of items without a problem) // Usage: // ImGuiListClipper clipper; // clipper.Begin(1000); // We have 1000 elements, evenly spaced. -// clipper.ForceDisplay(42); // Optional, force element with given index to be displayed (use f.e. if you need to update a tooltip for a drag&drop source) // while (clipper.Step()) // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) // ImGui::Text("line number %d", i); // Generally what happens is: // - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not. -// - User code submit one element. +// - User code submit that one element. // - Clipper can measure the height of the first element // - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element. // - User code submit visible elements. +// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc. struct ImGuiListClipper { - int DisplayStart; - int DisplayEnd; - - // [Internal] - int ItemsCount; - int RangeStart[4]; // 1 for the user, rest for internal use - int RangeEnd[4]; - int RangeCount; - int YRangeMin[1]; - int YRangeMax[1]; - int YRangeCount; - int StepNo; - int ItemsFrozen; - float ItemsHeight; - float StartPosY; + int DisplayStart; // First item to display, updated by each call to Step() + int DisplayEnd; // End of items to display (exclusive) + int ItemsCount; // [Internal] Number of items + float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it + float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed + void* TempData; // [Internal] Internal data + // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) + // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). IMGUI_API ImGuiListClipper(); IMGUI_API ~ImGuiListClipper(); + IMGUI_API void Begin(int items_count, float items_height = -1.0f); + 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. - // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) - // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). - IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. - IMGUI_API void ForceDisplayRange(int item_start, int item_end); // Optionally call before the first call to Step() if you need a range of items to be displayed regardless of visibility. - inline void ForceDisplay(int item_start, int item_count = 1) { ForceDisplayRange(item_start, item_start + item_count); } // Like ForceDisplayRange, but with a number instead of an end index. - IMGUI_API void ForceDisplayYRange(float y_min, float y_max); // Like ForceDisplayRange, but with y coordinates instead of item indices. - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + // Call ForceDisplayRangeXXX functions before first call to Step() if you need a range of items to be displayed regardless of visibility. + IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. + IMGUI_API void ForceDisplayRangeByPositions(float y_min, float y_max); // Absolute coordinates #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 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] diff --git a/imgui_internal.h b/imgui_internal.h index 3373db06..cf1f1b37 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -18,6 +18,7 @@ Index of this file: // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Clipper support // [SECTION] Navigation support // [SECTION] Columns support // [SECTION] Multi-select support @@ -1162,6 +1163,34 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// [SECTION] Clipper support +//----------------------------------------------------------------------------- + +struct ImGuiListClipperRange +{ + int Min; + int Max; + bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later) + ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices + ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices + + static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; } + static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; } +}; + +// Temporary clipper data, buffers shared/reused between instances +struct ImGuiListClipperData +{ + ImGuiListClipper* ListClipper; + int StepNo; + int ItemsFrozen; + ImVector Ranges; + + ImGuiListClipperData() { memset(this, 0, sizeof(*this)); } + void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } +}; + //----------------------------------------------------------------------------- // [SECTION] Navigation support //----------------------------------------------------------------------------- @@ -1583,6 +1612,7 @@ struct ImGuiContext ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. + ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted int NavScoringDebugCount; // Metrics for debugging int NavTabbingInputableRemaining; // >0 when counting items for tabbing ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow @@ -1627,6 +1657,10 @@ struct ImGuiContext ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads + // Clipper + int ClipperTempDataStacked; + ImVector ClipperTempData; + // Table ImGuiTable* CurrentTable; int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) @@ -1814,6 +1848,8 @@ struct ImGuiContext DragDropHoldJustPressedId = 0; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); + ClipperTempDataStacked = 0; + CurrentTable = NULL; TablesTempDataStacked = 0; CurrentTabBar = NULL; From 6a7e2c74fb079fe21953307c48447b880f1437d0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Nov 2021 13:55:37 +0100 Subject: [PATCH 128/332] Clipper: remove ForceDisplayRangeByIndices/ForceDisplayRangeByPositions functions until we find a need for them, since #3841 is now solved automatically. --- docs/TODO.txt | 2 +- imgui.cpp | 18 ------------------ imgui.h | 4 ---- 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 95a9326f..27b6f1f3 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -142,7 +142,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - plot: option/feature: draw unit - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) - - clipper: ability to force display 1 item in the list would be convenient (for patterns where we need to set active id etc.) + - clipper: ability to force display 1 item in the list would be convenient (for patterns where we need to set active id etc.) (#3841) (can resurrect ForceDisplayRangeXXX functions removed) - clipper: ability to disable the clipping through a simple flag/bool. - clipper: ability to run without knowing full count in advance. - clipper: horizontal clipping support. (#2580) diff --git a/imgui.cpp b/imgui.cpp index d7c7385e..7654e3bc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2405,24 +2405,6 @@ void ImGuiListClipper::End() data->ListClipper->TempData = data; } -void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) -{ - ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; - IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. - IM_ASSERT(item_min <= item_max); - if (item_min < item_max) - data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); -} - -void ImGuiListClipper::ForceDisplayRangeByPositions(float y_min, float y_max) -{ - ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; - IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. - IM_ASSERT(y_min <= y_max); - if (y_min < y_max) - data->Ranges.push_back(ImGuiListClipperRange::FromPositions(y_min, y_max, 0, 0)); -} - bool ImGuiListClipper::Step() { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index cfc813e8..cf3c3198 100644 --- a/imgui.h +++ b/imgui.h @@ -2208,10 +2208,6 @@ 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 ForceDisplayRangeXXX functions before first call to Step() if you need a range of items to be displayed regardless of visibility. - IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. - IMGUI_API void ForceDisplayRangeByPositions(float y_min, float y_max); // Absolute coordinates - #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 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 From 64daeddf6f6ca877e2a4ed3b8f0eee926cc465fd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Nov 2021 14:14:16 +0100 Subject: [PATCH 129/332] Removed CalcListClipping() function. (#3841) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 11 ++++++----- imgui.h | 5 +++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 83618619..defb517e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,6 +36,9 @@ HOW TO UPDATE? Breaking Changes: +- Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. + Please open an issue if you think you really need this function. (#3841) + Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] diff --git a/imgui.cpp b/imgui.cpp index 7654e3bc..f1c1b3c7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -380,6 +380,7 @@ 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. + - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() @@ -2245,11 +2246,10 @@ static bool GetSkipItemForListClipping() return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); } -// Helper to calculate coarse clipping of large list of evenly sized items. -// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. -// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX -// FIXME: This legacy API is not ideal because it assume we will return a single contiguous rectangle. -// Prefer using ImGuiListClipper which returns disconnected ranges. +#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 assume 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; @@ -2290,6 +2290,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items *out_items_display_start = start; *out_items_display_end = end; } +#endif static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) { diff --git a/imgui.h b/imgui.h index cf3c3198..4ef88105 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18505 +#define IMGUI_VERSION_NUM 18506 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -869,7 +869,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 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 the ImGuiListClipper higher-level helper if you can. 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) @@ -2829,6 +2828,8 @@ struct ImGuiViewport #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // 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.81 (from February 2021) From 32779c5b648a7f9631c16de4cbd078fe2f234f99 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Nov 2021 15:14:35 +0100 Subject: [PATCH 130/332] Clipper: hotfix (amend 93cccd2, was broken) (#3841, #1725) --- imgui.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f1c1b3c7..6a527121 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2357,7 +2357,7 @@ ImGuiListClipper::ImGuiListClipper() ImGuiListClipper::~ImGuiListClipper() { - IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); + End(); } // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 @@ -2388,22 +2388,24 @@ void ImGuiListClipper::Begin(int items_count, float items_height) void ImGuiListClipper::End() { - ImGuiContext& g = *GImGui; - if (ItemsCount < 0) // Already ended - return; - // 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. - ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; - if (ItemsCount < INT_MAX && DisplayStart >= 0) + ImGuiContext& g = *GImGui; + if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) ImGuiListClipper_SeekCursorForItem(this, ItemsCount); ItemsCount = -1; - data->StepNo = data->Ranges.Size; // Restore temporary buffer and fix back pointers which may be invalidated when nesting - IM_ASSERT(g.ClipperTempDataStacked > 0); - data = (--g.ClipperTempDataStacked > 0) ? &g.ClipperTempData[g.ClipperTempDataStacked - 1] : NULL; - if (data) - data->ListClipper->TempData = data; + if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) + { + IM_ASSERT(data->ListClipper == this); + data->StepNo = data->Ranges.Size; + if (--g.ClipperTempDataStacked > 0) + { + data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->ListClipper->TempData = data; + } + TempData = NULL; + } } bool ImGuiListClipper::Step() From f01a6d3e8fe7de24fe76e33888b50a82d6808ea4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Nov 2021 19:49:39 +0100 Subject: [PATCH 131/332] InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) + Stack Tool: favor inspecting HoveredID over ActiveID as the later is more likely to be locked. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 +++-- imgui.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index defb517e..71989d1c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,6 +51,7 @@ Other Changes: by the clipper to display. (#3841) - Clipper: Fixed content height declaration slightly mismatching the value of when not using a clipper. (an additional ItemSpacing.y was declared, affecting scrollbar range). +- InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: fixed resizing window from borders setting navigation to Menu layer. - Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse. diff --git a/imgui.cpp b/imgui.cpp index 6a527121..98aad1ac 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9717,8 +9717,9 @@ void ImGui::NavMoveRequestApplyResult() } } - ClearActiveID(); g.NavWindow = result->Window; + if (g.ActiveId != result->ID) + ClearActiveID(); if (g.NavId != result->ID) { // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) @@ -12188,7 +12189,7 @@ void ImGui::UpdateDebugToolStackQueries() // Update queries. The steps are: -1: query Stack, >= 0: query each stack item // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time - const ImGuiID query_id = g.ActiveId ? g.ActiveId : g.HoveredIdPreviousFrame; + const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; if (tool->QueryId != query_id) { tool->QueryId = query_id; diff --git a/imgui.h b/imgui.h index 4ef88105..2eda0cfa 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18506 +#define IMGUI_VERSION_NUM 18507 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 1a7526dcd4aa73e9b5fa95244f4308e8ed66229f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 8 Oct 2021 15:24:09 +0200 Subject: [PATCH 132/332] Nav, Tabbing: refactor tabbing system to support clipped items, scrolling, using nav queries. (#4449) Not using counter/modulo anymore and special provisions to handle tab wrapping with ImGuiListClipper. Wrapping may be done better as a next-frame forwarded request. Also one extra step toward #3949 #3985 --- docs/CHANGELOG.txt | 20 ++-- docs/TODO.txt | 1 - imgui.cpp | 232 ++++++++++++++++++++++----------------------- imgui.h | 2 +- imgui_internal.h | 35 +++---- 5 files changed, 138 insertions(+), 152 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 71989d1c..c8ca45d6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,16 @@ Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) +- Nav: tabbing now cycles through clipped items and scroll accordingly. (#4449) +- Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. +- Nav: fixed resizing window from borders setting navigation to Menu layer. +- Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse. +- Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using + Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when + already at edge) reactivates the navigation highlight. +- InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) +- InputText: made double-click select word, triple-line select line. Word delimitation logic differs + slightly from the one used by CTRL+arrows. (#2244) - Clipper: currently focused item is automatically included in clipper range. Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is still submitted. (#3841, #1725) [@GamingMinds-DanielC, @ocornut] @@ -51,16 +61,6 @@ Other Changes: by the clipper to display. (#3841) - Clipper: Fixed content height declaration slightly mismatching the value of when not using a clipper. (an additional ItemSpacing.y was declared, affecting scrollbar range). -- InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) -- Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. -- Nav: fixed resizing window from borders setting navigation to Menu layer. -- Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse. -- Nav: (with ImGuiConfigFlags_NavEnableSetMousePos): fixed absolute mouse position when using Home/End - leads to scrolling. -- Nav: (with ImGuiConfigFlags_NavEnableSetMousePos): fixed not setting mouse position when a failed - move request (e.g. already at edge) reactivate the navigation highlight. -- InputText: made double-click select word, triple-line select line. Word delimitation logic differs - slightly from the one used by CTRL+arrows. (#2244) - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) diff --git a/docs/TODO.txt b/docs/TODO.txt index 27b6f1f3..b5f69e8b 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -341,7 +341,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child are currently not considered to enter into a NavFlattened child. - nav: NavFlattened: cannot access menu-bar of a flattened child window with Alt/menu key (not a very common use case..). - nav: simulate right-click or context activation? (SHIFT+F10) - - nav/tabbing: refactor old tabbing system and turn into navigation, should pass through all widgets (in submission order?). - nav/popup: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys, default validation button, etc. - nav/treenode: left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?) - nav/menus: pressing left-right on a vertically clipped menu bar tends to jump to the collapse/close buttons. diff --git a/imgui.cpp b/imgui.cpp index 98aad1ac..5cc01f31 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -916,6 +916,7 @@ static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); static void NavUpdateCancelRequest(); static void NavUpdateCreateMoveRequest(); +static void NavUpdateCreateTabbingRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); static void NavUpdateCreateWrappingRequest(); @@ -923,6 +924,7 @@ static void NavEndFrame(); static bool NavScoreItem(ImGuiNavItemData* result); static void NavApplyItemToResult(ImGuiNavItemData* result); static void NavProcessItem(); +static void NavProcessItemForTabbingRequest(ImGuiWindow* window, ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); @@ -940,7 +942,6 @@ static void UpdateDebugToolStackQueries(); static void UpdateSettings(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); -static void UpdateTabFocus(); 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 void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); @@ -2280,9 +2281,11 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items 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 - if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up) + // 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 (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down) + if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) end++; start = ImClamp(start, 0, items_count); @@ -2483,8 +2486,11 @@ bool ImGuiListClipper::Step() else { // Add range selected to be included for navigation - if (g.NavMoveScoringItems) + 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) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(ItemsCount - 1, ItemsCount)); // Add focused/active item ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); @@ -2492,8 +2498,8 @@ bool ImGuiListClipper::Step() data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); // Add visible range - const int off_min = (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; - const int off_max = (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; + const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; + const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max)); } @@ -3179,7 +3185,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } // Clear declaration of inputs claimed by the widget @@ -3367,48 +3373,6 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) return false; } -// Called by ItemAdd() -// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. -// [WIP] This will eventually be refactored and moved into NavProcessItem() -void ImGui::ItemInputable(ImGuiWindow* window, ImGuiID id) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(id != 0 && id == g.LastItemData.ID); - - // Increment counters - // FIXME: ImGuiItemFlags_Disabled should disable more. - const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - if (is_tab_stop) - { - window->DC.FocusCounterTabStop++; - if (g.NavId == id) - g.NavIdTabCounter = window->DC.FocusCounterTabStop; - } - - // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. - // (Note that we can always TAB out of a widget that doesn't allow tabbing in) - if (g.ActiveId == id && g.TabFocusPressed && g.TabFocusRequestNextWindow == NULL) - { - g.TabFocusRequestNextWindow = window; - g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - } - - // Handle focus requests - if (g.TabFocusRequestCurrWindow == window) - { - if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) - { - g.NavJustTabbedId = id; // FIXME-NAV: aim to eventually set in NavUpdate() once we finish the refactor - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing; - return; - } - - // If another item is about to be focused, we clear our own active id - if (g.ActiveId == id) - ClearActiveID(); - } -} - float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) { if (wrap_pos_x < 0.0f) @@ -3903,44 +3867,6 @@ void ImGui::UpdateMouseWheel() } } -void ImGui::UpdateTabFocus() -{ - ImGuiContext& g = *GImGui; - - // Pressing TAB activate widget focus - g.TabFocusPressed = false; - if (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - if (!g.IO.KeyCtrl && !g.IO.KeyAlt && IsKeyPressedMap(ImGuiKey_Tab) && !IsActiveIdUsingKey(ImGuiKey_Tab)) - g.TabFocusPressed = true; - if (g.ActiveId == 0 && g.TabFocusPressed) - { - // - This path is only taken when no widget are active/tabbed-into yet. - // Subsequent tabbing will be processed by FocusableItemRegister() - // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also - // manipulate the Next fields here even though they will be turned into Curr fields below. - g.TabFocusRequestNextWindow = g.NavWindow; - if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0); - else - g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; - } - - // Turn queued focus request into current one - g.TabFocusRequestCurrWindow = NULL; - g.TabFocusRequestCurrCounterTabStop = INT_MAX; - if (g.TabFocusRequestNextWindow != NULL) - { - ImGuiWindow* window = g.TabFocusRequestNextWindow; - g.TabFocusRequestCurrWindow = window; - if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) - g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); - g.TabFocusRequestNextWindow = NULL; - g.TabFocusRequestNextCounterTabStop = INT_MAX; - } - - g.NavIdTabCounter = INT_MAX; -} - // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) void ImGui::UpdateHoveredWindowAndCaptureFlags() { @@ -4186,9 +4112,6 @@ void ImGui::NewFrame() // Mouse wheel scrolling, scale UpdateMouseWheel(); - // Update legacy TAB focus - UpdateTabFocus(); - // 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; @@ -6399,7 +6322,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrentColumns = NULL; window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.FocusCounterTabStop = -1; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -7185,11 +7107,16 @@ void ImGui::SetKeyboardFocusHere(int offset) IM_ASSERT(offset >= -1); // -1 is allowed but not below g.NavWindow = window; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; - NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_None, ImGuiNavMoveFlags_Tabbing, 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, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. if (offset == -1) - NavMoveRequestResolveWithLastItem(); + { + NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); + } else - g.NavTabbingInputableRemaining = offset + 1; + { + g.NavTabbingDir = 1; + g.NavTabbingCounter = offset + 1; + } } void ImGui::SetItemDefaultFocus() @@ -7671,11 +7598,6 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] - // [WIP] Tab stop handling (previously was using internal FocusableItemRegister() api) - // FIXME-NAV: We would now want to move this before the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343) - if (extra_flags & ImGuiItemFlags_Inputable) - ItemInputable(window, id); - // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (IsMouseHoveringRect(bb.Min, bb.Max)) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; @@ -9117,19 +9039,17 @@ 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) { - if (item_flags & ImGuiItemFlags_Inputable) - g.NavTabbingInputableRemaining--; - - if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + const bool is_tab_stop = (item_flags & ImGuiItemFlags_Inputable) && (item_flags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; + const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; + if (is_tabbing) + { + if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)) + NavProcessItemForTabbingRequest(window, id); + } + else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) { ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - - if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) - { - if (g.NavTabbingInputableRemaining == 0) - NavMoveRequestResolveWithLastItem(); - } - else + if (!is_tabbing) { if (NavScoreItem(result)) NavApplyItemToResult(result); @@ -9155,6 +9075,52 @@ static void ImGui::NavProcessItem() } } +// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest(). +// Note that SetKeyboardFocusHere() API calls are considered tabbing requests! +// - Case 1: no nav/active id: set result to first eligible item, stop storing. +// - Case 2: tab forward: on ref id set counter, on counter elapse store result +// - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request +// - Case 4: tab backward: store all results, on ref id pick prev, stop storing +// - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested +void ImGui::NavProcessItemForTabbingRequest(ImGuiWindow* window, ImGuiID id) +{ + ImGuiContext& g = *GImGui; + + ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + if (g.NavTabbingDir == +1) + { + // Tab Forward or SetKeyboardFocusHere() with >= 0 + if (g.NavTabbingResultFirst.ID == 0) + NavApplyItemToResult(&g.NavTabbingResultFirst); + if (--g.NavTabbingCounter == 0) + NavMoveRequestResolveWithLastItem(result); + else if (g.NavId == id) + g.NavTabbingCounter = 1; + } + else if (g.NavTabbingDir == -1) + { + // Tab Backward + if (g.NavId == id) + { + if (result->ID) + { + g.NavMoveScoringItems = false; + NavUpdateAnyRequestFlag(); + } + } + else + { + NavApplyItemToResult(result); + } + } + else if (g.NavTabbingDir == 0) + { + // Tab Init + if (g.NavTabbingResultFirst.ID == 0) + NavMoveRequestResolveWithLastItem(&g.NavTabbingResultFirst); + } +} + bool ImGui::NavMoveRequestButNoResultYet() { ImGuiContext& g = *GImGui; @@ -9178,18 +9144,18 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM g.NavMoveScrollFlags = scroll_flags; g.NavMoveForwardToNextFrame = false; g.NavMoveKeyMods = g.IO.KeyMods; - g.NavTabbingInputableRemaining = 0; + g.NavTabbingCounter = 0; g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultOther.Clear(); NavUpdateAnyRequestFlag(); } -void ImGui::NavMoveRequestResolveWithLastItem() +void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; g.NavMoveScoringItems = false; // Ensure request doesn't need more processing - NavApplyItemToResult(&g.NavMoveResultLocal); + NavApplyItemToResult(result); NavUpdateAnyRequestFlag(); } @@ -9424,7 +9390,7 @@ static void ImGui::NavUpdate() // Process navigation move request if (g.NavMoveSubmitted) NavMoveRequestApplyResult(); - g.NavTabbingInputableRemaining = 0; + g.NavTabbingCounter = 0; g.NavMoveSubmitted = g.NavMoveScoringItems = false; // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) @@ -9433,8 +9399,6 @@ static void ImGui::NavUpdate() if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) set_mouse_pos = true; g.NavMousePosDirty = false; - g.NavIdIsAlive = false; - g.NavJustTabbedId = 0; IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 @@ -9496,7 +9460,10 @@ static void ImGui::NavUpdate() // Process move requests NavUpdateCreateMoveRequest(); + if (g.NavMoveDir == ImGuiDir_None) + NavUpdateCreateTabbingRequest(); NavUpdateAnyRequestFlag(); + g.NavIdIsAlive = false; // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) @@ -9668,6 +9635,31 @@ void ImGui::NavUpdateCreateMoveRequest() g.NavScoringNoClipRect.Add(scoring_rect); } +void ImGui::NavUpdateCreateTabbingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; + IM_ASSERT(g.NavMoveDir == ImGuiDir_None); + if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) + return; + + const bool tab_pressed = IsKeyPressedMap(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !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. + //// FIXME: We use (g.ActiveId == 0) but (g.NavDisableHighlight == false) might be righter once we can tab through anything + g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; + 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. + g.NavTabbingResultFirst.Clear(); + g.NavTabbingCounter = -1; +} + // Apply result from previous frame navigation directional move request. Always called from NavUpdate() void ImGui::NavMoveRequestApplyResult() { @@ -9680,6 +9672,14 @@ void ImGui::NavMoveRequestApplyResult() // Select which result to use ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; + // Tabbing forward wrap + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + if (g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) + { + IM_ASSERT(g.NavTabbingResultFirst.ID != 0); + result = &g.NavTabbingResultFirst; + } + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) if (result == NULL) { diff --git a/imgui.h b/imgui.h index 2eda0cfa..e0b1f04d 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18507 +#define IMGUI_VERSION_NUM 18508 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index cf1f1b37..258d3433 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1247,9 +1247,10 @@ 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_Tabbing = 1 << 9, // == Focus + Activate if item is Inputable + DontChangeNavHighlight - ImGuiNavMoveFlags_Activate = 1 << 10, - ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 11 // Do not alter the visible state of keyboard vs mouse nav highlight + 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 }; enum ImGuiNavLayer @@ -1582,7 +1583,6 @@ struct ImGuiContext ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. ImGuiActivateFlags NavActivateFlags; - ImGuiID NavJustTabbedId; // Just tabbed to this id. 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). ImGuiKeyModFlags NavJustMovedToKeyMods; @@ -1590,7 +1590,6 @@ struct ImGuiContext ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. 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. - int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing 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) @@ -1614,10 +1613,12 @@ struct ImGuiContext ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted int NavScoringDebugCount; // Metrics for debugging - int NavTabbingInputableRemaining; // >0 when counting items for tabbing + int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id + int NavTabbingCounter; // >0 when counting items for tabbing ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + 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) ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! @@ -1627,13 +1628,6 @@ struct ImGuiContext float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; - // Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!) - ImGuiWindow* TabFocusRequestCurrWindow; // - ImGuiWindow* TabFocusRequestNextWindow; // - int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index - int TabFocusRequestNextCounterTabStop; // " - bool TabFocusPressed; // Set in NewFrame() when user pressed Tab - // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) ImGuiMouseCursor MouseCursor; @@ -1800,12 +1794,11 @@ struct ImGuiContext NavWindow = NULL; NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavInputSource = ImGuiInputSource_None; NavLayer = ImGuiNavLayer_Main; - NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; NavDisableHighlight = true; @@ -1822,17 +1815,13 @@ struct ImGuiContext NavMoveKeyMods = ImGuiKeyModFlags_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavScoringDebugCount = 0; - NavTabbingInputableRemaining = 0; + NavTabbingDir = 0; + NavTabbingCounter = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; - TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL; - TabFocusRequestCurrCounterTabStop = INT_MAX; - TabFocusRequestNextCounterTabStop = INT_MAX; - TabFocusPressed = false; - DimBgRatio = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; @@ -1939,7 +1928,6 @@ 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() - int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // 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. @@ -2501,7 +2489,6 @@ namespace ImGui IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f); 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 void ItemInputable(ImGuiWindow* window, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); @@ -2561,7 +2548,7 @@ namespace ImGui IMGUI_API bool NavMoveRequestButNoResultYet(); 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(); + IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); From ebac375791c0d27af54e44d3fdbf82a27c4b7ad9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 8 Nov 2021 16:20:57 +0100 Subject: [PATCH 133/332] Docs: added info about Freetype, editing sponsors. --- docs/README.md | 4 ++-- misc/freetype/imgui_freetype.cpp | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 9d6dc93f..7997a80b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -204,10 +204,10 @@ Ongoing Dear ImGui development is currently financially supported by users and p - [Google](https://github.com/google/filament), [Nvidia](https://developer.nvidia.com/nvidia-omniverse), [Ubisoft](https://montreal.ubisoft.com/en/ubisoft-sponsors-user-interface-library-for-c-dear-imgui) *Chocolate sponsors* -- [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras PranckeviÄius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [RAD Game Tools](http://www.radgametools.com/) +- [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras PranckeviÄius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [RAD Game Tools](http://www.radgametools.com/), [Supercell](https://supercell.com) *Salty-caramel sponsors* -- [Framefield](http://framefield.com), [Grinding Gear Games](https://www.grindinggear.com), [Kylotonn](https://www.kylotonn.com), [Next Level Games](https://www.nextlevelgames.com), [O-Net Communications (USA)](http://en.o-netcom.com), [Supercell](https://supercell.com). +- [Framefield](http://framefield.com), [Grinding Gear Games](https://www.grindinggear.com), [Kylotonn](https://www.kylotonn.com), [Next Level Games](https://www.nextlevelgames.com), [O-Net Communications (USA)](http://en.o-netcom.com) Please see [detailed list of Dear ImGui supporters](https://github.com/ocornut/imgui/wiki/Sponsors) for past sponsors. From November 2014 to December 2019, ongoing development has also been financially supported by its users on Patreon and through individual donations. diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index a8ec51b3..a72ec8c8 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -226,6 +226,12 @@ namespace uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint); if (glyph_index == 0) return NULL; + + // If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts. + // - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076 + // - https://github.com/ocornut/imgui/issues/4567 + // - https://github.com/ocornut/imgui/issues/4566 + // You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version. FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags); if (error) return NULL; From bce1ba400fd7167174586f2bb32eb4caab1a2848 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 8 Nov 2021 17:16:52 +0100 Subject: [PATCH 134/332] Clipper: add ForceDisplayRangeByIndices (#3841, #3578) This partially reverts commit 6a7e2c74fb079fe21953307c48447b880f1437d0. --- docs/CHANGELOG.txt | 4 +++- docs/TODO.txt | 1 - imgui.cpp | 9 +++++++++ imgui.h | 5 ++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c8ca45d6..6b51f36a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -57,9 +57,11 @@ Other Changes: - Clipper: currently focused item is automatically included in clipper range. Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is still submitted. (#3841, #1725) [@GamingMinds-DanielC, @ocornut] +- Cliooer: added ForceDisplayRangeByIndices() to force a given item (or several) to be stepped out + during a clipping operation. (#3841) [@@GamingMinds-DanielC] - Clipper: rework so gamepad/keyboard navigation doesn't create spikes in number of items requested by the clipper to display. (#3841) -- Clipper: Fixed content height declaration slightly mismatching the value of when not using a clipper. +- Clipper: fixed content height declaration slightly mismatching the value of when not using a clipper. (an additional ItemSpacing.y was declared, affecting scrollbar range). - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling diff --git a/docs/TODO.txt b/docs/TODO.txt index b5f69e8b..9d005ed4 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -142,7 +142,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - plot: option/feature: draw unit - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) - - clipper: ability to force display 1 item in the list would be convenient (for patterns where we need to set active id etc.) (#3841) (can resurrect ForceDisplayRangeXXX functions removed) - clipper: ability to disable the clipping through a simple flag/bool. - clipper: ability to run without knowing full count in advance. - clipper: horizontal clipping support. (#2580) diff --git a/imgui.cpp b/imgui.cpp index 5cc01f31..91cf1e37 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2411,6 +2411,15 @@ void ImGuiListClipper::End() } } +void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) +{ + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; + IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. + IM_ASSERT(item_min <= item_max); + if (item_min < item_max) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); +} + bool ImGuiListClipper::Step() { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index e0b1f04d..cf50f044 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18508 +#define IMGUI_VERSION_NUM 18509 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -2207,6 +2207,9 @@ 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 ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. + IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 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 From 8ce23b3ccd5b9338002f58679a3fc53559d3280b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 8 Nov 2021 17:41:36 +0100 Subject: [PATCH 135/332] Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of _NavEnableKeyboard. (#4023, #767) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 6 +++--- imgui_demo.cpp | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6b51f36a..7f17d510 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,6 +44,8 @@ Other Changes: - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) +- Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of using the _NavEnableKeyboard + configuration flag. This is part of an effort to generalize the use of keyboard inputs. (#4023, #787). - Nav: tabbing now cycles through clipped items and scroll accordingly. (#4449) - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: fixed resizing window from borders setting navigation to Menu layer. diff --git a/imgui.cpp b/imgui.cpp index 91cf1e37..c956ed6a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10019,10 +10019,9 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingTargetAnim = NULL; } - // Start CTRL-TAB or Square+L/R window selection - const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + // Start CTRL+Tab or Square+L/R window selection const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && nav_keyboard_active && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { @@ -10073,6 +10072,7 @@ 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. + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) { g.NavWindowingToggleLayer = true; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 29bcf132..cf2e0d3b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -210,7 +210,8 @@ void ImGui::ShowUserGuide() "(double-click to auto fit window to its contents)."); ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); - if (io.FontAllowUserScaling) + ImGui::BulletText("CTRL+Tab to select a window."); + if (io.FontAllowUserScaling) ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); ImGui::BulletText("While inputing text:\n"); ImGui::Indent(); @@ -228,7 +229,6 @@ void ImGui::ShowUserGuide() ImGui::BulletText("Return to input text into a widget."); ImGui::BulletText("Escape to deactivate a widget, close popup, exit child window."); ImGui::BulletText("Alt to jump to the menu layer of a window."); - ImGui::BulletText("CTRL+Tab to select a window."); ImGui::Unindent(); } From eb02dcf87f1a62afc0bc4a5a05e71dc4e6333cde Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 8 Nov 2021 18:42:07 +0100 Subject: [PATCH 136/332] Nav: enable move/resize window with keyboard in Ctrl+Tabbing windowing menu even without _NavEnableKeyboard. (#4023, #787). --- docs/CHANGELOG.txt | 1 + imgui.cpp | 6 ++++-- imgui_internal.h | 7 ++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7f17d510..0e31d5ec 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,7 @@ Other Changes: - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) - Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of using the _NavEnableKeyboard configuration flag. This is part of an effort to generalize the use of keyboard inputs. (#4023, #787). + Note that while this is active you can also moving windows (with arrow) and resize (shift+arrows). - Nav: tabbing now cycles through clipped items and scroll accordingly. (#4449) - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: fixed resizing window from borders setting navigation to Menu layer. diff --git a/imgui.cpp b/imgui.cpp index c956ed6a..98bdf049 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5523,7 +5523,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { ImVec2 nav_resize_delta; if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); if (g.NavInputSource == ImGuiInputSource_Gamepad) nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) @@ -9335,6 +9335,8 @@ float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) { ImVec2 delta(0.0f, 0.0f); + if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) + delta += ImVec2((float)IsKeyDown(GetKeyIndex(ImGuiKey_RightArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_LeftArrow)), (float)IsKeyDown(GetKeyIndex(ImGuiKey_DownArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_UpArrow))); if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) @@ -10101,7 +10103,7 @@ static void ImGui::NavUpdateWindowing() { ImVec2 move_delta; if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); if (g.NavInputSource == ImGuiInputSource_Gamepad) move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); if (move_delta.x != 0.0f || move_delta.y != 0.0f) diff --git a/imgui_internal.h b/imgui_internal.h index 258d3433..448c2b13 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1230,9 +1230,10 @@ enum ImGuiNavHighlightFlags_ enum ImGuiNavDirSourceFlags_ { ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_Keyboard = 1 << 0, - ImGuiNavDirSourceFlags_PadDPad = 1 << 1, - ImGuiNavDirSourceFlags_PadLStick = 1 << 2 + ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), faciliate use of some functions before we can unify nav and keys + ImGuiNavDirSourceFlags_Keyboard = 1 << 1, + ImGuiNavDirSourceFlags_PadDPad = 1 << 2, + ImGuiNavDirSourceFlags_PadLStick = 1 << 3 }; enum ImGuiNavMoveFlags_ From a9ea1ae854e864a7761c5c030c195e65b45656e0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Nov 2021 11:42:39 +0100 Subject: [PATCH 137/332] Nav: fixing assert using Tab in a window without any tabbable element (#4449) Amend 1a7526dc --- imgui.cpp | 5 +---- imgui.h | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 98bdf049..a152ae9f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9685,11 +9685,8 @@ void ImGui::NavMoveRequestApplyResult() // Tabbing forward wrap if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) - if (g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) - { - IM_ASSERT(g.NavTabbingResultFirst.ID != 0); + if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) result = &g.NavTabbingResultFirst; - } // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) if (result == NULL) diff --git a/imgui.h b/imgui.h index cf50f044..23b59fb8 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18509 +#define IMGUI_VERSION_NUM 18510 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -2209,7 +2209,7 @@ struct ImGuiListClipper // Call ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. - + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 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 From e790fc0e79eee4417396204724cdd46e25a78649 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Nov 2021 12:00:43 +0100 Subject: [PATCH 138/332] Nav: prevent child from clipping items when using _NavFlattened and parent has a pending nav request. (#787) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0e31d5ec..254c9329 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -50,6 +50,7 @@ Other Changes: - Nav: tabbing now cycles through clipped items and scroll accordingly. (#4449) - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: fixed resizing window from borders setting navigation to Menu layer. +- Nav: prevent child from clipping items when using _NavFlattened and parent has a pending request. - Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse. - Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when diff --git a/imgui.cpp b/imgui.cpp index a152ae9f..21d41a28 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6403,9 +6403,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // 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?? - if (!g.LogEnabled) + { + 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) window->HiddenFramesCanSkipItems = 1; + } // Hide along with parent or if parent is collapsed if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) From cad790d43b4e4e6e8fa47fc7b4ef19e6af689fef Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Nov 2021 12:44:43 +0100 Subject: [PATCH 139/332] Nav: fixed backwrap tabbing wrap on sibling child windows with _NavFlattened flag. (#4449) Amend 1a7526dc --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 21d41a28..854706b2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -924,7 +924,7 @@ static void NavEndFrame(); static bool NavScoreItem(ImGuiNavItemData* result); static void NavApplyItemToResult(ImGuiNavItemData* result); static void NavProcessItem(); -static void NavProcessItemForTabbingRequest(ImGuiWindow* window, ImGuiID id); +static void NavProcessItemForTabbingRequest(ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); @@ -9056,7 +9056,7 @@ static void ImGui::NavProcessItem() if (is_tabbing) { if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)) - NavProcessItemForTabbingRequest(window, id); + NavProcessItemForTabbingRequest(id); } else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) { @@ -9094,11 +9094,12 @@ static void ImGui::NavProcessItem() // - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request // - Case 4: tab backward: store all results, on ref id pick prev, stop storing // - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested -void ImGui::NavProcessItemForTabbingRequest(ImGuiWindow* window, ImGuiID id) +void ImGui::NavProcessItemForTabbingRequest(ImGuiID id) { ImGuiContext& g = *GImGui; - ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows) + ImGuiNavItemData* result = &g.NavMoveResultLocal; if (g.NavTabbingDir == +1) { // Tab Forward or SetKeyboardFocusHere() with >= 0 From 7cd3cfa58a509af90e221709484f6370caee8c1a Mon Sep 17 00:00:00 2001 From: Pascal Thomet Date: Sun, 21 Feb 2021 22:12:48 +0100 Subject: [PATCH 140/332] Demo: Add IMGUI_DEMO_MARKER (currently unused, but usage by altering GImGuiDemoMarkerCallback) (#3689) Various amends/renames. (Amend to fix a warning) --- imgui.h | 2 +- imgui_demo.cpp | 173 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 171 insertions(+), 4 deletions(-) diff --git a/imgui.h b/imgui.h index 23b59fb8..d90d9041 100644 --- a/imgui.h +++ b/imgui.h @@ -152,7 +152,7 @@ struct ImGuiContext; // Dear ImGui context (opaque structure, unl struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items -struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro +struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage diff --git a/imgui_demo.cpp b/imgui_demo.cpp index cf2e0d3b..91bfb2d8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -200,6 +200,14 @@ static void HelpMarker(const char* desc) } } +// Helper to wire demo markers located in code to a interactive browser +typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data); +extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; +extern void* GImGuiDemoMarkerCallbackUserData; +ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL; +void* GImGuiDemoMarkerCallbackUserData = NULL; +#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) + // Helper to display basic user controls. void ImGui::ShowUserGuide() { @@ -361,11 +369,13 @@ void ImGui::ShowDemoWindow(bool* p_open) { if (ImGui::BeginMenu("Menu")) { + IMGUI_DEMO_MARKER("Menu/File"); ShowExampleMenuFile(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Examples")) { + IMGUI_DEMO_MARKER("Menu/Examples"); ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); ImGui::MenuItem("Console", NULL, &show_app_console); ImGui::MenuItem("Log", NULL, &show_app_log); @@ -384,6 +394,7 @@ void ImGui::ShowDemoWindow(bool* p_open) //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! if (ImGui::BeginMenu("Tools")) { + IMGUI_DEMO_MARKER("Menu/Tools"); #ifndef IMGUI_DISABLE_METRICS_WINDOW ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool); @@ -398,6 +409,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION); ImGui::Spacing(); + IMGUI_DEMO_MARKER("Help"); if (ImGui::CollapsingHeader("Help")) { ImGui::Text("ABOUT THIS DEMO:"); @@ -420,6 +432,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::ShowUserGuide(); } + IMGUI_DEMO_MARKER("Configuration"); if (ImGui::CollapsingHeader("Configuration")) { ImGuiIO& io = ImGui::GetIO(); @@ -460,6 +473,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Backend Flags"); if (ImGui::TreeNode("Backend Flags")) { HelpMarker( @@ -476,6 +490,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Style"); if (ImGui::TreeNode("Style")) { HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); @@ -484,6 +499,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Capture, Logging"); if (ImGui::TreeNode("Capture/Logging")) { HelpMarker( @@ -503,6 +519,7 @@ void ImGui::ShowDemoWindow(bool* p_open) } } + IMGUI_DEMO_MARKER("Window options"); if (ImGui::CollapsingHeader("Window options")) { if (ImGui::BeginTable("split", 3)) @@ -536,6 +553,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static void ShowDemoWindowWidgets() { + IMGUI_DEMO_MARKER("Widgets"); if (!ImGui::CollapsingHeader("Widgets")) return; @@ -543,8 +561,10 @@ static void ShowDemoWindowWidgets() if (disable_all) ImGui::BeginDisabled(); + IMGUI_DEMO_MARKER("Widgets/Basic"); if (ImGui::TreeNode("Basic")) { + IMGUI_DEMO_MARKER("Widgets/Basic/Button"); static int clicked = 0; if (ImGui::Button("Button")) clicked++; @@ -554,15 +574,18 @@ static void ShowDemoWindowWidgets() ImGui::Text("Thanks for clicking me!"); } + IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox"); static bool check = true; ImGui::Checkbox("checkbox", &check); + IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton"); static int e = 0; ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); ImGui::RadioButton("radio c", &e, 2); // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. + IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)"); for (int i = 0; i < 7; i++) { if (i > 0) @@ -584,6 +607,7 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); // Arrow buttons with Repeater + IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); static int counter = 0; float spacing = ImGui::GetStyle().ItemInnerSpacing.x; ImGui::PushButtonRepeat(true); @@ -594,6 +618,7 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); ImGui::Text("%d", counter); + IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); ImGui::Text("Hover over me"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("I am a tooltip"); @@ -610,12 +635,12 @@ static void ShowDemoWindowWidgets() } ImGui::Separator(); - ImGui::LabelText("label", "Value"); { // Using the _simplified_ one-liner Combo() api here // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api. + IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); @@ -626,6 +651,7 @@ static void ShowDemoWindowWidgets() { // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. + IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); static char str0[128] = "Hello, world!"; ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); ImGui::SameLine(); HelpMarker( @@ -644,6 +670,7 @@ static void ShowDemoWindowWidgets() static char str1[128] = ""; ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); + IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); static int i0 = 123; ImGui::InputInt("input int", &i0); ImGui::SameLine(); HelpMarker( @@ -668,6 +695,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat"); static int i1 = 50, i2 = 42; ImGui::DragInt("drag int", &i1, 1); ImGui::SameLine(); HelpMarker( @@ -683,6 +711,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat"); static int i1 = 0; ImGui::SliderInt("slider int", &i1, -1, 3); ImGui::SameLine(); HelpMarker("CTRL+click to input value."); @@ -691,12 +720,14 @@ static void ShowDemoWindowWidgets() ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic); + IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle"); static float angle = 0.0f; ImGui::SliderAngle("slider angle", &angle); // Using the format string to display a name instead of an integer. // Here we completely omit '%d' from the format string, so it'll only display a name. // This technique can also be used with DragInt(). + IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)"); enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT }; static int elem = Element_Fire; const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; @@ -706,6 +737,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4"); static float col1[3] = { 1.0f, 0.0f, 0.2f }; static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; ImGui::ColorEdit3("color 1", col1); @@ -721,6 +753,7 @@ static void ShowDemoWindowWidgets() { // Using the _simplified_ one-liner ListBox() api here // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api. + IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; static int item_current = 1; ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); @@ -737,8 +770,10 @@ static void ShowDemoWindowWidgets() // if (once) // ImGui::Text("This will be displayed only once."); + IMGUI_DEMO_MARKER("Widgets/Trees"); if (ImGui::TreeNode("Trees")) { + IMGUI_DEMO_MARKER("Widgets/Trees/Basic trees"); if (ImGui::TreeNode("Basic trees")) { for (int i = 0; i < 5; i++) @@ -759,6 +794,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Trees/Advanced, with Selectable nodes"); if (ImGui::TreeNode("Advanced, with Selectable nodes")) { HelpMarker( @@ -842,6 +878,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Collapsing Headers"); if (ImGui::TreeNode("Collapsing Headers")) { static bool closable_group = true; @@ -865,6 +902,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Bullets"); if (ImGui::TreeNode("Bullets")) { ImGui::BulletText("Bullet point 1"); @@ -879,8 +917,10 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text"); if (ImGui::TreeNode("Text")) { + IMGUI_DEMO_MARKER("Widgets/Text/Colored Text"); if (ImGui::TreeNode("Colorful Text")) { // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. @@ -891,6 +931,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping"); if (ImGui::TreeNode("Word Wrapping")) { // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. @@ -924,6 +965,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text"); if (ImGui::TreeNode("UTF-8 Text")) { // UTF-8 test with Japanese characters @@ -950,6 +992,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Images"); if (ImGui::TreeNode("Images")) { ImGuiIO& io = ImGui::GetIO(); @@ -1003,6 +1046,8 @@ static void ShowDemoWindowWidgets() ImGui::EndTooltip(); } } + + IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); ImGui::TextWrapped("And now some textured buttons.."); static int pressed_count = 0; for (int i = 0; i < 8; i++) @@ -1024,6 +1069,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Combo"); if (ImGui::TreeNode("Combo")) { // Expose flags as checkbox for the demo @@ -1074,6 +1120,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/List Boxes"); if (ImGui::TreeNode("List boxes")) { // Using the generic BeginListBox() API, you have full control over how to display the combo contents. @@ -1116,6 +1163,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables"); if (ImGui::TreeNode("Selectables")) { // Selectable() has 2 overloads: @@ -1124,6 +1172,7 @@ static void ShowDemoWindowWidgets() // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) // The earlier is more flexible, as in real application your selection may be stored in many different ways // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc). + IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); if (ImGui::TreeNode("Basic")) { static bool selection[5] = { false, true, false, false, false }; @@ -1136,6 +1185,7 @@ static void ShowDemoWindowWidgets() selection[4] = !selection[4]; ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Single Selection"); if (ImGui::TreeNode("Selection State: Single Selection")) { static int selected = -1; @@ -1148,6 +1198,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple Selection"); if (ImGui::TreeNode("Selection State: Multiple Selection")) { HelpMarker("Hold CTRL and click to select multiple items."); @@ -1165,6 +1216,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more text into the same line"); if (ImGui::TreeNode("Rendering more text into the same line")) { // Using the Selectable() override that takes "bool* p_selected" parameter, @@ -1175,6 +1227,7 @@ static void ShowDemoWindowWidgets() ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/In columns"); if (ImGui::TreeNode("In columns")) { static bool selected[10] = {}; @@ -1209,6 +1262,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); if (ImGui::TreeNode("Grid")) { static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; @@ -1241,6 +1295,7 @@ static void ShowDemoWindowWidgets() ImGui::PopStyleVar(); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); if (ImGui::TreeNode("Alignment")) { HelpMarker( @@ -1268,8 +1323,10 @@ static void ShowDemoWindowWidgets() // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. + IMGUI_DEMO_MARKER("Widgets/Text Input"); if (ImGui::TreeNode("Text Input")) { + IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input"); if (ImGui::TreeNode("Multi-line Text Input")) { // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize @@ -1295,6 +1352,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input"); if (ImGui::TreeNode("Filtered Text Input")) { struct TextFilters @@ -1317,6 +1375,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Password input"); if (ImGui::TreeNode("Password Input")) { static char password[64] = "password123"; @@ -1383,6 +1442,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback"); if (ImGui::TreeNode("Resize Callback")) { // To wire InputText() with std::string or any other custom string type, @@ -1429,8 +1489,10 @@ static void ShowDemoWindowWidgets() } // Tabs + IMGUI_DEMO_MARKER("Widgets/Tabs"); if (ImGui::TreeNode("Tabs")) { + IMGUI_DEMO_MARKER("Widgets/Tabs/Basic"); if (ImGui::TreeNode("Basic")) { ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; @@ -1457,6 +1519,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button"); if (ImGui::TreeNode("Advanced & Close Button")) { // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). @@ -1499,6 +1562,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags"); if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags")) { static ImVector active_tabs; @@ -1570,12 +1634,14 @@ static void ShowDemoWindowWidgets() // Plot/Graph widgets are not very good. // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions) - if (ImGui::TreeNode("Plots Widgets")) + IMGUI_DEMO_MARKER("Widgets/Plotting"); + if (ImGui::TreeNode("Plotting")) { static bool animate = true; ImGui::Checkbox("Animate", &animate); // Plot as lines and plot as histogram + IMGUI_DEMO_MARKER("Widgets/Plotting/PlotLines, PlotHistogram"); static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); @@ -1629,6 +1695,7 @@ static void ShowDemoWindowWidgets() ImGui::Separator(); // Animate a simple progress bar + IMGUI_DEMO_MARKER("Widgets/Plotting/ProgressBar"); static float progress = 0.0f, progress_dir = 1.0f; if (animate) { @@ -1650,6 +1717,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Color"); if (ImGui::TreeNode("Color/Picker Widgets")) { static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); @@ -1666,18 +1734,22 @@ static void ShowDemoWindowWidgets() ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); ImGui::Text("Color widget:"); ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" "CTRL+click on individual component to input value.\n"); ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); ImGui::Text("Color widget HSV with Alpha:"); ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); ImGui::Text("Color widget with Float Display:"); ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); ImGui::Text("Color button with Picker:"); ImGui::SameLine(); HelpMarker( "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" @@ -1685,6 +1757,7 @@ static void ShowDemoWindowWidgets() "be used for the tooltip and picker popup."); ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); ImGui::Text("Color button with Custom Picker Popup:"); // Generate a default palette. The palette will persist and can be edited. @@ -1752,11 +1825,13 @@ static void ShowDemoWindowWidgets() ImGui::EndPopup(); } + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)"); ImGui::Text("Color button only:"); static bool no_border = false; ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); + IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); ImGui::Text("Color picker:"); static bool alpha = true; static bool alpha_bar = true; @@ -1824,6 +1899,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags"); if (ImGui::TreeNode("Drag/Slider Flags")) { // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same! @@ -1857,6 +1933,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Range Widgets"); if (ImGui::TreeNode("Range Widgets")) { static float begin = 10, end = 90; @@ -1867,6 +1944,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Data Types"); if (ImGui::TreeNode("Data Types")) { // DragScalar/InputScalar/SliderScalar functions allow various data types @@ -1916,6 +1994,7 @@ static void ShowDemoWindowWidgets() const float drag_speed = 0.2f; static bool drag_clamp = false; + IMGUI_DEMO_MARKER("Widgets/Data Types/Drags"); ImGui::Text("Drags:"); ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker( @@ -1934,6 +2013,7 @@ static void ShowDemoWindowWidgets() ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams"); ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic); + IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders"); ImGui::Text("Sliders"); ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); @@ -1966,6 +2046,7 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" IM_PRId64); ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" IM_PRIu64 " ms"); + IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); static bool inputs_step = true; ImGui::Text("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); @@ -1985,6 +2066,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets"); if (ImGui::TreeNode("Multi-component Widgets")) { static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; @@ -2016,6 +2098,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Vertical Sliders"); if (ImGui::TreeNode("Vertical Sliders")) { const float spacing = 4; @@ -2080,8 +2163,10 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and drop"); if (ImGui::TreeNode("Drag and Drop")) { + IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets"); if (ImGui::TreeNode("Drag and drop in standard widgets")) { // ColorEdit widgets automatically act as drag source and drag target. @@ -2096,6 +2181,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items"); if (ImGui::TreeNode("Drag and drop to copy/swap items")) { enum Mode @@ -2163,6 +2249,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); if (ImGui::TreeNode("Drag to reorder items (simple)")) { // Simple reordering @@ -2192,6 +2279,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)"); if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { // Select an item type @@ -2286,6 +2374,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)"); if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) { static bool embed_all_inside_a_child_window = false; @@ -2370,6 +2459,7 @@ static void ShowDemoWindowWidgets() if (disable_all) ImGui::EndDisabled(); + IMGUI_DEMO_MARKER("Widgets/Disable Block"); if (ImGui::TreeNode("Disable block")) { ImGui::Checkbox("Disable entire section above", &disable_all); @@ -2380,9 +2470,11 @@ static void ShowDemoWindowWidgets() static void ShowDemoWindowLayout() { + IMGUI_DEMO_MARKER("Layout"); if (!ImGui::CollapsingHeader("Layout & Scrolling")) return; + IMGUI_DEMO_MARKER("Layout/Child windows"); if (ImGui::TreeNode("Child windows")) { HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); @@ -2468,6 +2560,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Widgets Width"); if (ImGui::TreeNode("Widgets Width")) { static float f = 0.0f; @@ -2544,11 +2637,13 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout"); if (ImGui::TreeNode("Basic Horizontal Layout")) { ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); // Text + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); ImGui::Text("Two items: Hello"); ImGui::SameLine(); ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); @@ -2569,6 +2664,7 @@ static void ShowDemoWindowLayout() ImGui::Text("can fit within a text block."); // Aligned to arbitrary position. Easy/cheap column. + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)"); ImGui::Text("Aligned"); ImGui::SameLine(150); ImGui::Text("x=150"); ImGui::SameLine(300); ImGui::Text("x=300"); @@ -2577,6 +2673,7 @@ static void ShowDemoWindowLayout() ImGui::SameLine(300); ImGui::SmallButton("x=300"); // Checkbox + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)"); static bool c1 = false, c2 = false, c3 = false, c4 = false; ImGui::Checkbox("My", &c1); ImGui::SameLine(); ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); @@ -2608,6 +2705,7 @@ static void ShowDemoWindowLayout() ImGui::PopItemWidth(); // Dummy + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy"); ImVec2 button_sz(40, 40); ImGui::Button("A", button_sz); ImGui::SameLine(); ImGui::Dummy(button_sz); ImGui::SameLine(); @@ -2615,7 +2713,8 @@ static void ShowDemoWindowLayout() // Manually wrapping // (we should eventually provide this as an automatic layout feature, but for now you can do it manually) - ImGui::Text("Manually wrapping:"); + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping"); + ImGui::Text("Manual wrapping:"); ImGuiStyle& style = ImGui::GetStyle(); int buttons_count = 20; float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; @@ -2633,6 +2732,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Groups"); if (ImGui::TreeNode("Groups")) { HelpMarker( @@ -2680,6 +2780,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment"); if (ImGui::TreeNode("Text Baseline Alignment")) { { @@ -2798,9 +2899,11 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Scrolling"); if (ImGui::TreeNode("Scrolling")) { // Vertical scroll functions + IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical"); HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position."); static int track_item = 50; @@ -2873,6 +2976,7 @@ static void ShowDemoWindowLayout() ImGui::PopID(); // Horizontal scroll functions + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal"); ImGui::Spacing(); HelpMarker( "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n" @@ -2918,6 +3022,7 @@ static void ShowDemoWindowLayout() ImGui::PopID(); // Miscellaneous Horizontal Scrolling Demo + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)"); HelpMarker( "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n" "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin()."); @@ -2992,6 +3097,7 @@ static void ShowDemoWindowLayout() if (explicit_content_size) ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f)); ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0); + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window"); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); HelpMarker("Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); @@ -3078,6 +3184,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Clipping"); if (ImGui::TreeNode("Clipping")) { static ImVec2 size(100.0f, 100.0f); @@ -3146,6 +3253,7 @@ static void ShowDemoWindowLayout() static void ShowDemoWindowPopups() { + IMGUI_DEMO_MARKER("Popups"); if (!ImGui::CollapsingHeader("Popups & Modal windows")) return; @@ -3167,6 +3275,7 @@ static void ShowDemoWindowPopups() // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. + IMGUI_DEMO_MARKER("Popups/Popups"); if (ImGui::TreeNode("Popups")) { ImGui::TextWrapped( @@ -3246,6 +3355,7 @@ static void ShowDemoWindowPopups() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Context menus"); if (ImGui::TreeNode("Context menus")) { HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier."); @@ -3330,6 +3440,7 @@ static void ShowDemoWindowPopups() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Modals"); if (ImGui::TreeNode("Modals")) { ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside."); @@ -3405,6 +3516,7 @@ static void ShowDemoWindowPopups() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Menus inside a regular window"); if (ImGui::TreeNode("Menus inside a regular window")) { ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); @@ -3584,6 +3696,7 @@ static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) static void ShowDemoWindowTables() { //ImGui::SetNextItemOpen(true, ImGuiCond_Once); + IMGUI_DEMO_MARKER("Tables"); if (!ImGui::CollapsingHeader("Tables & Columns")) return; @@ -3623,6 +3736,7 @@ static void ShowDemoWindowTables() // Demos if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Basic"); if (ImGui::TreeNode("Basic")) { // Here we will showcase three different ways to output a table. @@ -3684,6 +3798,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Borders, background"); if (ImGui::TreeNode("Borders, background")) { // Expose a few Borders related flags interactively @@ -3754,6 +3869,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, stretch"); if (ImGui::TreeNode("Resizable, stretch")) { // By default, if we don't enable ScrollX the sizing policy for each columns is "Stretch" @@ -3783,6 +3899,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, fixed"); if (ImGui::TreeNode("Resizable, fixed")) { // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set) @@ -3816,6 +3933,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, mixed"); if (ImGui::TreeNode("Resizable, mixed")) { HelpMarker( @@ -3865,6 +3983,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers"); if (ImGui::TreeNode("Reorderable, hideable, with headers")) { HelpMarker( @@ -3922,6 +4041,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Padding"); if (ImGui::TreeNode("Padding")) { // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding. @@ -4030,6 +4150,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Explicit widths"); if (ImGui::TreeNode("Sizing policies")) { static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; @@ -4133,6 +4254,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping"); if (ImGui::TreeNode("Vertical scrolling, with clipping")) { HelpMarker("Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\nWe also demonstrate using ImGuiListClipper to virtualize the submission of many items."); @@ -4175,6 +4297,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Horizontal scrolling"); if (ImGui::TreeNode("Horizontal scrolling")) { HelpMarker( @@ -4263,6 +4386,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Columns flags"); if (ImGui::TreeNode("Columns flags")) { // Create a first table just to show all the options/flags we want to make visible in our example! @@ -4327,6 +4451,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Columns widths"); if (ImGui::TreeNode("Columns widths")) { HelpMarker("Using TableSetupColumn() to setup default width."); @@ -4392,6 +4517,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Nested tables"); if (ImGui::TreeNode("Nested tables")) { HelpMarker("This demonstrate embedding a table into another table cell."); @@ -4436,6 +4562,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Row height"); if (ImGui::TreeNode("Row height")) { HelpMarker("You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\nWe cannot honor a _maximum_ row height as that would requires a unique clipping rectangle per row."); @@ -4455,6 +4582,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Outer size"); if (ImGui::TreeNode("Outer size")) { // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY @@ -4521,6 +4649,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Background color"); if (ImGui::TreeNode("Background color")) { static ImGuiTableFlags flags = ImGuiTableFlags_RowBg; @@ -4578,6 +4707,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Tree view"); if (ImGui::TreeNode("Tree view")) { static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; @@ -4649,6 +4779,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Item width"); if (ImGui::TreeNode("Item width")) { HelpMarker( @@ -4694,6 +4825,7 @@ static void ShowDemoWindowTables() // Demonstrate using TableHeader() calls instead of TableHeadersRow() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Custom headers"); if (ImGui::TreeNode("Custom headers")) { const int COLUMNS_COUNT = 3; @@ -4741,6 +4873,7 @@ static void ShowDemoWindowTables() // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Context menus"); if (ImGui::TreeNode("Context menus")) { HelpMarker("By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); @@ -4847,6 +4980,7 @@ static void ShowDemoWindowTables() // Demonstrate creating multiple tables with the same ID if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Synced instances"); if (ImGui::TreeNode("Synced instances")) { HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); @@ -4882,6 +5016,7 @@ static void ShowDemoWindowTables() }; if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Sorting"); if (ImGui::TreeNode("Sorting")) { // Create item list @@ -4969,6 +5104,7 @@ static void ShowDemoWindowTables() //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG] if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Advanced"); if (ImGui::TreeNode("Advanced")) { static ImGuiTableFlags flags = @@ -5285,6 +5421,7 @@ static void ShowDemoWindowTables() // [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!] static void ShowDemoWindowColumns() { + IMGUI_DEMO_MARKER("Columns (legacy API)"); bool open = ImGui::TreeNode("Legacy Columns API"); ImGui::SameLine(); HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!"); @@ -5292,6 +5429,7 @@ static void ShowDemoWindowColumns() return; // Basic columns + IMGUI_DEMO_MARKER("Columns (legacy API)/Basic"); if (ImGui::TreeNode("Basic")) { ImGui::Text("Without border:"); @@ -5336,6 +5474,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Borders"); if (ImGui::TreeNode("Borders")) { // NB: Future columns API should allow automatic horizontal borders. @@ -5371,6 +5510,7 @@ static void ShowDemoWindowColumns() } // Create multiple items in a same cell before switching to next column + IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items"); if (ImGui::TreeNode("Mixed items")) { ImGui::Columns(3, "mixed"); @@ -5402,6 +5542,7 @@ static void ShowDemoWindowColumns() } // Word wrapping + IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping"); if (ImGui::TreeNode("Word-wrapping")) { ImGui::Columns(2, "word-wrapping"); @@ -5416,6 +5557,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling"); if (ImGui::TreeNode("Horizontal Scrolling")) { ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); @@ -5441,6 +5583,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Tree"); if (ImGui::TreeNode("Tree")) { ImGui::Columns(2, "tree", true); @@ -5482,6 +5625,7 @@ static void ShowDemoWindowColumns() static void ShowDemoWindowMisc() { + IMGUI_DEMO_MARKER("Filtering"); if (ImGui::CollapsingHeader("Filtering")) { // Helper class to easy setup a text filter. @@ -5499,6 +5643,7 @@ static void ShowDemoWindowMisc() ImGui::BulletText("%s", lines[i]); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus"); if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) { ImGuiIO& io = ImGui::GetIO(); @@ -5512,6 +5657,7 @@ static void ShowDemoWindowMisc() ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); // Display Mouse state + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse State"); if (ImGui::TreeNode("Mouse State")) { if (ImGui::IsMousePosValid()) @@ -5535,6 +5681,7 @@ static void ShowDemoWindowMisc() } // Display Keyboard/Mouse state + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State"); if (ImGui::TreeNode("Keyboard & Navigation State")) { ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyDown(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); } @@ -5556,6 +5703,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Tabbing"); if (ImGui::TreeNode("Tabbing")) { ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); @@ -5571,6 +5719,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Focus from code"); if (ImGui::TreeNode("Focus from code")) { bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); @@ -5612,6 +5761,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Dragging"); if (ImGui::TreeNode("Dragging")) { ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); @@ -5640,6 +5790,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse cursors"); if (ImGui::TreeNode("Mouse cursors")) { const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed" }; @@ -5677,6 +5828,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); @@ -5864,6 +6016,7 @@ bool ImGui::ShowStyleSelector(const char* label) void ImGui::ShowStyleEditor(ImGuiStyle* ref) { + IMGUI_DEMO_MARKER("Tools/Style Editor"); // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to // (without a reference style pointer, we will use one compared locally as a reference) ImGuiStyle& style = ImGui::GetStyle(); @@ -6147,6 +6300,7 @@ static void ShowExampleAppMainMenuBar() // (future version will add explicit flags to BeginMenu() to request processing shortcuts) static void ShowExampleMenuFile() { + IMGUI_DEMO_MARKER("Examples/Menu"); ImGui::MenuItem("(demo menu)", NULL, false, false); if (ImGui::MenuItem("New")) {} if (ImGui::MenuItem("Open", "Ctrl+O")) {} @@ -6172,6 +6326,7 @@ static void ShowExampleMenuFile() if (ImGui::MenuItem("Save As..")) {} ImGui::Separator(); + IMGUI_DEMO_MARKER("Examples/Menu/Options"); if (ImGui::BeginMenu("Options")) { static bool enabled = true; @@ -6188,6 +6343,7 @@ static void ShowExampleMenuFile() ImGui::EndMenu(); } + IMGUI_DEMO_MARKER("Examples/Menu/Colors"); if (ImGui::BeginMenu("Colors")) { float sz = ImGui::GetTextLineHeight(); @@ -6208,6 +6364,7 @@ static void ShowExampleMenuFile() // In a real code-base using it would make senses to use this feature from very different code locations. if (ImGui::BeginMenu("Options")) // <-- Append! { + IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu"); static bool b = true; ImGui::Checkbox("SomeOption", &b); ImGui::EndMenu(); @@ -6240,6 +6397,7 @@ struct ExampleAppConsole ExampleAppConsole() { + IMGUI_DEMO_MARKER("Examples/Console"); ClearLog(); memset(InputBuf, 0, sizeof(InputBuf)); HistoryPos = -1; @@ -6717,6 +6875,7 @@ static void ShowExampleAppLog(bool* p_open) // Most of the contents of the window will be added by the log.Draw() call. ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); ImGui::Begin("Example: Log", p_open); + IMGUI_DEMO_MARKER("Examples/Log"); if (ImGui::SmallButton("[Debug] Add 5 entries")) { static int counter = 0; @@ -6747,6 +6906,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) { + IMGUI_DEMO_MARKER("Examples/Simple layout"); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) @@ -6863,6 +7023,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Property Editor"); HelpMarker( "This example shows how you may implement a property editor using two columns.\n" @@ -6898,6 +7059,7 @@ static void ShowExampleAppLongText(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Long text display"); static int test_type = 0; static ImGuiTextBuffer log; @@ -6959,6 +7121,7 @@ static void ShowExampleAppAutoResize(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Auto-resizing window"); static int lines = 10; ImGui::TextUnformatted( @@ -7010,6 +7173,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) { + IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } @@ -7052,6 +7216,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) { + IMGUI_DEMO_MARKER("Examples/Simple Overlay"); ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); ImGui::Separator(); if (ImGui::IsMousePosValid()) @@ -7126,6 +7291,7 @@ static void ShowExampleAppWindowTitles(bool*) // Using "##" to display same title but have unique identifier. ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##1"); + IMGUI_DEMO_MARKER("Examples/Manipulating window titles"); ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); ImGui::End(); @@ -7155,6 +7321,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Custom Rendering"); // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your From f8a806ad9f82d18b3b49334cbfc8e3cdb1f82276 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Nov 2021 17:53:58 +0100 Subject: [PATCH 141/332] Tooltips, Internal: (Breaking) swapped parameter order to accomodate for future tooltip api rework. --- imgui.cpp | 8 ++++---- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 854706b2..aafec54c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8219,10 +8219,10 @@ void ImGui::SetScrollHereY(float center_y_ratio) void ImGui::BeginTooltip() { - BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None); + BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } -void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags) +void ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; @@ -8251,7 +8251,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags toolt 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; - Begin(window_name, NULL, flags | extra_flags); + Begin(window_name, NULL, flags | extra_window_flags); } void ImGui::EndTooltip() @@ -8262,7 +8262,7 @@ void ImGui::EndTooltip() void ImGui::SetTooltipV(const char* fmt, va_list args) { - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); TextV(fmt, args); EndTooltip(); } diff --git a/imgui_internal.h b/imgui_internal.h index 448c2b13..9e4fc159 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2527,7 +2527,7 @@ namespace ImGui IMGUI_API void ClosePopupsExceptModals(); IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); - IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags); + IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 96c0e2cb..00f481b5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5580,7 +5580,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { From 2c29e391dd2e640f4c5ddca18e2e7df7ccf57b23 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Nov 2021 19:17:01 +0100 Subject: [PATCH 142/332] Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) Reduced amount of self critical commentary since it'll appear like a hack for users but it isn't more a hack than many other things. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 254c9329..944a0975 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,6 +67,8 @@ Other Changes: by the clipper to display. (#3841) - Clipper: fixed content height declaration slightly mismatching the value of when not using a clipper. (an additional ItemSpacing.y was declared, affecting scrollbar range). +- Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose + tooltip when scrolling. (#143) - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) diff --git a/imgui.cpp b/imgui.cpp index aafec54c..ef1d2554 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3121,7 +3121,7 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); - const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; + ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); ImGui::KeepAliveID(id); return id; @@ -10276,16 +10276,16 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) return false; // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: - // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. + // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag. if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) { IM_ASSERT(0); return false; } - // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() + // Magic fallback to handle items with no assigned ID, e.g. Text(), Image() // We build a throwaway ID based on current ID stack + relative AABB of items in window. - // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. + // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled. // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. // Rely on keeping other window->LastItemXXX fields intact. source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); From d80a9123b741dc7d99cee76cbbb047cfb7d91292 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Nov 2021 20:17:11 +0100 Subject: [PATCH 143/332] Internals: move chunks of IsItemHovered() so upcoming commit can be less noisy. This commit should be a no-op (check by comparing without white-space changes) --- imgui.cpp | 62 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ef1d2554..d92f3f4d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3278,42 +3278,46 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; - return IsItemFocused(); + if (!IsItemFocused()) + return false; } - - // Test for bounding box overlap, as updated as ItemAdd() - ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; - if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) - return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function - - // Test if we are hovering the right window (our window could be behind another window) - // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) - // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable - // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was - // the test that has been running for a long while. - if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) - if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + else + { + // Test for bounding box overlap, as updated as ItemAdd() + ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; + if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function + + // Test if we are hovering the right window (our window could be behind another window) + // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) + // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable + // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was + // the test that has been running for a long while. + if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + return false; + + // Test if another item is active (e.g. being dragged) + if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) + if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + return false; - // Test if another item is active (e.g. being dragged) - if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + // Test if interactions on this window are blocked by an active popup or modal. + // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. + if (!IsWindowContentHoverable(window, flags)) return false; - // Test if interactions on this window are blocked by an active popup or modal. - // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. - if (!IsWindowContentHoverable(window, flags)) - return false; + // Test if the item is disabled + if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) + return false; - // Test if the item is disabled - if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) - return false; + // Special handling for calling after Begin() which represent the title bar or tab. + // When the window is 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) + return false; + } - // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is 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) - return false; return true; } From 3fde445b91a357db4d3e78a1526170a479b130f4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Nov 2021 16:05:54 +0100 Subject: [PATCH 144/332] Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when io.MouseDrawCursor is enabled). (#4713) --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 58 ++++++++++++++++++++++++---------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 944a0975..089d8e84 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -72,6 +72,8 @@ Other Changes: - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) +- Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the + io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] - CI: Add MinGW DLL build to test suite. [@rokups] diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 86bc0f2b..809c8490 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1919,37 +1919,38 @@ ImFontConfig::ImFontConfig() // A work of art lies ahead! (. = white layer, X = black layer, others are blank) // The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. -const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 108; // Actual texture will be 2 times that + 1 spacing. +// (This is used when io.MouseDrawCursor = true) +const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing. const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = { - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX " - "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X " - "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X " - "X - X.X - X.....X - X.....X -X...X - X...X- X..X " - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X " - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX " - "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX " - "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X" - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X" - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X" - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X" - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X" - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X" - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X" - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X " - "X.X X..X - -X.......X- X.......X - XX XX - - X..........X " - "XX X..X - - X.....X - X.....X - X.X X.X - - X........X " - " X..X - X...X - X...X - X..X X..X - - X........X " - " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX " - "------------ - X - X -X.....................X- ------------------" - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX " + "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X" + "X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X " + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X " + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X " + "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X " + "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X" + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X" + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------" + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - " + "X.X X..X - -X.......X- X.......X - XX XX - - X..........X - " + "XX X..X - - X.....X - X.....X - X.X X.X - - X........X - " + " X..X - - X...X - X...X - X..X X..X - - X........X - " + " XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - " + "------------- - X - X -X.....................X- ------------------- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " }; static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = @@ -1963,6 +1964,7 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3 { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand + { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed }; ImFontAtlas::ImFontAtlas() From 2080d12bd930124ddaece6f61befe51f47436837 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Nov 2021 01:02:25 +0100 Subject: [PATCH 145/332] Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0. (#3152, #2871) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 +- imgui.h | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fb0059a4..3ab3774c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -154,6 +154,9 @@ Other Changes: Docking+Viewports Branch: +- Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order + to ensure a window is not parented. Previously this would use the global default (which might be 0, + but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) - Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) diff --git a/imgui.cpp b/imgui.cpp index ace13208..730a6a87 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12457,7 +12457,7 @@ void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_win // Update parent viewport ID // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport()) - if (window->WindowClass.ParentViewportId) + if (window->WindowClass.ParentViewportId != (ImGuiID)-1) window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive)) window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; diff --git a/imgui.h b/imgui.h index 9ad5a2bc..f922fc0e 100644 --- a/imgui.h +++ b/imgui.h @@ -2100,7 +2100,7 @@ struct ImGuiSizeCallbackData struct ImGuiWindowClass { ImGuiID ClassId; // User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others. - ImGuiID ParentViewportId; // Hint for the platform backend. If non-zero, the platform backend can create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not. + ImGuiID ParentViewportId; // Hint for the platform backend. -1: use default. 0: request platform backend to not parent the platform. != 0: request platform backend to create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiTabItemFlags TabItemFlagsOverrideSet; // [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags_Leading or ImGuiTabItemFlags_Trailing. @@ -2108,7 +2108,7 @@ struct ImGuiWindowClass bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override? - ImGuiWindowClass() { memset(this, 0, sizeof(*this)); DockingAllowUnclassed = true; } + ImGuiWindowClass() { memset(this, 0, sizeof(*this)); ParentViewportId = (ImGuiID)-1; DockingAllowUnclassed = true; } }; // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() From a3667f462a01b5dfc05e8032a2006762c426d4fd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Nov 2021 16:13:04 +0100 Subject: [PATCH 146/332] Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) Normally we would aim to ensure that g.Windows[] gets maintained to reflect display layer but it is presently non trivial. --- imgui.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d92f3f4d..a7bd7280 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4365,11 +4365,15 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) } } +static inline int GetWindowDisplayLayer(ImGuiWindow* window) +{ + return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; +} + // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) -static void AddRootWindowToDrawData(ImGuiWindow* window) +static inline void AddRootWindowToDrawData(ImGuiWindow* window) { - int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; - AddWindowToDrawData(window, layer); + AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -6763,6 +6767,12 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; + + // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array + const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); + if (display_layer_delta != 0) + return display_layer_delta > 0; + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* candidate_window = g.Windows[i]; From b50b22d7873d7709df978011b514e5ee9ccc30fd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Nov 2021 16:13:04 +0100 Subject: [PATCH 147/332] Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) Normally we would aim to ensure that g.Windows[] gets maintained to reflect display layer but it is presently non trivial. --- docs/CHANGELOG.txt | 13 +++---------- imgui.cpp | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3ab3774c..77a41481 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,16 +99,6 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. ------------------------------------------------------------------------ - VERSION 1.86 WIP (In Progress) ------------------------------------------------------------------------ - -Docking+Viewports Branch: - -- Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) -- Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) - - ----------------------------------------------------------------------- VERSION 1.86 WIP (In Progress) ----------------------------------------------------------------------- @@ -154,9 +144,12 @@ Other Changes: Docking+Viewports Branch: +- Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) +- Viewports: Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) +- Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) - Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) diff --git a/imgui.cpp b/imgui.cpp index 730a6a87..87c2bbfe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4534,11 +4534,15 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) } } +static inline int GetWindowDisplayLayer(ImGuiWindow* window) +{ + return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; +} + // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) -static void AddRootWindowToDrawData(ImGuiWindow* window) +static inline void AddRootWindowToDrawData(ImGuiWindow* window) { - int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; - AddWindowToDrawData(window, layer); + AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -7303,6 +7307,12 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; + + // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array + const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); + if (display_layer_delta != 0) + return display_layer_delta > 0; + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* candidate_window = g.Windows[i]; From 764f9e606d106591072a8e5a79d4e801c4c699fb Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Nov 2021 12:45:33 +0100 Subject: [PATCH 148/332] Fixed crash on right-click without modal, introduced by previous commit a3667f46, (#4729) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index a7bd7280..8b6613e0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3712,7 +3712,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); + bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } From 7f38773b738e8d37b1a3c1d627205821300ef765 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Nov 2021 12:45:33 +0100 Subject: [PATCH 149/332] Fixed crash on right-click without modal, introduced by previous commit a3667f46, (#4729) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 87c2bbfe..3663a46f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3831,7 +3831,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); + bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } From 4d9a3b19d5e348fafa5f680ffe1e49167b149aa0 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 18 Nov 2021 16:54:16 +0200 Subject: [PATCH 150/332] Internals: Implement a continuable IM_DEBUG_BREAK on GCC for common archs. (#2673) --- imgui_internal.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 9e4fc159..7b430dc4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -253,12 +253,19 @@ namespace ImStb #endif // Debug Tools -// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item. +// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item. +// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference. #ifndef IM_DEBUG_BREAK -#if defined(__clang__) -#define IM_DEBUG_BREAK() __builtin_debugtrap() -#elif defined (_MSC_VER) +#if defined (_MSC_VER) #define IM_DEBUG_BREAK() __debugbreak() +#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") +#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"); #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 From ea83d040e60d179248ac6213cf53b3343e95b371 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Nov 2021 16:46:40 +0100 Subject: [PATCH 151/332] Viewports: fix missing default per-window value for ParentViewportId due to zero-cleared in-window instance (#4756) Broken by 2080d12b --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 3663a46f..0621e732 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3092,6 +3092,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst DrawList = &DrawListInst; DrawList->_Data = &context->DrawListSharedData; DrawList->_OwnerName = Name; + IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass(); } ImGuiWindow::~ImGuiWindow() From 719d9313041b85227a3e6deb289a313819aaeab3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Nov 2021 17:49:21 +0100 Subject: [PATCH 152/332] Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace. (#4757) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 77a41481..54d79fc3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,6 +145,8 @@ Other Changes: Docking+Viewports Branch: - Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) +- Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace + when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 0621e732..8341889a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16157,7 +16157,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) node->State = ImGuiDockNodeState_HostWindowVisible; // Undock if we are submitted earlier than the host window - if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) + if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) { DockContextProcessUndockWindow(ctx, window); return; From 5ac25e7c7a8f244f84b935ea5a5f69b67b72b6f4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Nov 2021 15:10:47 +0100 Subject: [PATCH 153/332] InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 089d8e84..bde6e9e3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,6 +58,7 @@ Other Changes: - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) +- InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) [@actondev] - Clipper: currently focused item is automatically included in clipper range. Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is still submitted. (#3841, #1725) [@GamingMinds-DanielC, @ocornut] diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 00f481b5..bb90b118 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4484,8 +4484,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.Flags = flags; callback_data.UserData = callback_user_data; + char* callback_buf = is_readonly ? buf : state->TextA.Data; callback_data.EventKey = event_key; - callback_data.Buf = state->TextA.Data; + callback_data.Buf = callback_buf; callback_data.BufTextLen = state->CurLenA; callback_data.BufSize = state->BufCapacityA; callback_data.BufDirty = false; @@ -4500,7 +4501,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); const bool buf_dirty = callback_data.BufDirty; From 65f4be1a10932545a29faa54b8619eef7c0fe678 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Nov 2021 21:41:34 +0100 Subject: [PATCH 154/332] InputText: Internals: moved "apply_new_text" application code to reduce noise in a future commit (will be for #4714) + removed unused fields. The move would ideally be no-op. technically we now clear state->Flags before calling ResizeCallback but those are unrelated. The 2 unused fields were incorrectly added by 24ff25981. --- imgui.h | 4 +-- imgui_internal.h | 2 -- imgui_widgets.cpp | 64 ++++++++++++++++++++++------------------------- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/imgui.h b/imgui.h index d90d9041..49218b0f 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18510 +#define IMGUI_VERSION_NUM 18511 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -974,7 +974,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) + 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_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() diff --git a/imgui_internal.h b/imgui_internal.h index 7b430dc4..4ff33794 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1027,8 +1027,6 @@ 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 - ImGuiInputTextCallback UserCallback; // " - void* UserCallbackData; // " ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index bb90b118..a6f31958 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4165,8 +4165,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->Edited = false; state->BufCapacityA = buf_size; state->Flags = flags; - state->UserCallback = callback; - state->UserCallbackData = callback_user_data; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -4404,11 +4402,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Process callbacks and apply result back to user's buffer. + const char* apply_new_text = NULL; + int apply_new_text_length = 0; if (g.ActiveId == id) { IM_ASSERT(state != NULL); - const char* apply_new_text = NULL; - int apply_new_text_length = 0; if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. @@ -4528,39 +4526,37 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } } - // Copy result to user buffer - if (apply_new_text) - { - // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size - // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used - // without any storage on user's side. - IM_ASSERT(apply_new_text_length >= 0); - if (is_resizable) - { - ImGuiInputTextCallbackData callback_data; - callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; - callback_data.Flags = flags; - callback_data.Buf = buf; - callback_data.BufTextLen = apply_new_text_length; - callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); - callback_data.UserData = callback_user_data; - callback(&callback_data); - buf = callback_data.Buf; - buf_size = callback_data.BufSize; - apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); - IM_ASSERT(apply_new_text_length <= buf_size); - } - //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); + // Clear temporary user storage + state->Flags = ImGuiInputTextFlags_None; + } - // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. - ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); - value_changed = true; + // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) + if (apply_new_text != NULL) + { + // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size + // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used + // without any storage on user's side. + IM_ASSERT(apply_new_text_length >= 0); + if (is_resizable) + { + ImGuiInputTextCallbackData callback_data; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = apply_new_text_length; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); } + //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); - // Clear temporary user storage - state->Flags = ImGuiInputTextFlags_None; - state->UserCallback = NULL; - state->UserCallbackData = NULL; + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); + value_changed = true; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) From cb5c73f64a225bb3fdaea1a4db3e20096b8928be Mon Sep 17 00:00:00 2001 From: averne <45773016+averne@users.noreply.github.com> Date: Mon, 29 Nov 2021 00:05:57 +0100 Subject: [PATCH 155/332] Fix: IM_DEBUG_BREAK macro on ARM GCC (#4767) --- imgui_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 4ff33794..581ddd12 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -264,7 +264,7 @@ namespace ImStb #define IM_DEBUG_BREAK() __asm__ volatile("int $0x03") #elif defined(__GNUC__) && defined(__thumb__) #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") -#elif defined(__GNUC__) defined(__arm__) && !defined(__thumb__) +#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) #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! From 5ccb66794b7533cd473a7179d4114de992ebc271 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Nov 2021 12:18:25 +0100 Subject: [PATCH 156/332] Backends: WebGPU: Passing explicit buffer sizes as validation layers appears to not do what the in-flux specs says. (#4766 --- backends/imgui_impl_sdlrenderer.cpp | 9 ++++++--- backends/imgui_impl_sdlrenderer.h | 9 ++++++--- backends/imgui_impl_wgpu.cpp | 5 +++-- docs/BACKENDS.md | 5 ++++- docs/CHANGELOG.txt | 9 ++++++--- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index bfc2b676..6c1786e7 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -1,14 +1,17 @@ // dear imgui: Renderer Backend for SDL_Renderer // (Requires: SDL 2.0.17+) -// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer -// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// Important to understand: SDL_Renderer is an _optional_ component of SDL. // For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. +// If your application will want to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer offers a limited graphic API to the end-user and it might +// be difficult to step out of those boundaries. +// However, we understand it is a convenient choice to get an app started easily. // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! // Missing features: -// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices (SDL_RenderGeometryRaw() is missing a vertex offset). // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. diff --git a/backends/imgui_impl_sdlrenderer.h b/backends/imgui_impl_sdlrenderer.h index 8c67fa3a..e8f4d10b 100644 --- a/backends/imgui_impl_sdlrenderer.h +++ b/backends/imgui_impl_sdlrenderer.h @@ -1,14 +1,17 @@ // dear imgui: Renderer Backend for SDL_Renderer // (Requires: SDL 2.0.17+) -// Important to understand: SDL_Renderer is an _optional_ component of SDL. We do not recommend you use SDL_Renderer -// because it provide a rather limited API to the end-user. We provide this backend for the sake of completeness. +// Important to understand: SDL_Renderer is an _optional_ component of SDL. // For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. +// If your application will want to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer offers a limited graphic API to the end-user and it might +// be difficult to step out of those boundaries. +// However, we understand it is a convenient choice to get an app started easily. // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! // Missing features: -// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices (SDL_RenderGeometryRaw() is missing a vertex offset). #pragma once #include "imgui.h" // IMGUI_IMPL_API diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 2f621ed8..66430515 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -13,6 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-11-29: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer()/wgpuRenderPassEncoderSetIndexBuffer(). // 2021-08-24: Fix for latest specs. // 2021-05-24: Add support for draw_data->FramebufferScale. // 2021-05-19: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) @@ -313,8 +314,8 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->FramebufferScale.x * draw_data->DisplaySize.x, draw_data->FramebufferScale.y * draw_data->DisplaySize.y, 0, 1); // Bind shader and vertex buffers - wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, 0); - wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, 0); + wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize); + wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize); wgpuRenderPassEncoderSetPipeline(ctx, g_pipelineState); wgpuRenderPassEncoderSetBindGroup(ctx, 0, g_resources.CommonBindGroup, 0, NULL); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index c878132b..f061d70c 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -128,7 +128,10 @@ Once it works, if you really need it you can replace parts of backends with your and you have high-level systems everywhere.
Suggestion: try using a non-portable backend first (e.g. win32 + underlying graphics API) to get your desktop builds working first. This will get you running faster and get your acquainted with -how Dear ImGui works and is setup. You can then rewrite a custom backend using your own engine API. +how Dear ImGui works and is setup. You can then rewrite a custom backend using your own engine API... + +Generally: +It is unlikely you will add value to your project by creating your own backend. Also: The [multi-viewports feature](https://github.com/ocornut/imgui/issues/1542) of the 'docking' branch allows diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bde6e9e3..8bc8d57f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -70,13 +70,16 @@ Other Changes: (an additional ItemSpacing.y was declared, affecting scrollbar range). - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) -- Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce - likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling - vkCmdSetScissor() explicitly every frame. (#4644) - Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] - CI: Add MinGW DLL build to test suite. [@rokups] +- Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce + likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling + vkCmdSetScissor() explicitly every frame. (#4644) +- Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and + wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the + in-flux specs says. (#4766) [@meshula] ----------------------------------------------------------------------- From 66f0fb986ce9f187d5d17e0488e7836fe14ceafc Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 29 Nov 2021 16:25:45 +0100 Subject: [PATCH 157/332] Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) --- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- imgui_widgets.cpp | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8bc8d57f..5f18a91a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,7 @@ Other Changes: Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when already at edge) reactivates the navigation highlight. - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) +- Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) [@actondev] diff --git a/imgui.h b/imgui.h index 49218b0f..6cbe6944 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18511 +#define IMGUI_VERSION_NUM 18512 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a6f31958..431a9183 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3966,7 +3966,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ 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, + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) BeginGroup(); const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); @@ -4770,9 +4770,22 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline) { + // 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; EndChild(); + 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... + ImGuiLastItemData item_data = g.LastItemData; EndGroup(); + if (g.LastItemData.ID == 0) + { + g.LastItemData.ID = id; + g.LastItemData.InFlags = item_data.InFlags; + g.LastItemData.StatusFlags = item_data.StatusFlags; + } } // Log as text From 9d704d99d3d76da51be36e824df1fc2059403fc2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Nov 2021 12:42:23 +0100 Subject: [PATCH 158/332] Internals: wrapped ImQsort() in an inline function + added a define guard. --- imgui.cpp | 6 ++---- imgui_internal.h | 8 +++++--- imgui_widgets.cpp | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8b6613e0..ed3cebc2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1954,8 +1954,7 @@ void ImGuiStorage::BuildSortByKey() return 0; } }; - if (Data.Size > 1) - ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); + ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); } int ImGuiStorage::GetInt(ImGuiID key, int default_val) const @@ -4304,8 +4303,7 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im if (window->Active) { int count = window->DC.ChildWindows.Size; - if (count > 1) - ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); + ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); for (int i = 0; i < count; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; diff --git a/imgui_internal.h b/imgui_internal.h index 581ddd12..52b20654 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -302,7 +302,9 @@ static inline ImGuiID ImHash(const void* data, int size, ImU32 seed = 0) { ret #endif // Helpers: Sorting -#define ImQsort qsort +#ifndef ImQsort +static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } +#endif // Helpers: Color Blending IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); @@ -409,8 +411,8 @@ static inline double ImLog(double x) { return log(x); } static inline int ImAbs(int x) { return x < 0 ? -x : x; } static inline float ImAbs(float x) { return fabsf(x); } static inline double ImAbs(double x) { return fabs(x); } -static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument -static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : ((x > 0.0) ? 1.0 : 0.0); } +static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument +static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } #ifdef IMGUI_ENABLE_SSE static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } #else diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 431a9183..04ab40d8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7247,8 +7247,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) - if (tab_bar->Tabs.Size > 1) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); tab_bar->TabsAddedNew = false; // Flags From c5db276521d56f3408cef7c17f20f05f9722abba Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Nov 2021 15:17:18 +0100 Subject: [PATCH 159/332] InputText, Nav: fixed tabbing through InputTextMultiline(). (#4761, #3092) Messy... Broken by 66f0fb9. Added ImGuiItemFlags_NoTabStop to EndGroup() ahead of time (not strictly needed here). --- imgui.cpp | 2 +- imgui_demo.cpp | 21 +++++++++++---------- imgui_widgets.cpp | 11 +++++++---- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ed3cebc2..2172d0fe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7942,7 +7942,7 @@ void ImGui::EndGroup() window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. ItemSize(group_bb.GetSize()); - ItemAdd(group_bb, 0); + ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 91bfb2d8..d8a25b55 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2285,7 +2285,7 @@ static void ShowDemoWindowWidgets() // Select an item type const char* item_names[] = { - "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputFloat", + "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat", "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" }; static int item_type = 4; @@ -2308,15 +2308,16 @@ static void ShowDemoWindowWidgets() if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) - if (item_type == 6) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input - if (item_type == 7) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 8) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 9) { ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item - if (item_type == 10){ ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) - if (item_type == 11){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node - if (item_type == 12){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. - if (item_type == 13){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } - if (item_type == 14){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window) + if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input + if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 10){ ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item + if (item_type == 11){ ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) + if (item_type == 12){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node + if (item_type == 13){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. + if (item_type == 14){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } + if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } // Display the values of IsItemHovered() and other common item state functions. // Note that the ImGuiHoveredFlags_XXX flags can be combined. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 04ab40d8..c3d09e5a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3979,6 +3979,7 @@ 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) { ImVec2 backup_pos = window->DC.CursorPos; @@ -3989,6 +3990,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ return false; } item_status_flags = g.LastItemData.StatusFlags; + item_data_backup = g.LastItemData; window->DC.CursorPos = backup_pos; // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. @@ -4773,18 +4775,19 @@ 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; + g.CurrentItemFlags |= 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... - ImGuiLastItemData item_data = g.LastItemData; + // FIXME: This quite messy/tricky, should attempt to get rid of the child window. EndGroup(); if (g.LastItemData.ID == 0) { g.LastItemData.ID = id; - g.LastItemData.InFlags = item_data.InFlags; - g.LastItemData.StatusFlags = item_data.StatusFlags; + g.LastItemData.InFlags = item_data_backup.InFlags; + g.LastItemData.StatusFlags = item_data_backup.StatusFlags; } } From f087a5b856b1f5e2d4572baa54ddff05073644b4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Nov 2021 16:02:47 +0100 Subject: [PATCH 160/332] Metrics: Added a node showing windows in submission order and showing the Begin() stack. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 50 +++++++++++++++++++++++++++++++++++++++++----- imgui_internal.h | 2 ++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5f18a91a..d7401c93 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -71,6 +71,7 @@ Other Changes: (an additional ItemSpacing.y was declared, affecting scrollbar range). - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) +- Metrics: Added a node showing windows in submission order and showing the Begin() stack. - Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] diff --git a/imgui.cpp b/imgui.cpp index 2172d0fe..a7568e4f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1946,7 +1946,7 @@ void ImGuiStorage::BuildSortByKey() { struct StaticFunc { - static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) + static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) { // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; @@ -1954,7 +1954,7 @@ void ImGuiStorage::BuildSortByKey() return 0; } }; - ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); + ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairComparerByID); } int ImGuiStorage::GetInt(ImGuiID key, int default_val) const @@ -5872,7 +5872,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // 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; + } // Process SetNextWindow***() calls // (FIXME: Consider splitting the HasXXX flags into X/Y components @@ -11314,6 +11317,7 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} // - DebugNodeWindow() [Internal] // - DebugNodeWindowSettings() [Internal] // - DebugNodeWindowsList() [Internal] +// - DebugNodeWindowsListByBeginStackParent() [Internal] //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_METRICS_WINDOW @@ -11536,8 +11540,27 @@ void ImGui::ShowMetricsWindow(bool* p_open) } // Windows - DebugNodeWindowsList(&g.Windows, "Windows"); - //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder"); + if (TreeNode("Windows", "Windows (%d)", g.Windows.Size)) + { + //SetNextItemOpen(true, ImGuiCond_Once); + DebugNodeWindowsList(&g.Windows, "By display order"); + DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)"); + if (TreeNode("By submission order (begin stack)")) + { + // 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]); + 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); + TreePop(); + } + + TreePop(); + } // DrawLists int drawlist_count = 0; @@ -12161,7 +12184,6 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la { if (!TreeNode(label, "%s (%d)", label, windows->Size)) return; - Text("(In front-to-back order:)"); for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back { PushID((*windows)[i]); @@ -12171,6 +12193,24 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la TreePop(); } +// FIXME-OPT: This is technically suboptimal, but it is simpler this way. +void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack) +{ + for (int i = 0; i < windows_size; i++) + { + ImGuiWindow* window = windows[i]; + if (window->ParentWindowInBeginStack != parent_in_begin_stack) + continue; + char buf[20]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext); + //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); + DebugNodeWindow(window, buf); + Indent(); + DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window); + Unindent(); + } +} + //----------------------------------------------------------------------------- // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index 52b20654..f624e707 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2026,6 +2026,7 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* ParentWindowInBeginStack; ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. 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. @@ -2784,6 +2785,7 @@ namespace ImGui IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); + IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); From 440824d4314b59049a196e9e6964d8e72aed25bd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Nov 2021 21:48:29 +0100 Subject: [PATCH 161/332] Backends: Fixed early out on empty clip rect. In particular, DX12 backend would warn about it (others not so much). (#4775, #4464) Amend/fix 2b0bd40b --- backends/imgui_impl_allegro5.cpp | 2 +- backends/imgui_impl_dx10.cpp | 2 +- backends/imgui_impl_dx11.cpp | 2 +- backends/imgui_impl_dx12.cpp | 2 +- backends/imgui_impl_dx9.cpp | 2 +- backends/imgui_impl_metal.mm | 2 +- backends/imgui_impl_opengl2.cpp | 2 +- backends/imgui_impl_opengl3.cpp | 2 +- backends/imgui_impl_sdlrenderer.cpp | 2 +- backends/imgui_impl_vulkan.cpp | 2 +- backends/imgui_impl_wgpu.cpp | 2 +- docs/CHANGELOG.txt | 1 + 12 files changed, 12 insertions(+), 11 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index ccb1396d..43b04f6b 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -174,7 +174,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle, Draw diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 5be46f67..79bf731d 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -256,7 +256,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index bfe5dca2..3f628cbc 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -266,7 +266,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 5058885c..e9d2e961 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -262,7 +262,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply Scissor/clipping rectangle, Bind texture, Draw diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 9234cb01..4cbd7959 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -251,7 +251,7 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply Scissor/clipping rectangle, Bind texture, Draw diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 18dcc914..26ddc922 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -513,7 +513,7 @@ void ImGui_ImplMetal_DestroyDeviceObjects() if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index ee52632d..65e778f5 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -203,7 +203,7 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle (Y is inverted in OpenGL) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 58664ea1..5a802887 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -445,7 +445,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle (Y is inverted in OpenGL) diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index 6c1786e7..e86f473f 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -164,7 +164,7 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) }; diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index e4f12f2b..aa6a3ddb 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -520,7 +520,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 66430515..072f2549 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -447,7 +447,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder // Project scissor/clipping rectangles into framebuffer space ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - if (clip_max.x < clip_min.x || clip_max.y < clip_min.y) + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; // Apply scissor/clipping rectangle, Draw diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d7401c93..c54ebcaa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -79,6 +79,7 @@ Other Changes: - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) +- Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the in-flux specs says. (#4766) [@meshula] From 5f5ba8eb119dc2c692a1cc81f4b93b482287f0ec Mon Sep 17 00:00:00 2001 From: Mikko Sivulainen Date: Wed, 1 Dec 2021 12:10:37 +0200 Subject: [PATCH 162/332] Docking: Fix typo (had no side effect) (#4778) Co-authored-by: Mikko Sivulainen --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 8341889a..adc4d6b8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13426,7 +13426,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) IM_ASSERT(last_focused_node != NULL); ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); - last_focused_node->SetLocalFlags(last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode); + last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode); node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode); last_focused_root_node->CentralNode = last_focused_node; } From 1ab3007752a78b8bb41df2cdda2729d8522e8328 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Dec 2021 14:42:58 +0100 Subject: [PATCH 163/332] Viewports: Fixed CTRL+TAB highlight outline on docked windows not always fitting in host viewport + moved EndFrameDrawDimmedBackgrounds() call + removed duplicate code in Begin() already in EndFrameDrawDimmedBackgrounds() --- docs/CHANGELOG.txt | 1 + imgui.cpp | 22 ++++------------------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 54d79fc3..682bd019 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -151,6 +151,7 @@ Docking+Viewports Branch: to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) - Viewports: Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) +- Viewports: Fixed CTRL+TAB highlight outline on docked windows not always fitting in host viewport. - Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) - Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) diff --git a/imgui.cpp b/imgui.cpp index adc4d6b8..fd1e563b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4664,7 +4664,7 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); ImRect bb = window->Rect(); bb.Expand(g.FontSize); - if (bb.Contains(window->Viewport->GetMainRect())) // If a window fits the entire viewport, adjust its highlight inward + if (!window->Viewport->GetMainRect().Contains(bb)) // If a window fits the entire viewport, adjust its highlight inward { bb.Expand(-g.FontSize - 1.0f); rounding = window->WindowRounding; @@ -4704,9 +4704,6 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); - // Draw modal whitening background on _other_ viewports than the one the modal is one - EndFrameDrawDimmedBackgrounds(); - // Update navigation: CTRL+Tab, wrap-around requests NavEndFrame(); @@ -4736,6 +4733,9 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); + // Draw modal/window whitening backgrounds + EndFrameDrawDimmedBackgrounds(); + // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) UpdateViewportsEndFrame(); @@ -6743,20 +6743,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList = &window->DrawListInst; } - // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTargetAnim == window) - { - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); - } - // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) // Work rectangle. From 5b1a70aa2dafcd589843deb3bd7e97473b47d3ec Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Dec 2021 15:24:55 +0100 Subject: [PATCH 164/332] Fixed RenderRectFilledWithHole() (used by docking) + added dummy EndFrameDrawDimmedBackgrounds() stub in master. The round corner flag change in 033dfd9d forgot to default to _None. --- imgui.cpp | 9 +++++++++ imgui_draw.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a7568e4f..2c43500f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -946,6 +946,7 @@ static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVe static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, 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 EndFrameDrawDimmedBackgrounds(); // Viewports static void UpdateViewportsNewFrame(); @@ -4430,6 +4431,11 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } +static void ImGui::EndFrameDrawDimmedBackgrounds() +{ + // (This is currently empty, left here to facilitate sync/merge with docking branch) +} + // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. void ImGui::EndFrame() { @@ -4485,6 +4491,9 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); + // Draw modal/window whitening backgrounds + EndFrameDrawDimmedBackgrounds(); + // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because children may not exist yet g.WindowsTempSortBuffer.resize(0); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 809c8490..f76dc2dc 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3901,10 +3901,10 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect const bool fill_R = (inner.Max.x < outer.Max.x); const bool fill_U = (inner.Min.y > outer.Min.y); const bool fill_D = (inner.Max.y < outer.Max.y); - if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); - if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); - if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); - if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); + if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); + if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft); if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight); if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft); From 6afcfe3442c07ef654c8a6d99af4e21f04629ad7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Dec 2021 17:46:04 +0100 Subject: [PATCH 165/332] Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d31e753a..12547d62 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -158,6 +158,7 @@ Docking+Viewports Branch: - Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) - Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) +- Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 39905874..28c31b54 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4646,7 +4646,7 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); } - // Draw modal whitening background between CTRL-TAB list + // Draw modal whitening background behind CTRL-TAB list if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active) { // Choose a draw list that will be front-most across all our children @@ -14488,7 +14488,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused) node->LastFrameFocused = g.FrameCount; ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawFlags_RoundCornersTop); + bool rounding_t = node->Pos.y <= host_window->Pos.y + DOCKING_SPLITTER_SIZE; + bool rounding_tl = rounding_t && (node->Pos.x <= host_window->Pos.x + DOCKING_SPLITTER_SIZE); + bool rounding_tr = rounding_t && (node->Pos.x + node->Size.x >= host_window->Pos.x + host_window->Size.x - DOCKING_SPLITTER_SIZE); + ImDrawFlags rounding_flags = ImDrawFlags_RoundCornersNone | (rounding_tl ? ImDrawFlags_RoundCornersTopLeft : 0) | (rounding_tr ? ImDrawFlags_RoundCornersTopRight : 0); + host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags); // Docking/Collapse button if (has_window_menu_button) From 8733ca49b0a2f29d858e2121f41759187bc7e234 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Dec 2021 18:39:52 +0100 Subject: [PATCH 166/332] Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. Fix 718e15c7d while preserving its intended property. Tested by "docking_window_appearing_layout". (#2109) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 41 ++++++++++++++++++++++------------------- imgui_internal.h | 2 +- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 12547d62..09a3e527 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -159,6 +159,7 @@ Docking+Viewports Branch: - Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) - Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. +- Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 28c31b54..291b6f39 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12908,7 +12908,7 @@ namespace ImGui // ImGuiDockNode tree manipulations static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); - static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false); + static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL); static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos); static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); @@ -13604,7 +13604,6 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) IsVisible = true; IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; - MarkedForPosSizeWrite = false; } ImGuiDockNode::~ImGuiDockNode() @@ -14045,7 +14044,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiContext& g = *GImGui; IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; - node->MarkedForPosSizeWrite = false; node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) @@ -15153,11 +15151,11 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG // Update Pos/Size for a node hierarchy (don't affect child Windows yet) // (Depth-first, Pre-Order) -void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes) +void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node) { // During the regular dock node update we write to all nodes. - // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away. - const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite); + // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away. + const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node; if (write_to_node) { node->Pos = pos; @@ -15171,15 +15169,21 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si ImGuiDockNode* child_1 = node->ChildNodes[1]; ImVec2 child_0_pos = pos, child_1_pos = pos; ImVec2 child_0_size = size, child_1_size = size; - if (child_0->IsVisible && child_1->IsVisible) + + const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0)); + const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1)); + const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node; + const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node; + + if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible) { + ImGuiContext& g = *GImGui; const float spacing = DOCKING_SPLITTER_SIZE; const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; const float size_avail = ImMax(size[axis] - spacing, 0.0f); // Size allocation policy // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows. - ImGuiContext& g = *GImGui; const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing. @@ -15230,11 +15234,15 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si child_1_pos[axis] += spacing + child_0_size[axis]; } - child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; - if (child_0->IsVisible) + if (only_write_to_single_node == NULL) + child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; + + const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible; + const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible; + if (child_0_recurse) DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); - if (child_1->IsVisible) + if (child_1_recurse) DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size); } @@ -16048,16 +16056,11 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu if (!node->IsVisible) { ImGuiDockNode* ancestor_node = node; - while (!ancestor_node->IsVisible) - { - ancestor_node->IsVisible = true; - ancestor_node->MarkedForPosSizeWrite = true; - if (ancestor_node->ParentNode) - ancestor_node = ancestor_node->ParentNode; - } + while (!ancestor_node->IsVisible && ancestor_node->ParentNode) + ancestor_node = ancestor_node->ParentNode; IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node)); - DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); + DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node); } // Add window to node diff --git a/imgui_internal.h b/imgui_internal.h index 0a2b3c4a..fa41c67e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1457,7 +1457,6 @@ struct IMGUI_API ImGuiDockNode bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window bool WantHiddenTabBarUpdate :1; bool WantHiddenTabBarToggle :1; - bool MarkedForPosSizeWrite :1; // Update by DockNodeTreeUpdatePosSize() write-filtering ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); @@ -2837,6 +2836,7 @@ namespace ImGui IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); IMGUI_API void DockNodeEndAmendTabBar(); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + inline bool DockNodeIsInHierarchyOf(ImGuiDockNode* node, ImGuiDockNode* parent) { while (node) { if (node == parent) return true; node = node->ParentNode; } return false; } inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } inline ImGuiID DockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImHashStr("#COLLAPSE", 0, node->ID); } inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } From bf80204e630f6bd30dfd77a82cd63358a44fe1ab Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Dec 2021 19:20:59 +0100 Subject: [PATCH 167/332] Docking: internals: extracted rounding corner calculation into reusable CalcRoundingFlagsForRectInRect() function. --- imgui.cpp | 7 ++----- imgui_draw.cpp | 11 +++++++++++ imgui_internal.h | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 291b6f39..18187728 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14486,10 +14486,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused) node->LastFrameFocused = g.FrameCount; ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - bool rounding_t = node->Pos.y <= host_window->Pos.y + DOCKING_SPLITTER_SIZE; - bool rounding_tl = rounding_t && (node->Pos.x <= host_window->Pos.x + DOCKING_SPLITTER_SIZE); - bool rounding_tr = rounding_t && (node->Pos.x + node->Size.x >= host_window->Pos.x + host_window->Size.x - DOCKING_SPLITTER_SIZE); - ImDrawFlags rounding_flags = ImDrawFlags_RoundCornersNone | (rounding_tl ? ImDrawFlags_RoundCornersTopLeft : 0) | (rounding_tr ? ImDrawFlags_RoundCornersTopRight : 0); + ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), DOCKING_SPLITTER_SIZE); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags); // Docking/Collapse button @@ -14958,7 +14955,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock overlay_rect.Min.y += GetFrameHeight(); if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable) for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) - overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding); + overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), DOCKING_SPLITTER_SIZE)); } // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index cc3e45af..437a4a41 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3926,6 +3926,17 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); } +ImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold) +{ + bool round_l = r_in.Min.x <= r_outer.Min.x + threshold; + bool round_r = r_in.Max.x >= r_outer.Max.x - threshold; + bool round_t = r_in.Min.y <= r_outer.Min.y + threshold; + bool round_b = r_in.Max.y >= r_outer.Max.y - threshold; + return ImDrawFlags_RoundCornersNone + | ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0) + | ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0); +} + // Helper for ColorPicker4() // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. // Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. diff --git a/imgui_internal.h b/imgui_internal.h index fa41c67e..4c7cd4af 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2976,6 +2976,7 @@ namespace ImGui IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding); + IMGUI_API ImDrawFlags CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // [1.71: 2019/06/07: Updating prototypes of some of the internal functions. Leaving those for reference for a short while] From 5c388c39f44283304e85afe9838597bffcfd6019 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 3 Dec 2021 12:03:05 +0200 Subject: [PATCH 168/332] Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enable 'ImGuiBackendFlags_RendererHasVtxOffset' in this backend. (#3926) --- backends/imgui_impl_sdlrenderer.cpp | 14 ++++++++------ backends/imgui_impl_sdlrenderer.h | 3 +-- docs/CHANGELOG.txt | 2 ++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index e86f473f..1aab5b48 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -10,14 +10,15 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // Missing features: -// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices (SDL_RenderGeometryRaw() is missing a vertex offset). // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // 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 // CHANGELOG +// 2021-12-03: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2021-10-06: Backup and restore modified ClipRect/Viewport. // 2021-09-21: Initial version. @@ -61,6 +62,7 @@ bool ImGui_ImplSDLRenderer_Init(SDL_Renderer* renderer) ImGui_ImplSDLRenderer_Data* bd = IM_NEW(ImGui_ImplSDLRenderer_Data)(); io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_sdlrenderer"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. bd->SDLRenderer = renderer; @@ -127,7 +129,7 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) SDL_Rect ClipRect; }; BackupSDLRendererState old = {}; - old.ClipEnabled = SDL_RenderIsClipEnabled(bd->SDLRenderer); + old.ClipEnabled = SDL_RenderIsClipEnabled(bd->SDLRenderer) == SDL_TRUE; SDL_RenderGetViewport(bd->SDLRenderer, &old.Viewport); SDL_RenderGetClipRect(bd->SDLRenderer, &old.ClipRect); @@ -170,9 +172,9 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) }; SDL_RenderSetClipRect(bd->SDLRenderer, &r); - const float* xy = (const float*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)); - const float* uv = (const float*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)); - const int* color = (const int*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)); + const float* xy = (const float*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, pos)); + const float* uv = (const float*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, uv)); + const int* color = (const int*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, col)); // Bind texture, Draw SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); @@ -180,7 +182,7 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), uv, (int)sizeof(ImDrawVert), - cmd_list->VtxBuffer.Size, + cmd_list->VtxBuffer.Size - pcmd->VtxOffset, idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx)); } } diff --git a/backends/imgui_impl_sdlrenderer.h b/backends/imgui_impl_sdlrenderer.h index e8f4d10b..51a1c45f 100644 --- a/backends/imgui_impl_sdlrenderer.h +++ b/backends/imgui_impl_sdlrenderer.h @@ -10,8 +10,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! -// Missing features: -// [ ] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices (SDL_RenderGeometryRaw() is missing a vertex offset). +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. #pragma once #include "imgui.h" // IMGUI_IMPL_API diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c54ebcaa..81f3a67f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -80,6 +80,8 @@ Other Changes: likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) +- Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, + enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] - Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the in-flux specs says. (#4766) [@meshula] From 78c6435dbb65e84897f22cf8d4a6c5169c3775bc Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Dec 2021 12:38:58 +0100 Subject: [PATCH 169/332] Inputs: (breaking wip) removed IsMouseTripleClicked() added recently (during 1.86 WIP), replaced with GetMouseClickedCount(). (#3229) --- docs/CHANGELOG.txt | 6 ++++-- imgui.cpp | 4 ++-- imgui.h | 8 ++++---- imgui_demo.cpp | 11 +++-------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 81f3a67f..c90d3ce6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,7 +41,9 @@ Breaking Changes: Other Changes: -- Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] +- Added GetMouseClickedCount() function, returning the number of successive clicks. (#3229) [@kudaba] + (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, + but it allows testing for triple clicks and more). - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) - Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of using the _NavEnableKeyboard @@ -63,7 +65,7 @@ Other Changes: - Clipper: currently focused item is automatically included in clipper range. Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is still submitted. (#3841, #1725) [@GamingMinds-DanielC, @ocornut] -- Cliooer: added ForceDisplayRangeByIndices() to force a given item (or several) to be stepped out +- Clipper: added ForceDisplayRangeByIndices() to force a given item (or several) to be stepped out during a clipping operation. (#3841) [@@GamingMinds-DanielC] - Clipper: rework so gamepad/keyboard navigation doesn't create spikes in number of items requested by the clipper to display. (#3841) diff --git a/imgui.cpp b/imgui.cpp index 2c43500f..f53bdafe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4796,11 +4796,11 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) return g.IO.MouseClickedCount[button] == 2; } -bool ImGui::IsMouseTripleClicked(ImGuiMouseButton button) +int ImGui::GetMouseClickedCount(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseClickedCount[button] == 3; + return g.IO.MouseClickedCount[button]; } // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. diff --git a/imgui.h b/imgui.h index 6cbe6944..1fcadcbb 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18512 +#define IMGUI_VERSION_NUM 18513 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -896,10 +896,10 @@ namespace ImGui // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held? - IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down) + IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) - IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true) - IMGUI_API bool IsMouseTripleClicked(ImGuiMouseButton button); // did mouse button triple-clicked? (note that a triple-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d8a25b55..97963ae1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5668,14 +5668,9 @@ static void ShowDemoWindowMisc() ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); int count = IM_ARRAYSIZE(io.MouseDown); - ImGui::Text("Mouse down:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text(" - clicked double:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text(" - clicked triple:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseTripleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text(" - clicked count:"); for (int i = 0; i < count; i++) if (io.MouseClickedCount[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseClickedCount[i]); } - //ImGui::Text(" - last count:"); for (int i = 0; i < count; i++) if (io.MouseClickedLastCount[i]) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, io.MouseClickedLastCount[i]); } - - ImGui::Text("Mouse released:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse down:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, ImGui::GetMouseClickedCount(i)); } + ImGui::Text("Mouse released:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused ImGui::TreePop(); From b16f738d0414b2586b43da2740528d8e93a053c4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Dec 2021 22:44:49 +0100 Subject: [PATCH 170/332] Docking: docked windows honor ImGuiCol_WindowBg. Host window in charge of rendering seams. (#2700, #2539 + Docked windows honor display their border properly. (#2522) Plus: better support for transparent one in nodes Side effects: DockContextBindNodeToWindow doesn't alter node->IsVisible. Side effects: ImDrawList:: _ResetForNewFrame() needs to merge, sane (in case of (Amended, force-pushed) --- docs/CHANGELOG.txt | 2 + imgui.cpp | 94 +++++++++++++++++++++++++++++++++++++--------- imgui_draw.cpp | 2 + imgui_internal.h | 5 ++- imgui_widgets.cpp | 6 ++- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 09a3e527..f184f7a6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -158,6 +158,8 @@ Docking+Viewports Branch: - Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) - Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) +- Docking, Style: Docked windows honor ImGuiCol_WindowBg. (#2700, #2539) +- Docking, Style: Docked windows honor display their border properly. (#2522) - Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. - Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order diff --git a/imgui.cpp b/imgui.cpp index 18187728..2d0a096f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4528,6 +4528,8 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = window->Viewport; g.IO.MetricsRenderWindows++; + if (window->Flags & ImGuiWindowFlags_DockNodeHost) + window->DrawList->ChannelsMerge(); AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { @@ -4709,6 +4711,9 @@ void ImGui::EndFrame() // Update navigation: CTRL+Tab, wrap-around requests NavEndFrame(); + // Update docking + DockContextEndFrame(&g); + SetCurrentViewport(NULL, NULL); // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) @@ -5653,11 +5658,11 @@ ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) return size_final; } -static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) +static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; - if (flags & ImGuiWindowFlags_ChildWindow) + if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } @@ -5950,7 +5955,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window) is_docking_transparent_payload = true; - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); if (window->ViewportOwned) { // No alpha @@ -5976,8 +5981,21 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (override_alpha) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); } - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + + // Render, for docked windows and host windows we ensure bg goes before decorations + ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList; + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + bg_draw_list->ChannelsSetCurrent(0); + if (window->DockIsActive) + window->DockNode->LastBgColor = bg_col; + + bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + bg_draw_list->ChannelsSetCurrent(1); } + if (window->DockIsActive) + window->DockNode->IsBgDrawnThisFrame = true; // Title bar // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag, @@ -6346,6 +6364,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->IDStack.resize(1); window->DrawList->_ResetForNewFrame(); window->DC.CurrentTableIdx = -1; + if (flags & ImGuiWindowFlags_DockNodeHost) + { + window->DrawList->ChannelsSplit(2); + window->DrawList->ChannelsSetCurrent(1); // Render decorations on channel 1 as we will render the backgrounds manually later + } // Restore buffer capacity when woken from a compacted state, to avoid if (window->MemoryCompacted) @@ -12786,6 +12809,9 @@ void ImGui::DestroyPlatformWindows() // | BeginDockableDragDropTarget() // | - DockNodePreviewDockRender() //----------------------------------------------------------------------------- +// - EndFrame() +// | DockContextEndFrame() +//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Docking: Internal Types @@ -12940,6 +12966,7 @@ namespace ImGui // - DockContextRebuildNodes() // - DockContextNewFrameUpdateUndocking() // - DockContextNewFrameUpdateDocking() +// - DockContextEndFrame() // - DockContextFindNodeByID() // - DockContextBindNodeToWindow() // - DockContextGenNodeID() @@ -13075,6 +13102,22 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) DockNodeUpdate(node); } +void ImGui::DockContextEndFrame(ImGuiContext* ctx) +{ + // Draw backgrounds of node missing their window + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &g.DockContext; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame) + { + ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size); + ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), DOCKING_SPLITTER_SIZE); + node->HostWindow->DrawList->ChannelsSetCurrent(0); + node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags); + } +} + static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id); @@ -13592,6 +13635,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) SplitAxis = ImGuiAxis_None; State = ImGuiDockNodeState_Unknown; + LastBgColor = IM_COL32_WHITE; HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; CountNodeWithWindows = 0; @@ -13603,6 +13647,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; + IsBgDrawnThisFrame = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; } @@ -14044,6 +14089,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiContext& g = *GImGui; IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; + node->IsBgDrawnThisFrame = false; node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) @@ -14186,6 +14232,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse; window_flags |= ImGuiWindowFlags_NoTitleBar; + SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); Begin(window_label, NULL, window_flags); PopStyleVar(); @@ -14224,15 +14271,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (g.NavWindow && g.NavWindow->RootWindow->DockNode && g.NavWindow->RootWindow->ParentWindow == host_window) node->LastFocusedNodeId = g.NavWindow->RootWindow->DockNode->ID; - // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size - // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! - const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; - if (render_dockspace_bg) - { - host_window->DrawList->ChannelsSplit(2); - host_window->DrawList->ChannelsSetCurrent(1); - } - // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace ImGuiDockNode* central_node = node->CentralNode; const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); @@ -14265,15 +14303,25 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Update position/size, process and draw resizing splitters if (node->IsRootNode() && host_window) { + host_window->DrawList->ChannelsSetCurrent(1); DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); DockNodeTreeUpdateSplitter(node); } // Draw empty node background (currently can only be the Central Node) - if (host_window && node->IsEmpty() && node->IsVisible && !(node_flags & ImGuiDockNodeFlags_PassthruCentralNode)) - host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); + if (host_window && node->IsEmpty() && node->IsVisible) + { + host_window->DrawList->ChannelsSetCurrent(0); + node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg); + if (node->LastBgColor != 0) + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor); + node->IsBgDrawnThisFrame = true; + } // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set. + // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size + // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! + const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg && node->IsVisible) { host_window->DrawList->ChannelsSetCurrent(0); @@ -14281,10 +14329,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f); else host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f); - host_window->DrawList->ChannelsMerge(); } // Draw and populate Tab Bar + if (host_window) + host_window->DrawList->ChannelsSetCurrent(1); if (host_window && node->Windows.Size > 0) { DockNodeUpdateTabBar(node, host_window); @@ -14319,7 +14368,13 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Render outer borders last (after the tab bar) if (node->IsRootNode()) + { + host_window->DrawList->ChannelsSetCurrent(1); RenderWindowOuterBorders(host_window); + } + + // Further rendering (= hosted windows background) will be drawn on layer 0 + host_window->DrawList->ChannelsSetCurrent(0); } // End host window @@ -15330,7 +15385,8 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) float cur_size_1 = child_1->Size[axis]; float min_size_0 = resize_limits[0] - child_0->Pos[axis]; float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; - if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)) + ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg); + if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col)) { if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) { @@ -16061,7 +16117,9 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu } // Add window to node + bool node_was_visible = node->IsVisible; DockNodeAddWindow(node, window, true); + node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see) IM_ASSERT(node == window->DockNode); return node; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 437a4a41..6a24a8ca 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -412,6 +412,8 @@ void ImDrawList::_ResetForNewFrame() IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); + if (_Splitter._Count > 1) + _Splitter.Merge(this); CmdBuffer.resize(0); IdxBuffer.resize(0); diff --git a/imgui_internal.h b/imgui_internal.h index 4c7cd4af..d4ce01dc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1432,6 +1432,7 @@ struct IMGUI_API ImGuiDockNode ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. ImGuiAxis SplitAxis; // [Split node only] Split axis (X or Y) ImGuiWindowClass WindowClass; // [Root node only] + ImU32 LastBgColor; ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. @@ -1449,6 +1450,7 @@ struct IMGUI_API ImGuiDockNode ImGuiDataAuthority AuthorityForViewport :3; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; + bool IsBgDrawnThisFrame :1; bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. bool HasWindowMenuButton :1; bool HasCentralNodeChild :1; @@ -2828,6 +2830,7 @@ namespace ImGui IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); + IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx); IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); @@ -3005,7 +3008,7 @@ namespace ImGui IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); - IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); + IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextItemOpen() data, if any. May return true when logging IMGUI_API void TreePushOverrideID(ImGuiID id); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8fa3a109..122b9dc7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1435,7 +1435,7 @@ void ImGui::Separator() } // Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. -bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) +bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -1487,7 +1487,9 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float } } - // Render + // Render at new position + if (bg_col & IM_COL32_A_MASK) + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f); const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f); From c122c0ef892a843f611fe15c162509d0f06837e1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Dec 2021 16:10:24 +0100 Subject: [PATCH 171/332] Docking: Amend b16f738 fixed dimming of docked window + removed thin highlight around windows (never worked on docked window, not viewports friendly, hard to move to EndFrame) (#2700, #2539, #2522) --- imgui.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2d0a096f..20bde9aa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6730,16 +6730,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (dim_bg_for_modal || dim_bg_for_window_list) { const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + window->DrawList->ChannelsSetCurrent(0); window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - } - - // Draw navigation selection/windowing rectangle background - if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) - { - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + window->DrawList->ChannelsSetCurrent(1); } // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) From da3a36eefda4152488981f9953a91c2b69aa6e4a Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Dec 2021 19:11:58 +0100 Subject: [PATCH 172/332] Backport from docking branch: minor stuff. Fixed software mouse cursor being rendered multiple times if Render() is called more than once. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 29 +++++++++++++++++++---------- imgui.h | 2 +- imgui_internal.h | 3 ++- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c90d3ce6..64ff179e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -76,6 +76,7 @@ Other Changes: - Metrics: Added a node showing windows in submission order and showing the Begin() stack. - Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] +- Misc: Fixed software mouse cursor being rendered multiple times if Render() is called more than once. - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] - CI: Add MinGW DLL build to test suite. [@rokups] - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce diff --git a/imgui.cpp b/imgui.cpp index f53bdafe..83800dd5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3386,6 +3386,17 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) return false; } +// This is also inlined in ItemAdd() +// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect! +void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) +{ + ImGuiContext& g = *GImGui; + g.LastItemData.ID = item_id; + g.LastItemData.InFlags = in_flags; + g.LastItemData.StatusFlags = item_flags; + g.LastItemData.Rect = item_rect; +} + float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) { if (wrap_pos_x < 0.0f) @@ -4533,6 +4544,7 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); + const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; @@ -4570,7 +4582,7 @@ void ImGui::Render() viewport->DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested by io.MouseDrawCursor flag - if (g.IO.MouseDrawCursor) + if (g.IO.MouseDrawCursor && first_render_of_frame) RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); // Add foreground ImDrawList (for each active viewport) @@ -5351,11 +5363,11 @@ ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) return size_final; } -static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) +static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; - if (flags & ImGuiWindowFlags_ChildWindow) + if (window->Flags & ImGuiWindowFlags_ChildWindow) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } @@ -5633,7 +5645,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); bool override_alpha = false; float alpha = 1.0f; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) @@ -6386,10 +6398,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. - g.LastItemData.ID = window->MoveId; - g.LastItemData.InFlags = g.CurrentItemFlags; - g.LastItemData.StatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; - g.LastItemData.Rect = title_bar_rect; + SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); #ifdef IMGUI_ENABLE_TEST_ENGINE if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) @@ -6837,8 +6846,8 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) return false; if (flags & ImGuiFocusedFlags_AnyWindow) return true; - IM_ASSERT(cur_window); // Not inside a Begin()/End() + IM_ASSERT(cur_window); // Not inside a Begin()/End() const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; if (flags & ImGuiHoveredFlags_RootWindow) cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); diff --git a/imgui.h b/imgui.h index 1fcadcbb..848d6b57 100644 --- a/imgui.h +++ b/imgui.h @@ -954,7 +954,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. - ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node). ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file diff --git a/imgui_internal.h b/imgui_internal.h index f624e707..c1111c87 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2100,7 +2100,7 @@ struct ImGuiTabItem }; // Storage for a tab bar (sizeof() 152 bytes) -struct ImGuiTabBar +struct IMGUI_API ImGuiTabBar { ImVector Tabs; ImGuiTabBarFlags Flags; @@ -2499,6 +2499,7 @@ namespace ImGui 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 IsClippedEx(const ImRect& bb, ImGuiID id); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); From 1dc3af381aeb0574d3080c1a105d43c56b087c7e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Dec 2021 18:53:28 +0100 Subject: [PATCH 173/332] Nav, Docking: reworked modal/ctrl+tab dimming system to be entirely processed at end of the frame, which will simplify things for an upcoming commit. (Will backport some of this back to master now.) --- imgui.cpp | 153 ++++++++++++++++++++++++++++------------------- imgui_internal.h | 1 + 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 20bde9aa..e7f8d042 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -959,7 +959,8 @@ static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVe 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 EndFrameDrawDimmedBackgrounds(); +static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); +static void RenderDimmedBackgrounds(); // 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. @@ -4623,58 +4624,86 @@ static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window) return window; } -static void ImGui::EndFrameDrawDimmedBackgrounds() +static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) { - ImGuiContext& g = *GImGui; + if ((col & IM_COL32_A_MASK) == 0) + return; - // Draw modal whitening background on _other_ viewports than the one the modal is one - ImGuiWindow* modal_window = GetTopMostPopupModal(); - const bool dim_bg_for_modal = (modal_window != NULL); - const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL); - if (dim_bg_for_modal || dim_bg_for_window_list) - for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) - { - ImGuiViewportP* viewport = g.Viewports[viewport_n]; - if (modal_window && viewport == modal_window->Viewport) - continue; - if (g.NavWindowingListWindow && viewport == g.NavWindowingListWindow->Viewport) - continue; - if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport) - continue; - if (viewport->Window && modal_window && IsWindowAbove(viewport->Window, modal_window)) - continue; - ImDrawList* draw_list = GetForegroundDrawList(viewport); - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); - } + ImGuiViewportP* viewport = window->Viewport; + ImRect viewport_rect = viewport->GetMainRect(); - // Draw modal whitening background behind CTRL-TAB list - if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active) + // Draw behind window by moving the draw command at the FRONT of the draw list + { + ImDrawList* draw_list = window->RootWindowDockTree->DrawList; + 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->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 over sibling docking nodes in a same docking tree + if (window->RootWindow->DockIsActive) { - // Choose a draw list that will be front-most across all our children - // In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together. - ImGuiWindow* window = g.NavWindowingTargetAnim; ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; - draw_list->PushClipRectFullScreen(); + draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); + //if (window->RootWindowDockTree != window->RootWindow) + RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); + draw_list->PopClipRect(); + } +} - // Docking: draw modal whitening background on other nodes of a same dock tree - // For CTRL+TAB within a docking node we need to render the dimming background in 8 steps - // (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted) - if (window->RootWindow->DockIsActive) - if (window->RootWindowDockTree != window->RootWindow) - RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding); +static void ImGui::RenderDimmedBackgrounds() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); + const bool dim_bg_for_modal = (modal_window != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); + if (!dim_bg_for_modal && !dim_bg_for_window_list) + return; + + ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL }; + if (dim_bg_for_modal) + { + // Draw dimming behind modal + RenderDimmedBackgroundBehindWindow(modal_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + viewports_already_dimmed[0] = modal_window->Viewport; + } + else if (dim_bg_for_window_list) + { + // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window + RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) + RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport; + viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL; - // Draw navigation selection/windowing rectangle border - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); + // Draw border around CTRL+Tab target window + ImGuiWindow* window = g.NavWindowingTargetAnim; + ImGuiViewport* viewport = window->Viewport; + float distance = g.FontSize; ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!window->Viewport->GetMainRect().Contains(bb)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); - draw_list->PopClipRect(); + bb.Expand(distance); + if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) + bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); + window->DrawList->PopClipRect(); + } + + // Draw dimming background on _other_ viewports than the ones our windows are in + for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) + { + ImGuiViewportP* viewport = g.Viewports[viewport_n]; + if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1]) + continue; + if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window)) + continue; + ImDrawList* draw_list = GetForegroundDrawList(viewport); + const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); + draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); } } @@ -4740,9 +4769,6 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); - // Draw modal/window whitening backgrounds - EndFrameDrawDimmedBackgrounds(); - // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) UpdateViewportsEndFrame(); @@ -4785,6 +4811,7 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); + const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; @@ -4814,6 +4841,10 @@ void ImGui::Render() if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); @@ -4827,7 +4858,7 @@ void ImGui::Render() // Draw software mouse cursor if requested by io.MouseDrawCursor flag // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor) - if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f) + if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f && first_render_of_frame) { float scale = g.Style.MouseCursorScale * viewport->DpiScale; if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) @@ -6723,20 +6754,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame) - ImGuiWindow* window_window_list = g.NavWindowingListWindow; - const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindowDockTree) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport)); - if (dim_bg_for_modal || dim_bg_for_window_list) - { - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) - window->DrawList->ChannelsSetCurrent(0); - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) - window->DrawList->ChannelsSetCurrent(1); - } - // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) @@ -8985,6 +9002,16 @@ ImGuiWindow* ImGui::GetTopMostPopupModal() return NULL; } +ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) + return popup; + return NULL; +} + void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index d4ce01dc..a0aa89b8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2772,6 +2772,7 @@ namespace ImGui IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); From 23ef6c149ea14d8a5981cfec02994dcd7d21d09e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Dec 2021 19:21:39 +0100 Subject: [PATCH 174/332] Reworked modal/ctrl+tab dimming system to be entirely processed at end of the frame (backported 1dc3af3 from docking) --- imgui.cpp | 105 ++++++++++++++++++++++++++++++----------------- imgui.h | 2 +- imgui_internal.h | 1 + 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 83800dd5..ddae0b8f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -946,7 +946,8 @@ static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVe static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, 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 EndFrameDrawDimmedBackgrounds(); +static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); +static void RenderDimmedBackgrounds(); // Viewports static void UpdateViewportsNewFrame(); @@ -4442,9 +4443,58 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } -static void ImGui::EndFrameDrawDimmedBackgrounds() +static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) { - // (This is currently empty, left here to facilitate sync/merge with docking branch) + if ((col & IM_COL32_A_MASK) == 0) + return; + + ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); + ImRect viewport_rect = viewport->GetMainRect(); + + // Draw behind window by moving the draw command at the FRONT of the draw list + { + ImDrawList* draw_list = window->RootWindow->DrawList; + 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->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); + } +} + +static void ImGui::RenderDimmedBackgrounds() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); + const bool dim_bg_for_modal = (modal_window != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); + if (!dim_bg_for_modal && !dim_bg_for_window_list) + return; + + if (dim_bg_for_modal) + { + // Draw dimming behind modal + RenderDimmedBackgroundBehindWindow(modal_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + } + else if (dim_bg_for_window_list) + { + // Draw dimming behind CTRL+Tab target window + RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + + // Draw border around CTRL+Tab target window + ImGuiWindow* window = g.NavWindowingTargetAnim; + ImGuiViewport* viewport = GetMainViewport(); + float distance = g.FontSize; + ImRect bb = window->Rect(); + bb.Expand(distance); + if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) + bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); + window->DrawList->PopClipRect(); + } } // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. @@ -4502,9 +4552,6 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); - // Draw modal/window whitening backgrounds - EndFrameDrawDimmedBackgrounds(); - // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because children may not exist yet g.WindowsTempSortBuffer.resize(0); @@ -4574,6 +4621,10 @@ void ImGui::Render() if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + // Setup ImDrawData structures for end-user g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; for (int n = 0; n < g.Viewports.Size; n++) @@ -6248,24 +6299,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal window background (darkens what is behind them, all viewports) - const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); - if (dim_bg_for_modal || dim_bg_for_window_list) - { - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - } - - // Draw navigation selection/windowing rectangle background - if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) - { - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); - } - // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) @@ -6293,20 +6326,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList = &window->DrawListInst; } - // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTargetAnim == window) - { - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); - } - // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) // Work rectangle. @@ -8363,6 +8382,16 @@ ImGuiWindow* ImGui::GetTopMostPopupModal() return NULL; } +ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) + return popup; + return NULL; +} + void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 848d6b57..ab815799 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18513 +#define IMGUI_VERSION_NUM 18514 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index c1111c87..f0711d67 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2539,6 +2539,7 @@ namespace ImGui IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); From aa41f16589cb78adee1553eccce5f02458ccef16 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 5 Dec 2021 18:47:28 +0100 Subject: [PATCH 175/332] Separator: fixed cover all columns while called inside a table. (#4787, #205) --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 64ff179e..4586831a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,7 @@ Other Changes: - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) [@actondev] +- Separator: fixed cover all columns while called inside a table. (#4787) - Clipper: currently focused item is automatically included in clipper range. Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is still submitted. (#3841, #1725) [@GamingMinds-DanielC, @ocornut] diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c3d09e5a..0ebb348d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1391,11 +1391,20 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) 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; + } + ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; if (columns) 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) const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness_draw)); ItemSize(ImVec2(0.0f, thickness_layout)); const bool item_visible = ItemAdd(bb, 0); @@ -1424,7 +1433,7 @@ void ImGui::Separator() // Those flags should eventually be overridable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - flags |= ImGuiSeparatorFlags_SpanAllColumns; + flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot. SeparatorEx(flags); } From eea836135ad5ea980b8131825356e90dfd6a8b13 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 5 Dec 2021 19:03:40 +0100 Subject: [PATCH 176/332] InputText: fix buffer modifications in callbacks while using resize callback (#4784) Regressed by 5ac25e7c7 (#4762) --- imgui_widgets.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0ebb348d..6d8d9ccc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4510,7 +4510,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields + callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback + IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); const bool buf_dirty = callback_data.BufDirty; From 6e141a9cae758c799bea91058966fbde281dfce0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 12:00:16 +0100 Subject: [PATCH 177/332] Internals: made ScrollbarEx() use ImS64 to facilitate use with larger ranges (not fully tested) + clipper tweaks (#3609, #3962 + https://github.com/ocornut/imgui_club/issues/20) This does NOT fix all problems with large ranges and floating point precision, it merely attenuate them. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 9 ++++++--- imgui_internal.h | 2 +- imgui_widgets.cpp | 22 ++++++++++++---------- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4586831a..401adf3c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -72,6 +72,7 @@ Other Changes: by the clipper to display. (#3841) - Clipper: fixed content height declaration slightly mismatching the value of when not using a clipper. (an additional ItemSpacing.y was declared, affecting scrollbar range). +- Clipper: minor and incomplete changes to tame down precision issues on very large ranges (#3609, #3962). - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) - Metrics: Added a node showing windows in submission order and showing the Begin() stack. diff --git a/imgui.cpp b/imgui.cpp index ddae0b8f..f813103f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2348,8 +2348,9 @@ static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) { // StartPosY starts from ItemsFrozen hence the subtraction + // Perform the add and multiply with double to allow seeking through larger ranges ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; - float pos_y = clipper->StartPosY + (item_n - data->ItemsFrozen) * clipper->ItemsHeight; + float pos_y = (float)((double)clipper->StartPosY + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); } @@ -2520,8 +2521,10 @@ bool ImGuiListClipper::Step() for (int i = 0; i < data->Ranges.Size; i++) if (data->Ranges[i].PosToIndexConvert) { - data->Ranges[i].Min = ImClamp(already_submitted + (int)ImFloor((data->Ranges[i].Min - window->DC.CursorPos.y) / ItemsHeight) + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); - data->Ranges[i].Max = ImClamp(already_submitted + (int)ImCeil((data->Ranges[i].Max - window->DC.CursorPos.y) / ItemsHeight) + 0 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); + int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y) / ItemsHeight); + int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y) / ItemsHeight) + 0.999999f); + data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); + data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); data->Ranges[i].PosToIndexConvert = false; } ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); diff --git a/imgui_internal.h b/imgui_internal.h index f0711d67..53af6aef 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2707,7 +2707,7 @@ namespace ImGui IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API void Scrollbar(ImGuiAxis axis); - IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawFlags flags); + IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6d8d9ccc..9d73d7dc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -896,7 +896,9 @@ void ImGui::Scrollbar(ImGuiAxis axis) } float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; - ScrollbarEx(bb, id, axis, &window->Scroll[axis], size_avail, size_contents, rounding_corners); + ImS64 scroll = (ImS64)window->Scroll[axis]; + ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners); + window->Scroll[axis] = (float)scroll; } // Vertical/Horizontal scrollbar @@ -905,7 +907,7 @@ void ImGui::Scrollbar(ImGuiAxis axis) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. // Still, the code should probably be made simpler.. -bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float size_avail_v, float size_contents_v, ImDrawFlags flags) +bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_avail_v, ImS64 size_contents_v, ImDrawFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -936,8 +938,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const float win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), 1.0f); - const float grab_h_pixels = ImClamp(scrollbar_size_v * (size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); + const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), (ImS64)1); + const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_avail_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). @@ -945,13 +947,13 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa bool hovered = false; ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v); - float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v); + float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space if (held && allow_interaction && grab_h_norm < 1.0f) { - float scrollbar_pos_v = bb.Min[axis]; - float mouse_pos_v = g.IO.MousePos[axis]; + const float scrollbar_pos_v = bb.Min[axis]; + const float mouse_pos_v = g.IO.MousePos[axis]; // 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); @@ -971,10 +973,10 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); - *p_scroll_v = IM_ROUND(scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); // Update values for rendering - scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + 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 From a76bc52da5f54322eee6dcb295e1f6082618ecfd Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 12:48:21 +0100 Subject: [PATCH 178/332] Window, Clipper: store initial precision loss and apply in clipper. (#3609, #3962 + https://github.com/ocornut/imgui_club/issues/20) --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 15 +++++++++++---- imgui.h | 2 +- imgui_internal.h | 2 ++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 401adf3c..869d1d17 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -72,7 +72,8 @@ Other Changes: by the clipper to display. (#3841) - Clipper: fixed content height declaration slightly mismatching the value of when not using a clipper. (an additional ItemSpacing.y was declared, affecting scrollbar range). -- Clipper: minor and incomplete changes to tame down precision issues on very large ranges (#3609, #3962). +- Clipper: various and incomplete changes to tame down scrolling and precision issues on very large ranges. + Passing an explicit height to the clipper now allows larger ranges. (#3609, #3962). - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) - Metrics: Added a node showing windows in submission order and showing the Begin() stack. diff --git a/imgui.cpp b/imgui.cpp index f813103f..722e84b4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2350,7 +2350,7 @@ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int it // StartPosY starts from ItemsFrozen hence the subtraction // Perform the add and multiply with double to allow seeking through larger ranges ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; - float pos_y = (float)((double)clipper->StartPosY + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); + float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); } @@ -2388,6 +2388,7 @@ void ImGuiListClipper::Begin(int items_count, float items_height) g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData()); ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; data->Reset(this); + data->LossynessOffset = window->DC.CursorStartPosLossyness.y; TempData = data; } @@ -2521,8 +2522,8 @@ bool ImGuiListClipper::Step() for (int i = 0; i < data->Ranges.Size; i++) if (data->Ranges[i].PosToIndexConvert) { - int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y) / ItemsHeight); - int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y) / ItemsHeight) + 0.999999f); + int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight); + int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight) + 0.999999f); data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); data->Ranges[i].PosToIndexConvert = false; @@ -6360,7 +6361,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; window->DC.GroupOffset.x = 0.0f; window->DC.ColumnsOffset.x = 0.0f; - window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); + + // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. + // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. + double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DC.ColumnsOffset.x; + double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + decoration_up_height; + window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); + window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; window->DC.CursorMaxPos = window->DC.CursorStartPos; diff --git a/imgui.h b/imgui.h index ab815799..d2da5fed 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18514 +#define IMGUI_VERSION_NUM 18515 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 53af6aef..b5d49049 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1190,6 +1190,7 @@ struct ImGuiListClipperRange struct ImGuiListClipperData { ImGuiListClipper* ListClipper; + float LossynessOffset; int StepNo; int ItemsFrozen; ImVector Ranges; @@ -1915,6 +1916,7 @@ struct IMGUI_API ImGuiWindowTempData ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; + ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensentate and fix the most common use case of large scroll area. // Keyboard/Gamepad navigation ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) From 027a7ba3eb19ec05e0f086997929b69cad6b1a0d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 16:20:53 +0100 Subject: [PATCH 179/332] Clipper: use line size instead of cursor comparaison when range are large. (#3609, #3962 + https://github.com/ocornut/imgui_club/issues/20) --- imgui.cpp | 6 ++++++ imgui.h | 2 +- imgui_internal.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 722e84b4..d5cf247e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2471,17 +2471,23 @@ bool ImGuiListClipper::Step() if (ItemsHeight <= 0.0f) { IM_ASSERT(data->StepNo == 1); + bool affected_by_floating_point_precision = false; if (table) { const float pos_y1 = table->RowPosY1; // Using RowPosY1 instead of StartPosY to handle clipper straddling the frozen row const float pos_y2 = table->RowPosY2; // Using RowPosY2 instead of CursorPos.y to take account of tallest cell. ItemsHeight = pos_y2 - pos_y1; window->DC.CursorPos.y = pos_y2; + affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(pos_y1) || ImIsFloatAboveGuaranteedIntegerPrecision(pos_y2); } else { ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); + affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); } + if (affected_by_floating_point_precision) + ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. + IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } diff --git a/imgui.h b/imgui.h index d2da5fed..215106ea 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18515 +#define IMGUI_VERSION_NUM 18516 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index b5d49049..d56204d3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -448,6 +448,7 @@ static inline float ImDot(const ImVec2& a, const ImVec2& b) 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); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry From 926addbfe2e9fc7b78cfb50d8bd90a93537dc6fe Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 17:03:47 +0100 Subject: [PATCH 180/332] Clipper: fixed invalid state when number of frozen table row is smaller than ItemCount. + Bonus rather unorthodox coding style. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 869d1d17..a3de7b03 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -74,6 +74,7 @@ Other Changes: (an additional ItemSpacing.y was declared, affecting scrollbar range). - Clipper: various and incomplete changes to tame down scrolling and precision issues on very large ranges. Passing an explicit height to the clipper now allows larger ranges. (#3609, #3962). +- Clipper: fixed invalid state when number of frozen table row is smaller than ItemCount. - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) - Metrics: Added a node showing windows in submission order and showing the Begin() stack. diff --git a/imgui.cpp b/imgui.cpp index d5cf247e..c3be5b98 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2435,10 +2435,7 @@ bool ImGuiListClipper::Step() // No items if (ItemsCount == 0 || GetSkipItemForListClipping()) - { - End(); - return false; - } + return (void)End(), false; // While we are in frozen row state, keep displaying items one by one, unclipped // FIXME: Could be stored as a table-agnostic state. @@ -2446,6 +2443,8 @@ bool ImGuiListClipper::Step() { DisplayStart = data->ItemsFrozen; DisplayEnd = data->ItemsFrozen + 1; + if (DisplayStart >= ItemsCount) + return (void)End(), false; data->ItemsFrozen++; return true; } @@ -2461,6 +2460,8 @@ bool ImGuiListClipper::Step() data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount); + if (DisplayStart == DisplayEnd) + return (void)End(), false; data->StepNo = 1; return true; } From 20e040c8581de043f4edd98285bf25dbd0b4965a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 17:17:15 +0100 Subject: [PATCH 181/332] Clipper, Tables: remove table specific code path should now be ok (added assert). --- imgui.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c3be5b98..c4a6e889 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2472,20 +2472,11 @@ bool ImGuiListClipper::Step() if (ItemsHeight <= 0.0f) { IM_ASSERT(data->StepNo == 1); - bool affected_by_floating_point_precision = false; if (table) - { - const float pos_y1 = table->RowPosY1; // Using RowPosY1 instead of StartPosY to handle clipper straddling the frozen row - const float pos_y2 = table->RowPosY2; // Using RowPosY2 instead of CursorPos.y to take account of tallest cell. - ItemsHeight = pos_y2 - pos_y1; - window->DC.CursorPos.y = pos_y2; - affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(pos_y1) || ImIsFloatAboveGuaranteedIntegerPrecision(pos_y2); - } - else - { - ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); - affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); - } + IM_ASSERT(table->RowPosY1 == StartPosY && table->RowPosY2 == window->DC.CursorPos.y); + + ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); + float affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); if (affected_by_floating_point_precision) ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. From 657073a650338a324526f6280b84e96a44f4ef62 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 18:49:57 +0100 Subject: [PATCH 182/332] Nav, Docking: fix dimming crash with accessing zero-cmd ImDrawList (amend 1dc3af3, 23ef6c1) # Conflicts: # imgui.cpp --- imgui.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c4a6e889..4366aef1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2476,7 +2476,7 @@ bool ImGuiListClipper::Step() IM_ASSERT(table->RowPosY1 == StartPosY && table->RowPosY2 == window->DC.CursorPos.y); ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); - float affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); + bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); if (affected_by_floating_point_precision) ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. @@ -4456,13 +4456,13 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 // Draw behind window by moving the draw command at the FRONT of the draw list { ImDrawList* draw_list = window->RootWindow->DrawList; - 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->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(); } } @@ -4608,6 +4608,10 @@ void ImGui::Render() AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + // 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; @@ -4623,10 +4627,6 @@ void ImGui::Render() if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); - // Draw modal/window whitening backgrounds - if (first_render_of_frame) - RenderDimmedBackgrounds(); - // Setup ImDrawData structures for end-user g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; for (int n = 0; n < g.Viewports.Size; n++) From fc198fe1dbae2e045b7fb31d53466a54f5368414 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 19:50:42 +0100 Subject: [PATCH 183/332] Nav, Docking: Fix dimming on docked windows. --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 056fbe96..92e97495 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4828,10 +4828,6 @@ void ImGui::Render() AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } - // Draw modal/window whitening backgrounds - if (first_render_of_frame) - RenderDimmedBackgrounds(); - // 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->RootWindowDockTree : NULL; @@ -4847,6 +4843,10 @@ void ImGui::Render() if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); From b38af0f52279e7066c9cd653ea89e22925b5a609 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 27 Jul 2021 16:47:18 +0300 Subject: [PATCH 184/332] Popups: Fix popups being closed by newly appearing windows. (#4317) * Popups/modals now remain open when new windows are created from within popup/modal begin stack. * Modals are not closed when new window appears behind active modal. Tested by "window_popup_interruptions" --- docs/CHANGELOG.txt | 4 ++ imgui.cpp | 114 +++++++++++++++++++++++++++++++++++++++++++-- imgui.h | 2 +- imgui_internal.h | 4 ++ 4 files changed, 119 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a3de7b03..3a9b36a0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,10 @@ Other Changes: (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, but it allows testing for triple clicks and more). - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) +- Modals, Popups, Windows: changes how appearing windows are interrupting popups and modals. (#4317) [@rokups] + - appearing windows created from within the begin stack of a popup/modal will no longer close it. + - appearing windows created not within the begin stack of a modal will no longer close the modal, + and automatically appear behind it. - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) - Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of using the _NavEnableKeyboard configuration flag. This is part of an effort to generalize the use of keyboard inputs. (#4023, #787). diff --git a/imgui.cpp b/imgui.cpp index 4366aef1..46fea469 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -948,6 +948,7 @@ static void RenderWindowDecorations(ImGuiWindow* window, const ImRec 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 ImGuiWindow* FindBlockingModal(ImGuiWindow* window); // Viewports static void UpdateViewportsNewFrame(); @@ -3910,7 +3911,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window, true)) + if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) clear_hovered_windows = true; // Disabled mouse? @@ -4466,6 +4467,23 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 } } +ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* bottom_most_visible_window = parent_window; + for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + if (!IsWindowWithinBeginStackOf(window, parent_window)) + break; + if (IsWindowActiveAndVisible(window)) + bottom_most_visible_window = window; + } + return bottom_most_visible_window; +} + static void ImGui::RenderDimmedBackgrounds() { ImGuiContext& g = *GImGui; @@ -4477,8 +4495,9 @@ static void ImGui::RenderDimmedBackgrounds() if (dim_bg_for_modal) { - // Draw dimming behind modal - RenderDimmedBackgroundBehindWindow(modal_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + // 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)); } else if (dim_bg_for_window_list) { @@ -5859,6 +5878,36 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags } } +// 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 +// - Modal1 // .. returns Modal2 +// - Window // .. returns Modal2 +// - Window // .. returns Modal2 +// - Modal2 // .. returns Modal2 +static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.Size <= 0) + 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--) + { + ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; + if (popup_window == NULL || !popup_window->WasActive || !(popup_window->Flags & ImGuiWindowFlags_Modal)) // Check WasActive, because this code may run before popup renders on current frame. + continue; + 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. + } + return NULL; +} + // Push a new Dear ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -6204,6 +6253,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) want_focus = true; else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; + + ImGuiWindow* modal = GetTopMostPopupModal(); + if (modal != NULL && !IsWindowWithinBeginStackOf(window, modal)) + { + // Avoid focusing a window that is created outside of active modal. This will prevent active modal from being closed. + // Since window is not focused it would reappear at the same display position like the last time it was visible. + // In case of completely new windows it would go to the top (over current modal), but input to such window would still be blocked by modal. + // Position window behind a modal that is not a begin-parent of this window. + want_focus = false; + if (window == window->RootWindow) + { + ImGuiWindow* blocking_modal = FindBlockingModal(window); + IM_ASSERT(blocking_modal != NULL); + BringWindowToDisplayBehind(window, blocking_modal); + } + } } // Handle manual resize: Resize Grips, Borders, Gamepad @@ -6581,6 +6646,34 @@ void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) } } +void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window) +{ + IM_ASSERT(window != NULL && behind_window != NULL); + ImGuiContext& g = *GImGui; + window = window->RootWindow; + behind_window = behind_window->RootWindow; + int pos_wnd = FindWindowDisplayIndex(window); + int pos_beh = FindWindowDisplayIndex(behind_window); + if (pos_wnd < pos_beh) + { + size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes); + g.Windows[pos_beh - 1] = window; + } + else + { + size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes); + g.Windows[pos_beh] = window; + } +} + +int ImGui::FindWindowDisplayIndex(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + return g.Windows.index_from_ptr(g.Windows.find(window)); +} + // Moving window to front of display and set focus (which happens to be back of our sorted list) void ImGui::FocusWindow(ImGuiWindow* window) { @@ -6810,6 +6903,19 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, return false; } +bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +{ + if (window->RootWindow == potential_parent) + return true; + while (window != NULL) + { + if (window == potential_parent) + return true; + window = window->ParentWindowInBeginStack; + } + return false; +} + bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; @@ -8492,7 +8598,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to 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) - if (popup_window->RootWindow == ref_window->RootWindow) + if (IsWindowWithinBeginStackOf(ref_window, popup_window)) { ref_window_is_descendent_of_popup = true; break; diff --git a/imgui.h b/imgui.h index 215106ea..d9c29ab4 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18516 +#define IMGUI_VERSION_NUM 18517 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index d56204d3..0a1cc100 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2418,6 +2418,7 @@ namespace ImGui IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_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); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); @@ -2433,6 +2434,9 @@ namespace ImGui IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window); + IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); + IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); From c80e8b964cbb75c4202949ad86f4b0bb3ea6aa44 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Dec 2021 16:26:52 +0100 Subject: [PATCH 185/332] Backends: OpenGL2, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. (#4790) --- backends/imgui_impl_marmalade.cpp | 6 +++--- backends/imgui_impl_opengl2.cpp | 4 ++-- docs/CHANGELOG.txt | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_marmalade.cpp b/backends/imgui_impl_marmalade.cpp index aa6b3206..6e9b8f80 100644 --- a/backends/imgui_impl_marmalade.cpp +++ b/backends/imgui_impl_marmalade.cpp @@ -6,13 +6,14 @@ // Missing features: // [ ] Renderer: Clipping rectangles are not honored. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-05-19: Renderer: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. // 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). @@ -91,9 +92,8 @@ void ImGui_Marmalade_RenderDrawData(ImDrawData* draw_data) pCurrentMaterial->SetAlphaTestMode(CIwMaterial::ALPHATEST_DISABLED); pCurrentMaterial->SetTexture((CIwTexture*)pcmd->GetTexID()); IwGxSetMaterial(pCurrentMaterial); - IwGxDrawPrims(IW_GX_TRI_LIST, (uint16*)idx_buffer, pcmd->ElemCount); + IwGxDrawPrims(IW_GX_TRI_LIST, (uint16*)(idx_buffer + pcmd->IdxOffset), pcmd->ElemCount); } - idx_buffer += pcmd->ElemCount; } IwGxFlush(); } diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 65e778f5..17a6faeb 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -19,6 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-08: OpenGL: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-01-03: OpenGL: Backup, setup and restore GL_SHADE_MODEL state, disable GL_STENCIL_TEST and disable GL_NORMAL_ARRAY client state to increase compatibility with legacy OpenGL applications. @@ -211,9 +212,8 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) // Bind texture, Draw glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset); } - idx_buffer += pcmd->ElemCount; } } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3a9b36a0..9aa7f2b4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,6 +90,9 @@ Other Changes: - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) +- Backends: OpenGL2, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. This is an old bug, + but due to the way we created drawlists, it never had any visible side-effect before. + The new code for handling Modal and CTRL+Tab dimming/whitening recently made the bug surface. (#4790) - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] From a19815dc6bb6ca03941f213424a59a362cfd8158 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Dec 2021 16:34:57 +0100 Subject: [PATCH 186/332] Backends: Allegro5: Fixed mishandling of the ImDrawCmd::IdxOffset field. (#4790) --- backends/imgui_impl_allegro5.cpp | 5 ++--- docs/CHANGELOG.txt | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 43b04f6b..8eb63614 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-05-19: Renderer: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) @@ -155,7 +156,6 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) } // Render command lists - int idx_offset = 0; ImVec2 clip_off = draw_data->DisplayPos; for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { @@ -180,9 +180,8 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) // Apply scissor/clipping rectangle, Draw ALLEGRO_BITMAP* texture = (ALLEGRO_BITMAP*)pcmd->GetTexID(); al_set_clipping_rectangle(clip_min.x, clip_min.y, clip_max.x - clip_min.x, clip_max.y - clip_min.y); - al_draw_prim(&vertices[0], bd->VertexDecl, texture, idx_offset, idx_offset + pcmd->ElemCount, ALLEGRO_PRIM_TRIANGLE_LIST); + al_draw_prim(&vertices[0], bd->VertexDecl, texture, pcmd->IdxOffset, pcmd->IdxOffset + pcmd->ElemCount, ALLEGRO_PRIM_TRIANGLE_LIST); } - idx_offset += pcmd->ElemCount; } } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9aa7f2b4..6f7553e9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,8 +90,8 @@ Other Changes: - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) -- Backends: OpenGL2, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. This is an old bug, - but due to the way we created drawlists, it never had any visible side-effect before. +- Backends: OpenGL2, Allegro5, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. + This is an old bug, but due to the way we created drawlists, it never had any visible side-effect before. The new code for handling Modal and CTRL+Tab dimming/whitening recently made the bug surface. (#4790) - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, From 0647cf434c3eca69343471c5d5eb6d6e03183547 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Dec 2021 17:02:24 +0100 Subject: [PATCH 187/332] Nav, Docking: Fix crash on dimming docked window and DockSpaceOverViewport() with PassthruCentralNode. (amend 1dc3af3, 23ef6c1, 657073a) --- imgui.cpp | 7 ++++++- imgui_draw.cpp | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 92e97495..fa971f1f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4636,7 +4636,11 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 // Draw behind window by moving the draw command at the FRONT of the draw list { + // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, + // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. ImDrawList* draw_list = window->RootWindowDockTree->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->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); ImDrawCmd cmd = draw_list->CmdBuffer.back(); @@ -4651,7 +4655,6 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 { ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); - //if (window->RootWindowDockTree != window->RootWindow) RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); draw_list->PopClipRect(); } @@ -4690,6 +4693,8 @@ static void ImGui::RenderDimmedBackgrounds() bb.Expand(distance); if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + if (window->DrawList->CmdBuffer.Size == 0) + window->DrawList->AddDrawCmd(); window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); window->DrawList->PopClipRect(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6a24a8ca..bcc320d8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -481,6 +481,7 @@ void ImDrawList::_PopUnusedDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) @@ -502,6 +503,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) // Try to merge two last draw commands void ImDrawList::_TryMergeDrawCmds() { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; ImDrawCmd* prev_cmd = curr_cmd - 1; if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) @@ -516,6 +518,7 @@ void ImDrawList::_TryMergeDrawCmds() void ImDrawList::_OnChangedClipRect() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) { @@ -538,6 +541,7 @@ void ImDrawList::_OnChangedClipRect() void ImDrawList::_OnChangedTextureID() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) { @@ -561,6 +565,7 @@ void ImDrawList::_OnChangedVtxOffset() { // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) From 270d4d0855ea3b2f66833ab1124ec475abe9df9c Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Dec 2021 17:02:24 +0100 Subject: [PATCH 188/332] Nav, Docking: Fix crash on dimming docked window and DockSpaceOverViewport() with PassthruCentralNode. (amend 1dc3af3, 23ef6c1, 657073a) # Conflicts: # imgui.cpp --- imgui.cpp | 6 ++++++ imgui_draw.cpp | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 46fea469..da8c6df2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4456,7 +4456,11 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 // Draw behind window by moving the draw command at the FRONT of the draw list { + // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, + // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. 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->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); ImDrawCmd cmd = draw_list->CmdBuffer.back(); @@ -4512,6 +4516,8 @@ static void ImGui::RenderDimmedBackgrounds() bb.Expand(distance); if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + if (window->DrawList->CmdBuffer.Size == 0) + window->DrawList->AddDrawCmd(); window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); window->DrawList->PopClipRect(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index f76dc2dc..6b5e70e5 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -473,6 +473,7 @@ void ImDrawList::_PopUnusedDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) @@ -494,6 +495,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) // Try to merge two last draw commands void ImDrawList::_TryMergeDrawCmds() { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; ImDrawCmd* prev_cmd = curr_cmd - 1; if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) @@ -508,6 +510,7 @@ void ImDrawList::_TryMergeDrawCmds() void ImDrawList::_OnChangedClipRect() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) { @@ -530,6 +533,7 @@ void ImDrawList::_OnChangedClipRect() void ImDrawList::_OnChangedTextureID() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) { @@ -553,6 +557,7 @@ void ImDrawList::_OnChangedVtxOffset() { // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) From 072caa4a9068b93070567879afc5fbcbb772c5ae Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 11:25:58 +0100 Subject: [PATCH 189/332] InputText: fixed incorrect padding when FrameBorder > 0. (#4794, #3781) --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6f7553e9..610bd212 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -66,6 +66,7 @@ Other Changes: - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) [@actondev] +- InputText: fixed incorrect padding when FrameBorder > 0. (#4794, #3781) - Separator: fixed cover all columns while called inside a table. (#4787) - Clipper: currently focused item is automatically included in clipper range. Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9d73d7dc..c084b0d8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4009,8 +4009,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ 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); - PopStyleVar(2); + PopStyleVar(3); PopStyleColor(); if (!child_visible) { From bdd2a943150628fdc827c285eafaced6005a2f64 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 11:30:42 +0100 Subject: [PATCH 190/332] InputTextMultiline: fixed vertical tracking with large values of FramePadding.y. (#3781, #4794) --- docs/CHANGELOG.txt | 3 ++- imgui_widgets.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 610bd212..66e0926e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -66,7 +66,8 @@ Other Changes: - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) [@actondev] -- InputText: fixed incorrect padding when FrameBorder > 0. (#4794, #3781) +- InputTextMultiline: fixed incorrect padding when FrameBorder > 0. (#3781, #4794) +- InputTextMultiline: fixed vertical tracking with large values of FramePadding.y. (#3781, #4794) - Separator: fixed cover all columns while called inside a table. (#4787) - Clipper: currently focused item is automatically included in clipper range. Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c084b0d8..dbdc0e5c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4693,7 +4693,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Test if cursor is vertically visible if (cursor_offset.y - g.FontSize < scroll_y) scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - inner_size.y >= scroll_y) + else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); From e03383d088fbfd8fc5b6a34864caf9e808dc2412 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 11:50:23 +0100 Subject: [PATCH 191/332] Fixes for tooltip over modals leading to incorrect modal dimming. (#4729) Amend 23ef6c1. Should rework tooltip to be in a consistent position in g.Windows[] --- imgui.cpp | 2 +- imgui.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index da8c6df2..5b2a6830 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4482,7 +4482,7 @@ ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* par continue; if (!IsWindowWithinBeginStackOf(window, parent_window)) break; - if (IsWindowActiveAndVisible(window)) + if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window)) bottom_most_visible_window = window; } return bottom_most_visible_window; diff --git a/imgui.h b/imgui.h index d9c29ab4..60e57820 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18517 +#define IMGUI_VERSION_NUM 18518 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From a8e1dde3572660886990e21493e56a892d025743 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 15:17:58 +0100 Subject: [PATCH 192/332] Fix static analyser, update readme. --- docs/README.md | 4 ++-- imgui.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7997a80b..58fcdb20 100644 --- a/docs/README.md +++ b/docs/README.md @@ -201,10 +201,10 @@ Ongoing Dear ImGui development is currently financially supported by users and p - [Blizzard](https://careers.blizzard.com/en-us/openings/engineering/all/all/all/1) *Double-chocolate sponsors* -- [Google](https://github.com/google/filament), [Nvidia](https://developer.nvidia.com/nvidia-omniverse), [Ubisoft](https://montreal.ubisoft.com/en/ubisoft-sponsors-user-interface-library-for-c-dear-imgui) +- [Ubisoft](https://montreal.ubisoft.com/en/ubisoft-sponsors-user-interface-library-for-c-dear-imgui), [Supercell](https://supercell.com) *Chocolate sponsors* -- [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras PranckeviÄius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [RAD Game Tools](http://www.radgametools.com/), [Supercell](https://supercell.com) +- [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras PranckeviÄius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [Google](https://github.com/google/filament), [Nvidia](https://developer.nvidia.com/nvidia-omniverse), [RAD Game Tools](http://www.radgametools.com/) *Salty-caramel sponsors* - [Framefield](http://framefield.com), [Grinding Gear Games](https://www.grindinggear.com), [Kylotonn](https://www.kylotonn.com), [Next Level Games](https://www.nextlevelgames.com), [O-Net Communications (USA)](http://en.o-netcom.com) diff --git a/imgui.cpp b/imgui.cpp index 5b2a6830..40946144 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12006,6 +12006,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list + IM_ASSERT(fg_draw_list != NULL); // For static analyzers if (window && IsItemHovered()) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) From c80179921819818c9743f168ceae6f300851b014 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 15:35:02 +0100 Subject: [PATCH 193/332] Added an assertion for the common user mistake of using "" as an identifier at the root level of a window. (#1414, #2562, #2807, #4008, #4158, #4375, #4548, #4657, #4796) #4158, #4375, #4548, #4657, #4796) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 66e0926e..bd4224a0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,10 @@ Breaking Changes: Other Changes: +- Added an assertion for the common user mistake of using "" as an identifier at the root level of a window + instead of using "##something". Empty identifiers are valid and useful in a very small amount of cases, + but 99.9% of the time if you need an empty label you should use "##something". (#1414, #2562, #2807, #4008, + #4158, #4375, #4548, #4657, #4796). READ THE FAQ ABOUT HOW THE ID STACK WORKS -> https://dearimgui.org/faq - Added GetMouseClickedCount() function, returning the number of successive clicks. (#3229) [@kudaba] (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, but it allows testing for triple clicks and more). diff --git a/imgui.cpp b/imgui.cpp index 40946144..968a5be2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7760,6 +7760,11 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu 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.org/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!"); + // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX if (id == g.DebugItemPickerBreakId) From f605351307c172f83794484a59b4c017e8270abd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 15:35:02 +0100 Subject: [PATCH 194/332] Added an assertion for the common user mistake of using "" as an identifier at the root level of a window. (#1414, #2562, #2807, #4008, #4158, #4375, #4548, #4657, #4796) #4158, #4375, #4548, #4657, #4796) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0304eaf5..bb83fcf5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -110,6 +110,10 @@ Breaking Changes: Other Changes: +- Added an assertion for the common user mistake of using "" as an identifier at the root level of a window + instead of using "##something". Empty identifiers are valid and useful in a very small amount of cases, + but 99.9% of the time if you need an empty label you should use "##something". (#1414, #2562, #2807, #4008, + #4158, #4375, #4548, #4657, #4796). READ THE FAQ ABOUT HOW THE ID STACK WORKS -> https://dearimgui.org/faq - Added GetMouseClickedCount() function, returning the number of successive clicks. (#3229) [@kudaba] (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, but it allows testing for triple clicks and more). diff --git a/imgui.cpp b/imgui.cpp index 17e4558b..45418b4d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8379,6 +8379,11 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu 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.org/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!"); + // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX if (id == g.DebugItemPickerBreakId) From 747f7fdbbaa1fdd46d5115c7333ab0722a7c2afc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 16:46:41 +0100 Subject: [PATCH 195/332] Docking: prevent docking any window created above a popup/modal. (#4317) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bb83fcf5..f548e785 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -184,6 +184,7 @@ Docking+Viewports Branch: - Docking, Style: Docked windows honor display their border properly. (#2522) - Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. - Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. +- Docking: Prevent docking any window created above a popup/modal. (#4317) - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 45418b4d..8a51bfd8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14897,12 +14897,23 @@ static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_win return false; } + // Prevent docking any window created above a popup + // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features), + // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test. + // But it would requires more work on our end because the dock host windows is technically created in NewFrame() + // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking. + ImGuiContext& g = *GImGui; + for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window) + if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window)) // Payload is created from within a popup begin stack. + return false; + return true; } static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) { - if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) + if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering return true; const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1; From 8a9fe268662292ed9495048d942d8d0341c03185 Mon Sep 17 00:00:00 2001 From: xndcn Date: Fri, 10 Dec 2021 18:47:54 +0100 Subject: [PATCH 196/332] Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 3 +-- imgui.h | 2 +- imgui_widgets.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bd4224a0..bc72a06b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -65,6 +65,7 @@ Other Changes: - Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when already at edge) reactivates the navigation highlight. +- Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) [@xndcn] - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs diff --git a/imgui.cpp b/imgui.cpp index 968a5be2..6e752f6e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12011,8 +12011,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list - IM_ASSERT(fg_draw_list != NULL); // For static analyzers - if (window && IsItemHovered()) + if (window && IsItemHovered() && fg_draw_list) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; diff --git a/imgui.h b/imgui.h index 60e57820..4d0ecaa1 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18518 +#define IMGUI_VERSION_NUM 18519 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index dbdc0e5c..c5c52ab1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6901,7 +6901,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) ImVec2 label_size = CalcTextSize(label, NULL, true); bool pressed; - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + bool menuset_is_open = (window->Flags & ImGuiWindowFlags_MenuBar) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) From b720f1f03c70c1b83482a902ad0f0c9a34a6861a Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 11 Dec 2021 08:10:21 +1100 Subject: [PATCH 197/332] Backends: OSX: Add Game Controller support. (#4759) --- backends/imgui_impl_osx.h | 1 + backends/imgui_impl_osx.mm | 48 +++++++++++++++++++ docs/CHANGELOG.txt | 1 + .../project.pbxproj | 4 ++ .../project.pbxproj | 4 ++ 5 files changed, 58 insertions(+) diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index e4c1d04a..c223c2cc 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -5,6 +5,7 @@ // Implemented features: // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // Issues: // [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 97555e6c..0c1b3dec 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -5,6 +5,7 @@ // Implemented features: // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // Issues: // [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. @@ -17,9 +18,11 @@ #include "imgui_impl_osx.h" #import #include +#import // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. // 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. // 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. @@ -234,6 +237,50 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() } } +void ImGui_ImplOSX_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + + GCController* controller; + if (@available(macOS 11.0, *)) + controller = GCController.current; + else + controller = GCController.controllers.firstObject; + + if (controller == nil || controller.extendedGamepad == nil) + { + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + return; + } + + GCExtendedGamepad* gp = controller.extendedGamepad; + +#define MAP_BUTTON(NAV_NO, NAME) { io.NavInputs[NAV_NO] = gp.NAME.isPressed ? 1.0 : 0.0; } + MAP_BUTTON(ImGuiNavInput_Activate, buttonA); + MAP_BUTTON(ImGuiNavInput_Cancel, buttonB); + MAP_BUTTON(ImGuiNavInput_Menu, buttonX); + MAP_BUTTON(ImGuiNavInput_Input, buttonY); + MAP_BUTTON(ImGuiNavInput_DpadLeft, dpad.left); + MAP_BUTTON(ImGuiNavInput_DpadRight, dpad.right); + MAP_BUTTON(ImGuiNavInput_DpadUp, dpad.up); + MAP_BUTTON(ImGuiNavInput_DpadDown, dpad.down); + MAP_BUTTON(ImGuiNavInput_FocusPrev, leftShoulder); + MAP_BUTTON(ImGuiNavInput_FocusNext, rightShoulder); + MAP_BUTTON(ImGuiNavInput_TweakSlow, leftTrigger); + MAP_BUTTON(ImGuiNavInput_TweakFast, rightTrigger); +#undef MAP_BUTTON + + io.NavInputs[ImGuiNavInput_LStickLeft] = gp.leftThumbstick.left.value; + io.NavInputs[ImGuiNavInput_LStickRight] = gp.leftThumbstick.right.value; + io.NavInputs[ImGuiNavInput_LStickUp] = gp.leftThumbstick.up.value; + io.NavInputs[ImGuiNavInput_LStickDown] = gp.leftThumbstick.down.value; + + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; +} + void ImGui_ImplOSX_NewFrame(NSView* view) { // Setup display size @@ -256,6 +303,7 @@ void ImGui_ImplOSX_NewFrame(NSView* view) g_Time = current_time; ImGui_ImplOSX_UpdateMouseCursorAndButtons(); + ImGui_ImplOSX_UpdateGamepads(); } static int mapCharacterToKey(int c) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bc72a06b..6af83a6e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -103,6 +103,7 @@ Other Changes: - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] +- Backends: OSX: Add Game Controller support (need linking GameController framework) (#4759) [@stuartcarnie] - Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the in-flux specs says. (#4766) [@meshula] diff --git a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj index 040fcd64..4bb4fc28 100644 --- a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj +++ b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05318E0E274C397200A8DE2E /* GameController.framework */; }; 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; }; @@ -32,6 +33,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 05318E0E274C397200A8DE2E /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; 07A82ED62139413C0078D120 /* imgui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = ../../imgui_internal.h; sourceTree = ""; }; 07A82ED72139413C0078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = ""; }; 5079822D257677DB0038A28D /* imgui_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = ../../imgui_tables.cpp; sourceTree = ""; }; @@ -76,6 +78,7 @@ files = ( 8309BDC6253CCCFE0045E2A1 /* AppKit.framework in Frameworks */, 83BBE9EC20EB471700295997 /* MetalKit.framework in Frameworks */, + 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */, 83BBE9ED20EB471700295997 /* Metal.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -133,6 +136,7 @@ 83BBE9E320EB46B800295997 /* Frameworks */ = { isa = PBXGroup; children = ( + 05318E0E274C397200A8DE2E /* GameController.framework */, 8309BDC5253CCCFE0045E2A1 /* AppKit.framework */, 8309BD8E253CCAAA0045E2A1 /* UIKit.framework */, 83BBE9EE20EB471C00295997 /* ModelIO.framework */, diff --git a/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj b/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj index 82fb267c..a168373d 100644 --- a/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj +++ b/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05E31B57274EF0360083FCB6 /* GameController.framework */; }; 07A82EDB213941D00078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82EDA213941D00078D120 /* imgui_widgets.cpp */; }; 4080A99820B02D340036BA46 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4080A98A20B02CD90036BA46 /* main.mm */; }; 4080A9A220B034280036BA46 /* imgui_impl_opengl2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A99E20B034280036BA46 /* imgui_impl_opengl2.cpp */; }; @@ -32,6 +33,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 05E31B57274EF0360083FCB6 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; 07A82EDA213941D00078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = ""; }; 4080A96B20B029B00036BA46 /* example_osx_opengl2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_osx_opengl2; sourceTree = BUILT_PRODUCTS_DIR; }; 4080A98A20B02CD90036BA46 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = SOURCE_ROOT; }; @@ -57,6 +59,7 @@ files = ( 4080A9B520B034EA0036BA46 /* OpenGL.framework in Frameworks */, 4080A9B320B034E40036BA46 /* Cocoa.framework in Frameworks */, + 05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -95,6 +98,7 @@ 4080A9B120B034E40036BA46 /* Frameworks */ = { isa = PBXGroup; children = ( + 05E31B57274EF0360083FCB6 /* GameController.framework */, 4080A9B420B034EA0036BA46 /* OpenGL.framework */, 4080A9B220B034E40036BA46 /* Cocoa.framework */, ); From f71ee5203e6f2e3fa9f0a43731626cfcc1603c9f Mon Sep 17 00:00:00 2001 From: SlavicPotato Date: Sat, 11 Dec 2021 15:03:30 +0100 Subject: [PATCH 198/332] Fix infinite loop in ImFontGlyphRangesBuilder::AddRanges if the user passes upper range = UINT16_MAX without IMGUI_USE_WCHAR32. (#4802) --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6af83a6e..638109dc 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -88,6 +88,8 @@ Other Changes: - Clipper: fixed invalid state when number of frozen table row is smaller than ItemCount. - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) +- Fonts: fixed infinite loop in ImFontGlyphRangesBuilder::AddRanges() when passing UINT16_MAX without + the IMGUI_USE_WCHAR32 compile-time option. (#4802) [@SlavicPotato] - Metrics: Added a node showing windows in submission order and showing the Begin() stack. - Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6b5e70e5..92570da7 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3080,8 +3080,8 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) - for (ImWchar c = ranges[0]; c <= ranges[1]; c++) - AddChar(c); + for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) + AddChar((ImWchar)c); } void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) From 1b6b8602c1115276bc3cf1868e6a07571a566f97 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 11 Dec 2021 09:20:17 +1100 Subject: [PATCH 199/332] Backends: OSX: Fix keyboard support. Handle scroll cancel. Don't set mouse cursor shape unconditionally. (#4759, #4253, #1873) Note the original FIXME: refered to GLFWs Cocoa implementation, which is largely what this commit provides. --- backends/imgui_impl_osx.h | 7 +- backends/imgui_impl_osx.mm | 293 +++++++++++++++++-------- docs/CHANGELOG.txt | 6 + examples/example_apple_metal/main.mm | 2 +- examples/example_apple_opengl2/main.mm | 5 +- 5 files changed, 212 insertions(+), 101 deletions(-) diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index c223c2cc..6cfe7eb2 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -6,10 +6,9 @@ // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// Issues: -// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. +// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space). -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs @@ -19,7 +18,7 @@ @class NSEvent; @class NSView; -IMGUI_IMPL_API bool ImGui_ImplOSX_Init(); +IMGUI_IMPL_API bool ImGui_ImplOSX_Init(NSView* _Nonnull view); IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view); IMGUI_IMPL_API bool ImGui_ImplOSX_HandleEvent(NSEvent* _Nonnull event, NSView* _Nullable view); diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 0c1b3dec..e1222fa7 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -6,22 +6,23 @@ // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// Issues: -// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. +// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space). // 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 -#include "imgui.h" -#include "imgui_impl_osx.h" +#import "imgui.h" +#import "imgui_impl_osx.h" #import -#include +#import +#import #import // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. // 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. @@ -40,15 +41,17 @@ // 2018-07-07: Initial version. @class ImFocusObserver; +@class KeyEventResponder; // Data -static double g_HostClockPeriod = 0.0; -static double g_Time = 0.0; -static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; -static bool g_MouseCursorHidden = false; -static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; -static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; -static ImFocusObserver* g_FocusObserver = NULL; +static double g_HostClockPeriod = 0.0; +static double g_Time = 0.0; +static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; +static bool g_MouseCursorHidden = false; +static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; +static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; +static ImFocusObserver* g_FocusObserver = nil; +static KeyEventResponder* g_KeyEventResponder = nil; // Undocumented methods for creating cursors. @interface NSCursor() @@ -77,6 +80,102 @@ static void resetKeys() io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; } +/** + KeyEventResponder implements the NSTextInputClient protocol as is required by the macOS text input manager. + + The macOS text input manager is invoked by calling the interpretKeyEvents method from the keyDown method. + Keyboard events are then evaluated by the macOS input manager and valid text input is passed back via the + insertText:replacementRange method. + + This is the same approach employed by other cross-platform libraries such as SDL2: + https://github.com/spurious/SDL-mirror/blob/e17aacbd09e65a4fd1e166621e011e581fb017a8/src/video/cocoa/SDL_cocoakeyboard.m#L53 + and GLFW: + https://github.com/glfw/glfw/blob/b55a517ae0c7b5127dffa79a64f5406021bf9076/src/cocoa_window.m#L722-L723 + */ +@interface KeyEventResponder: NSView +@end + +@implementation KeyEventResponder + +- (void)viewDidMoveToWindow +{ + // Ensure self is a first responder to receive the input events. + [self.window makeFirstResponder:self]; +} + +- (void)keyDown:(NSEvent*)event +{ + // Call to the macOS input manager system. + [self interpretKeyEvents:@[event]]; +} + +- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange +{ + ImGuiIO& io = ImGui::GetIO(); + + NSString* characters; + if ([aString isKindOfClass:[NSAttributedString class]]) + characters = [aString string]; + else + characters = (NSString*)aString; + + io.AddInputCharactersUTF8(characters.UTF8String); +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)doCommandBySelector:(SEL)myselector +{ +} + +- (nullable NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange +{ + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange +{ + return NSZeroRect; +} + +- (BOOL)hasMarkedText +{ + return NO; +} + +- (NSRange)markedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)selectedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (void)setMarkedText:(nonnull id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +{ +} + +- (void)unmarkText +{ +} + +- (nonnull NSArray*)validAttributesForMarkedText +{ + return @[]; +} + +@end + @interface ImFocusObserver : NSObject - (void)onApplicationBecomeActive:(NSNotification*)aNotification; @@ -106,7 +205,7 @@ static void resetKeys() @end // Functions -bool ImGui_ImplOSX_Init() +bool ImGui_ImplOSX_Init(NSView* view) { ImGuiIO& io = ImGui::GetIO(); @@ -118,29 +217,28 @@ bool ImGui_ImplOSX_Init() io.BackendPlatformName = "imgui_impl_osx"; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array. - const int offset_for_function_keys = 256 - 0xF700; - io.KeyMap[ImGuiKey_Tab] = '\t'; - io.KeyMap[ImGuiKey_LeftArrow] = NSLeftArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_RightArrow] = NSRightArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_UpArrow] = NSUpArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_DownArrow] = NSDownArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageUp] = NSPageUpFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageDown] = NSPageDownFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Home] = NSHomeFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_End] = NSEndFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Insert] = NSInsertFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Delete] = NSDeleteFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Backspace] = 127; - io.KeyMap[ImGuiKey_Space] = 32; - io.KeyMap[ImGuiKey_Enter] = 13; - io.KeyMap[ImGuiKey_Escape] = 27; - io.KeyMap[ImGuiKey_KeyPadEnter] = 3; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; + io.KeyMap[ImGuiKey_Tab] = kVK_Tab; + io.KeyMap[ImGuiKey_LeftArrow] = kVK_LeftArrow; + io.KeyMap[ImGuiKey_RightArrow] = kVK_RightArrow; + io.KeyMap[ImGuiKey_UpArrow] = kVK_UpArrow; + io.KeyMap[ImGuiKey_DownArrow] = kVK_DownArrow; + io.KeyMap[ImGuiKey_PageUp] = kVK_PageUp; + io.KeyMap[ImGuiKey_PageDown] = kVK_PageDown; + io.KeyMap[ImGuiKey_Home] = kVK_Home; + io.KeyMap[ImGuiKey_End] = kVK_End; + io.KeyMap[ImGuiKey_Insert] = kVK_F13; + io.KeyMap[ImGuiKey_Delete] = kVK_ForwardDelete; + io.KeyMap[ImGuiKey_Backspace] = kVK_Delete; + io.KeyMap[ImGuiKey_Space] = kVK_Space; + io.KeyMap[ImGuiKey_Enter] = kVK_Return; + io.KeyMap[ImGuiKey_Escape] = kVK_Escape; + io.KeyMap[ImGuiKey_KeyPadEnter] = kVK_ANSI_KeypadEnter; + io.KeyMap[ImGuiKey_A] = kVK_ANSI_A; + io.KeyMap[ImGuiKey_C] = kVK_ANSI_C; + io.KeyMap[ImGuiKey_V] = kVK_ANSI_V; + io.KeyMap[ImGuiKey_X] = kVK_ANSI_X; + io.KeyMap[ImGuiKey_Y] = kVK_ANSI_Y; + io.KeyMap[ImGuiKey_Z] = kVK_ANSI_Z; // Load cursors. Some of them are undocumented. g_MouseCursorHidden = false; @@ -193,6 +291,11 @@ bool ImGui_ImplOSX_Init() name:NSApplicationDidResignActiveNotification object:nil]; + // Add the NSTextInputClient to the view hierarchy, + // to receive keyboard events and translate them to input text. + g_KeyEventResponder = [[KeyEventResponder alloc] initWithFrame:NSZeroRect]; + [view addSubview:g_KeyEventResponder]; + return true; } @@ -227,8 +330,12 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() } else { - // Show OS mouse cursor - [g_MouseCursors[g_MouseCursors[imgui_cursor] ? imgui_cursor : ImGuiMouseCursor_Arrow] set]; + NSCursor* desired = g_MouseCursors[imgui_cursor] ?: g_MouseCursors[ImGuiMouseCursor_Arrow]; + // -[NSCursor set] generates measureable overhead if called unconditionally. + if (desired != NSCursor.currentCursor) + { + [desired set]; + } if (g_MouseCursorHidden) { g_MouseCursorHidden = false; @@ -306,17 +413,22 @@ void ImGui_ImplOSX_NewFrame(NSView* view) ImGui_ImplOSX_UpdateGamepads(); } -static int mapCharacterToKey(int c) +NSString* NSStringFromPhase(NSEventPhase phase) { - if (c >= 'a' && c <= 'z') - return c - 'a' + 'A'; - if (c == 25) // SHIFT+TAB -> TAB - return 9; - if (c >= 0 && c < 256) - return c; - if (c >= 0xF700 && c < 0xF700 + 256) - return c - 0xF700 + 256; - return -1; + static NSString* strings[] = + { + @"none", + @"began", + @"stationary", + @"changed", + @"ended", + @"cancelled", + @"mayBegin", + }; + + int pos = phase == NSEventPhaseNone ? 0 : __builtin_ctzl((NSUInteger)phase) + 1; + + return strings[pos]; } bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) @@ -349,6 +461,21 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if (event.type == NSEventTypeScrollWheel) { + // Ignore canceled events. + // + // From macOS 12.1, scrolling with two fingers and then decelerating + // by tapping two fingers results in two events appearing: + // + // 1. A scroll wheel NSEvent, with a phase == NSEventPhaseMayBegin, when the user taps + // two fingers to decelerate or stop the scroll events. + // + // 2. A scroll wheel NSEvent, with a phase == NSEventPhaseCancelled, when the user releases the + // two-finger tap. It is this event that sometimes contains large values for scrollingDeltaX and + // scrollingDeltaY. When these are added to the current x and y positions of the scrolling view, + // it appears to jump up or down. It can be observed in Preview, various JetBrains IDEs and here. + if (event.phase == NSEventPhaseCancelled) + return false; + double wheel_dx = 0.0; double wheel_dy = 0.0; @@ -370,6 +497,8 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) wheel_dy = [event deltaY]; } + //NSLog(@"dx=%0.3ff, dy=%0.3f, phase=%@", wheel_dx, wheel_dy, NSStringFromPhase(event.phase)); + if (fabs(wheel_dx) > 0.0) io.MouseWheelH += (float)wheel_dx * 0.1f; if (fabs(wheel_dy) > 0.0) @@ -377,59 +506,39 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) return io.WantCaptureMouse; } - // FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm. - if (event.type == NSEventTypeKeyDown) + if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) { - NSString* str = [event characters]; - NSUInteger len = [str length]; - for (NSUInteger i = 0; i < len; i++) - { - int c = [str characterAtIndex:i]; - if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) - io.AddInputCharacter((unsigned int)c); - - // We must reset in case we're pressing a sequence of special keys while keeping the command pressed - int key = mapCharacterToKey(c); - if (key != -1 && key < 256 && !io.KeySuper) - resetKeys(); - if (key != -1) - io.KeysDown[key] = true; - } + unsigned short code = event.keyCode; + IM_ASSERT(code >= 0 && code < IM_ARRAYSIZE(io.KeysDown)); + io.KeysDown[code] = event.type == NSEventTypeKeyDown; + NSEventModifierFlags flags = event.modifierFlags; + io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0; + io.KeyShift = (flags & NSEventModifierFlagShift) != 0; + io.KeyAlt = (flags & NSEventModifierFlagOption) != 0; + io.KeySuper = (flags & NSEventModifierFlagCommand) != 0; return io.WantCaptureKeyboard; } - if (event.type == NSEventTypeKeyUp) + if (event.type == NSEventTypeFlagsChanged) { - NSString* str = [event characters]; - NSUInteger len = [str length]; - for (NSUInteger i = 0; i < len; i++) + NSEventModifierFlags flags = event.modifierFlags; + switch (event.keyCode) { - int c = [str characterAtIndex:i]; - int key = mapCharacterToKey(c); - if (key != -1) - io.KeysDown[key] = false; + case kVK_Control: + io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0; + break; + case kVK_Shift: + io.KeyShift = (flags & NSEventModifierFlagShift) != 0; + break; + case kVK_Option: + io.KeyAlt = (flags & NSEventModifierFlagOption) != 0; + break; + case kVK_Command: + io.KeySuper = (flags & NSEventModifierFlagCommand) != 0; + break; } return io.WantCaptureKeyboard; } - if (event.type == NSEventTypeFlagsChanged) - { - unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; - - bool oldKeyCtrl = io.KeyCtrl; - bool oldKeyShift = io.KeyShift; - bool oldKeyAlt = io.KeyAlt; - bool oldKeySuper = io.KeySuper; - io.KeyCtrl = flags & NSEventModifierFlagControl; - io.KeyShift = flags & NSEventModifierFlagShift; - io.KeyAlt = flags & NSEventModifierFlagOption; - io.KeySuper = flags & NSEventModifierFlagCommand; - - // We must reset them as we will not receive any keyUp event if they where pressed with a modifier - if ((oldKeyShift && !io.KeyShift) || (oldKeyCtrl && !io.KeyCtrl) || (oldKeyAlt && !io.KeyAlt) || (oldKeySuper && !io.KeySuper)) - resetKeys(); - return io.WantCaptureKeyboard; - } - return false; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 638109dc..639a49f0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,9 @@ Breaking Changes: - Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. (#3841) +- Backends: OSX: Added NSView* parameter to ImGui_ImplOSX_Init(). (#4759) [@stuartcarnie] + Updated Apple+Metal and Apple+GL example applications accordingly. + Other Changes: @@ -105,6 +108,9 @@ Other Changes: - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] +- Backends: OSX: Generally fix keyboard support. Keyboard arrays indexed using kVK_* codes, e.g. + ImGui::IsKeyPressed(kVK_Space). Don't set mouse cursor shape unconditionally. Handle two fingers scroll + cancel event. (#4759, #4253, #1873) [@stuartcarnie] - Backends: OSX: Add Game Controller support (need linking GameController framework) (#4759) [@stuartcarnie] - Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index 5d4b7710..bbe51d31 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -119,7 +119,7 @@ return event; }]; - ImGui_ImplOSX_Init(); + ImGui_ImplOSX_Init(self.view); #endif } diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 825c8a8f..92774fbf 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -58,7 +58,7 @@ //ImGui::StyleColorsClassic(); // Setup Platform/Renderer backends - ImGui_ImplOSX_Init(); + ImGui_ImplOSX_Init(self); ImGui_ImplOpenGL2_Init(); // Load Fonts @@ -149,9 +149,6 @@ -(void)reshape { [[self openGLContext] update]; [self updateAndDrawDemoView]; } -(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; } -(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; } --(BOOL)acceptsFirstResponder { return (YES); } --(BOOL)becomeFirstResponder { return (YES); } --(BOOL)resignFirstResponder { return (YES); } -(void)dealloc { animationTimer = nil; } //----------------------------------------------------------------------------------- From d7350668d1ddfb58ced1facc08bd0e8d5759299c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Dec 2021 12:02:12 +0100 Subject: [PATCH 200/332] Fix OSX warnings + Examples/Apple+GL2 calls super reshape. (#4759, #1873) --- examples/example_apple_opengl2/main.mm | 2 +- imgui_draw.cpp | 2 +- imgui_widgets.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 92774fbf..f859162e 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -146,7 +146,7 @@ animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES]; } --(void)reshape { [[self openGLContext] update]; [self updateAndDrawDemoView]; } +-(void)reshape { [super reshape]; [[self openGLContext] update]; [self updateAndDrawDemoView]; } -(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; } -(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; } -(void)dealloc { animationTimer = nil; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 92570da7..3095d832 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3080,7 +3080,7 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) - for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) + for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 AddChar((ImWchar)c); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c5c52ab1..3cca2877 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3695,11 +3695,11 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #ifdef __APPLE__ // FIXME: Move setting to IO structure #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC #else +static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN #endif From 0e8c199e873fc1e328ea61eda02257e940e2626e Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 13 Dec 2021 13:57:33 +0200 Subject: [PATCH 201/332] CI: Use regex to fix vcxproj platform and toolset versions. (#3249) Remove Discord integration. --- .github/workflows/build.yml | 41 +++---------------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc8cbba7..13744f54 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,14 +37,10 @@ jobs: - name: Fix Projects shell: powershell run: | - # WARNING: This will need updating if toolset/sdk change in project files! + # CI workers do not supporter older Visual Studio versions. Fix projects to target newer available version. gci -recurse -filter "*.vcxproj" | ForEach-Object { - # Fix SDK and toolset for most samples. - (Get-Content $_.FullName) -Replace "v110","v142" | Set-Content -Path $_.FullName - (Get-Content $_.FullName) -Replace "8.1","10.0.18362.0" | Set-Content -Path $_.FullName - # Fix SDK and toolset for samples that require newer SDK/toolset. At the moment it is only dx12. - (Get-Content $_.FullName) -Replace "v140","v142" | Set-Content -Path $_.FullName - (Get-Content $_.FullName) -Replace "10.0.14393.0","10.0.18362.0" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "v\d{3}","v142" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "[\d\.]+","10.0.18362.0" | Set-Content -Path $_.FullName } # Not using matrix here because it would inflate job count too much. Check out and setup is done for every job and that makes build times way too long. @@ -497,34 +493,3 @@ jobs: run: | cd examples/example_android_opengl3/android gradle assembleDebug - - Discord-CI: - runs-on: ubuntu-18.04 - if: always() - needs: [Windows, Linux, MacOS, iOS, Emscripten, Android] - steps: - - uses: dearimgui/github_discord_notifier@latest - with: - discord-webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} - github-token: ${{ github.token }} - action-task: discord-jobs - discord-filter: "'{{ github.branch }}'.match(/master|docking/g) != null && '{{ run.conclusion }}' != '{{ last_run.conclusion }}'" - discord-username: GitHub Actions - discord-job-new-failure-message: '' - discord-job-fixed-failure-message: '' - discord-job-new-failure-embed: | - { - "title": "`{{ job.name }}` job is failing on `{{ github.branch }}`!", - "description": "Commit [{{ github.context.payload.head_commit.title }}]({{ github.context.payload.head_commit.url }}) pushed to [{{ github.branch }}]({{ github.branch_url }}) broke [{{ job.name }}]({{ job.url }}) build job.\nFailing steps: {{ failing_steps }}", - "url": "{{ job.url }}", - "color": "0xFF0000", - "timestamp": "{{ run.updated_at }}" - } - discord-job-fixed-failure-embed: | - { - "title": "`{{ github.branch }}` branch is no longer failing!", - "description": "Build failures were fixed on [{{ github.branch }}]({{ github.branch_url }}) branch.", - "color": "0x00FF00", - "url": "{{ github.context.payload.head_commit.url }}", - "timestamp": "{{ run.completed_at }}" - } From 11638fdf7d86b04907d69732320909a3af871a7b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Dec 2021 19:35:16 +0100 Subject: [PATCH 202/332] Menus: reuse more menu windows when stacked over popups. This is actually mostly aimed at ensuring the _ChildWindow flag on menu window doesn't vary as this would cause issues with our current g.WindowsFocusOrder[] scheme --- imgui.cpp | 6 +++++- imgui_internal.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 6e752f6e..99c8f0d9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5990,6 +5990,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window_stack_data.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindowStack.push_back(window_stack_data); g.CurrentWindow = NULL; + if (flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount++; if (flags & ImGuiWindowFlags_Popup) { @@ -6595,6 +6597,8 @@ void ImGui::End() // Pop from window stack g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; + if (window->Flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); @@ -8710,7 +8714,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) char name[20]; if (flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // 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 diff --git a/imgui_internal.h b/imgui_internal.h index 0a1cc100..02a5ebf3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1580,6 +1580,7 @@ struct ImGuiContext 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; // Viewports ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. @@ -1801,6 +1802,7 @@ struct ImGuiContext LastActiveIdTimer = 0.0f; CurrentItemFlags = ImGuiItemFlags_None; + BeginMenuCount = 0; NavWindow = NULL; NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; From a528398c7780647d8a7d3bc46593dec987e76a1f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Dec 2021 16:17:31 +0100 Subject: [PATCH 203/332] Internals: support for varying _ChildWindow flag for menu windows. (#3496, #4797) --- imgui.cpp | 31 +++++++++++++++++++++++++------ imgui.h | 2 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 99c8f0d9..39917cdb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5286,6 +5286,27 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin window->Collapsed = settings->Collapsed; } +static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiWindowFlags old_flags = window->Flags; + const bool child_flag_changed = (new_flags & ImGuiWindowFlags_ChildWindow) != (old_flags & ImGuiWindowFlags_ChildWindow); + + if ((just_created || child_flag_changed) && !(new_flags & ImGuiWindowFlags_ChildWindow)) + { + g.WindowsFocusOrder.push_back(window); + window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); + } + else if (child_flag_changed && (new_flags & ImGuiWindowFlags_ChildWindow)) + { + IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); + for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) + g.WindowsFocusOrder[n]->FocusOrder--; + g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); + window->FocusOrder = -1; + } +} + static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -5325,16 +5346,12 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - if (!(flags & ImGuiWindowFlags_ChildWindow)) - { - g.WindowsFocusOrder.push_back(window); - window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); - } - if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); + UpdateWindowInFocusOrderList(window, true, window->Flags); + return window; } @@ -5934,6 +5951,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_created = (window == NULL); if (window_just_created) window = CreateNewWindow(name, flags); + else + UpdateWindowInFocusOrderList(window, window_just_created, flags); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) diff --git a/imgui.h b/imgui.h index 4d0ecaa1..73375295 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18519 +#define IMGUI_VERSION_NUM 18520 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 48f263336bc7651e0b648d9aaacd7828e31b23f8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Dec 2021 19:46:01 +0100 Subject: [PATCH 204/332] Menus: fixed closing a menu inside a popup/modal. Fixed menu inside a popup/modal not inhibiting hovering of items in the popup/modal. (#3496, #4797) Fixed sub-menu items inside a popups from closing the popup (debatable). --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 +- imgui.h | 2 +- imgui_demo.cpp | 21 ++++++++++++++++++--- imgui_widgets.cpp | 42 +++++++++++++++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 639a49f0..05fdaeee 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -68,7 +68,10 @@ Other Changes: - Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when already at edge) reactivates the navigation highlight. +- Menus: fixed closing a menu inside a popup/modal by clicking on the popup/modal. (#3496, #4797) - Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) [@xndcn] +- Menus: fixed menu inside a popup/modal not inhibiting hovering of items in the popup/modal. (#3496, #4797) +- Menus: fixed sub-menu items inside a popups from closing the popup. - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs diff --git a/imgui.cpp b/imgui.cpp index 39917cdb..71f7a477 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8705,7 +8705,7 @@ void ImGui::CloseCurrentPopup() ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; bool close_parent = false; if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) - if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) + if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar)) close_parent = true; if (!close_parent) break; diff --git a/imgui.h b/imgui.h index 73375295..3a33653f 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18520 +#define IMGUI_VERSION_NUM 18521 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 97963ae1..b517aa2e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3345,11 +3345,26 @@ static void ShowDemoWindowPopups() } // Call the more complete ShowExampleMenuFile which we use in various places of this demo - if (ImGui::Button("File Menu..")) + if (ImGui::Button("With a menu..")) ImGui::OpenPopup("my_file_popup"); - if (ImGui::BeginPopup("my_file_popup")) + if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar)) { - ShowExampleMenuFile(); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Edit")) + { + ImGui::MenuItem("Dummy"); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + ImGui::Text("Hello from popup!"); + ImGui::Button("This is a dummy button.."); ImGui::EndPopup(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3cca2877..62151301 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6868,6 +6868,23 @@ void ImGui::EndMainMenuBar() End(); } +static bool IsRootOfOpenMenuSet() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) + return false; + + // Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID. + // This would however prevent the use of e.g. PuhsID() user code submitting menus. + // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, + // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. + // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup + // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. + const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; + return (upper_popup && /*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); +} + bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); @@ -6880,8 +6897,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + // The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) + if (window->Flags & ImGuiWindowFlags_ChildMenu) flags |= ImGuiWindowFlags_ChildWindow; // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). @@ -6900,11 +6918,12 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) g.MenusIdSubmittedThisFrame.push_back(id); ImVec2 label_size = CalcTextSize(label, NULL, true); - bool pressed; - bool menuset_is_open = (window->Flags & ImGuiWindowFlags_MenuBar) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + + // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) + const bool menuset_is_open = IsRootOfOpenMenuSet(); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + g.NavWindow = window; // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). @@ -6914,6 +6933,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) if (!enabled) BeginDisabled(); const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + bool pressed; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar @@ -7073,13 +7093,19 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut ImVec2 pos = window->DC.CursorPos; ImVec2 label_size = CalcTextSize(label, NULL, true); + const bool menuset_is_open = IsRootOfOpenMenuSet(); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) + g.NavWindow = window; + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. bool pressed; PushID(label); if (!enabled) BeginDisabled(); - const ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; + + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -7089,7 +7115,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut window->DC.CursorPos.x += IM_FLOOR(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, flags, ImVec2(w, 0.0f)); + pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); 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(). @@ -7104,7 +7130,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut float checkmark_w = IM_FLOOR(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, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); if (icon_w > 0.0f) RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); @@ -7121,6 +7147,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut if (!enabled) EndDisabled(); PopID(); + if (menuset_is_open) + g.NavWindow = backed_nav_window; return pressed; } From 389982eb5afbb9f93873d87f1201842ccbc82dec Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 26 Aug 2021 17:10:58 +0200 Subject: [PATCH 205/332] Backends: OpenGL3: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.. (#4468, #4504, #2981, #3381) --- backends/imgui_impl_opengl3.cpp | 19 +++++++++++++++++-- backends/imgui_impl_opengl3_loader.h | 5 +++++ docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 5a802887..0ed8348d 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers. // 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions. // 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -174,6 +175,8 @@ struct ImGui_ImplOpenGL3_Data GLuint AttribLocationVtxUV; GLuint AttribLocationVtxColor; unsigned int VboHandle, ElementsHandle; + GLsizeiptr VertexBufferSize; + GLsizeiptr IndexBufferSize; bool HasClipOrigin; ImGui_ImplOpenGL3_Data() { memset(this, 0, sizeof(*this)); } @@ -425,8 +428,20 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) const ImDrawList* cmd_list = draw_data->CmdLists[n]; // Upload vertex/index buffers - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert); + GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx); + if (bd->VertexBufferSize < vtx_buffer_size) + { + bd->VertexBufferSize = vtx_buffer_size; + glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW); + } + if (bd->IndexBufferSize < idx_buffer_size) + { + bd->IndexBufferSize = idx_buffer_size; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW); + } + glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data); for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index 9313deda..8452301e 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -249,11 +249,13 @@ typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); #endif #endif /* GL_VERSION_1_5 */ #ifndef GL_VERSION_2_0 @@ -447,6 +449,7 @@ union GL3WProcs { PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate; PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate; PFNGLBUFFERDATAPROC BufferData; + PFNGLBUFFERSUBDATAPROC BufferSubData; PFNGLCLEARPROC Clear; PFNGLCLEARCOLORPROC ClearColor; PFNGLCOMPILESHADERPROC CompileShader; @@ -506,6 +509,7 @@ GL3W_API extern union GL3WProcs imgl3wProcs; #define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate #define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate #define glBufferData imgl3wProcs.gl.BufferData +#define glBufferSubData imgl3wProcs.gl.BufferSubData #define glClear imgl3wProcs.gl.Clear #define glClearColor imgl3wProcs.gl.ClearColor #define glCompileShader imgl3wProcs.gl.CompileShader @@ -692,6 +696,7 @@ static const char *proc_names[] = { "glBlendEquationSeparate", "glBlendFuncSeparate", "glBufferData", + "glBufferSubData", "glClear", "glClearColor", "glCompileShader", diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 05fdaeee..ecf781fa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -105,6 +105,8 @@ Other Changes: - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) +- Backends: OpenGL3: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports + with some Intel HD drivers, and perhaps improve performances. (#4468, #4504, #2981, #3381) [@parbo] - Backends: OpenGL2, Allegro5, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. This is an old bug, but due to the way we created drawlists, it never had any visible side-effect before. The new code for handling Modal and CTRL+Tab dimming/whitening recently made the bug surface. (#4790) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 62151301..cfdbb96e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6882,7 +6882,7 @@ static bool IsRootOfOpenMenuSet() // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; - return (upper_popup && /*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); + return (/*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); } bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) From 1c4066cd9e0d73a262698185184fe97df5a699f4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Dec 2021 12:16:48 +0100 Subject: [PATCH 206/332] Internals: UpdateWindowInFocusOrderList: amend a528398 to fix docking. (#3496, #4797) --- imgui.cpp | 10 ++++++---- imgui_internal.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 71f7a477..d2e4bed5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5289,15 +5289,16 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) { ImGuiContext& g = *GImGui; - const ImGuiWindowFlags old_flags = window->Flags; - const bool child_flag_changed = (new_flags & ImGuiWindowFlags_ChildWindow) != (old_flags & ImGuiWindowFlags_ChildWindow); - if ((just_created || child_flag_changed) && !(new_flags & ImGuiWindowFlags_ChildWindow)) + const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; + const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; + if ((just_created || child_flag_changed) && !new_is_explicit_child) { + IM_ASSERT(!g.WindowsFocusOrder.contains(window)); g.WindowsFocusOrder.push_back(window); window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); } - else if (child_flag_changed && (new_flags & ImGuiWindowFlags_ChildWindow)) + else if (!just_created && child_flag_changed && new_is_explicit_child) { IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) @@ -5305,6 +5306,7 @@ static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); window->FocusOrder = -1; } + window->IsExplicitChild = new_is_explicit_child; } static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) diff --git a/imgui_internal.h b/imgui_internal.h index 02a5ebf3..17a15930 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1984,6 +1984,7 @@ struct IMGUI_API ImGuiWindow 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 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) From 248ed1b01dd4ab78a33ba1417b167a5fd24dfaa1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Dec 2021 12:16:48 +0100 Subject: [PATCH 207/332] Internals: UpdateWindowInFocusOrderList: amend a528398 to fix docking. (#3496, #4797) --- imgui.cpp | 10 ++++++---- imgui_internal.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c34f721c..b7e4a8e4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5547,15 +5547,16 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) { ImGuiContext& g = *GImGui; - const ImGuiWindowFlags old_flags = window->Flags; - const bool child_flag_changed = (new_flags & ImGuiWindowFlags_ChildWindow) != (old_flags & ImGuiWindowFlags_ChildWindow); - if ((just_created || child_flag_changed) && !(new_flags & ImGuiWindowFlags_ChildWindow)) + const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; + const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; + if ((just_created || child_flag_changed) && !new_is_explicit_child) { + IM_ASSERT(!g.WindowsFocusOrder.contains(window)); g.WindowsFocusOrder.push_back(window); window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); } - else if (child_flag_changed && (new_flags & ImGuiWindowFlags_ChildWindow)) + else if (!just_created && child_flag_changed && new_is_explicit_child) { IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) @@ -5563,6 +5564,7 @@ static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); window->FocusOrder = -1; } + window->IsExplicitChild = new_is_explicit_child; } static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) diff --git a/imgui_internal.h b/imgui_internal.h index f64d3bf2..e889acf1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2189,6 +2189,7 @@ struct IMGUI_API ImGuiWindow 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 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) From 06d5f9297de441d229f717bf6284bb14e71970f9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Dec 2021 16:03:20 +0100 Subject: [PATCH 208/332] Internals: reduced side-effects of setting window->HiddenFramesForRenderOnly > 0 --- imgui.cpp | 5 +++-- imgui.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b7e4a8e4..efa7dc6e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7070,7 +7070,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesCanSkipItems = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); + window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); // Disable inputs for requested number of frames if (window->DisableInputsFrames > 0) @@ -7081,7 +7082,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; - if (window->Collapsed || !window->Active || window->Hidden) + if (window->Collapsed || !window->Active || hidden_regular) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; diff --git a/imgui.h b/imgui.h index c122fffd..0dd92640 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18521 +#define IMGUI_VERSION_NUM 18522 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch From 4a43dcb940ea6616a28ae78e795e30f1f5f354c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Dec 2021 16:03:20 +0100 Subject: [PATCH 209/332] Internals: reduced side-effects of setting window->HiddenFramesForRenderOnly > 0 --- imgui.cpp | 5 +++-- imgui.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d2e4bed5..77ea282d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6570,7 +6570,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesCanSkipItems = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); + window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); // Disable inputs for requested number of frames if (window->DisableInputsFrames > 0) @@ -6581,7 +6582,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; - if (window->Collapsed || !window->Active || window->Hidden) + if (window->Collapsed || !window->Active || hidden_regular) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; diff --git a/imgui.h b/imgui.h index 3a33653f..ef32123a 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18521 +#define IMGUI_VERSION_NUM 18522 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 0cdc4a956530cbe64a4e319446f8d9d2d7d149ee Mon Sep 17 00:00:00 2001 From: thedmd Date: Wed, 2 May 2018 20:42:37 +0200 Subject: [PATCH 210/332] Backends: Win32: Store left/right variants of Ctrl/Shift/Alt mods in KeysDown[] array. (#2625) Technically not much required yet but will be by extra-keys work. fix miscleared GLFW field for consistency (no effect) --- backends/imgui_impl_glfw.cpp | 1 + backends/imgui_impl_win32.cpp | 21 +++++++++++++++++---- docs/CHANGELOG.txt | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index bf2a6543..1be2c524 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -270,6 +270,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. bd->PrevUserCallbackWindowFocus = NULL; + bd->PrevUserCallbackCursorEnter = NULL; bd->PrevUserCallbackMousebutton = NULL; bd->PrevUserCallbackScroll = NULL; bd->PrevUserCallbackKey = NULL; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 51fb4702..ce8f9d55 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -33,8 +33,9 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. -// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host windo doesn't have focus. +// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events). // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1). @@ -426,11 +427,23 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (wParam < 256) io.KeysDown[wParam] = down; if (wParam == VK_CONTROL) - io.KeyCtrl = down; + { + io.KeysDown[VK_LCONTROL] = ((::GetKeyState(VK_LCONTROL) & 0x8000) != 0); + io.KeysDown[VK_RCONTROL] = ((::GetKeyState(VK_RCONTROL) & 0x8000) != 0); + io.KeyCtrl = io.KeysDown[VK_LCONTROL] || io.KeysDown[VK_RCONTROL]; + } if (wParam == VK_SHIFT) - io.KeyShift = down; + { + io.KeysDown[VK_LSHIFT] = ((::GetKeyState(VK_LSHIFT) & 0x8000) != 0); + io.KeysDown[VK_RSHIFT] = ((::GetKeyState(VK_RSHIFT) & 0x8000) != 0); + io.KeyShift = io.KeysDown[VK_LSHIFT] || io.KeysDown[VK_RSHIFT]; + } if (wParam == VK_MENU) - io.KeyAlt = down; + { + io.KeysDown[VK_LMENU] = ((::GetKeyState(VK_LMENU) & 0x8000) != 0); + io.KeysDown[VK_RMENU] = ((::GetKeyState(VK_RMENU) & 0x8000) != 0); + io.KeyAlt = io.KeysDown[VK_LMENU] || io.KeysDown[VK_RMENU]; + } return 0; } case WM_SETFOCUS: diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ecf781fa..25d91ed6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -110,6 +110,7 @@ Other Changes: - Backends: OpenGL2, Allegro5, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. This is an old bug, but due to the way we created drawlists, it never had any visible side-effect before. The new code for handling Modal and CTRL+Tab dimming/whitening recently made the bug surface. (#4790) +- Backends: Win32: Store left/right variants of Ctrl/Shift/Alt mods in KeysDown[] array. (#2625) [@thedmd] - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] From d9bc1e44af3a52babdf40237148b028388778346 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Dec 2021 19:29:41 +0100 Subject: [PATCH 211/332] Backends: Marmalade: Removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. (#368, #375) --- backends/imgui_impl_marmalade.cpp | 318 ------------------ backends/imgui_impl_marmalade.h | 28 -- docs/BACKENDS.md | 5 +- docs/CHANGELOG.txt | 2 + docs/EXAMPLES.md | 4 - docs/README.md | 2 +- examples/example_marmalade/data/app.icf | 32 -- examples/example_marmalade/main.cpp | 124 ------- .../example_marmalade/marmalade_example.mkb | 47 --- imgui.cpp | 1 + 10 files changed, 6 insertions(+), 557 deletions(-) delete mode 100644 backends/imgui_impl_marmalade.cpp delete mode 100644 backends/imgui_impl_marmalade.h delete mode 100644 examples/example_marmalade/data/app.icf delete mode 100644 examples/example_marmalade/main.cpp delete mode 100644 examples/example_marmalade/marmalade_example.mkb diff --git a/backends/imgui_impl_marmalade.cpp b/backends/imgui_impl_marmalade.cpp deleted file mode 100644 index 6e9b8f80..00000000 --- a/backends/imgui_impl_marmalade.cpp +++ /dev/null @@ -1,318 +0,0 @@ -// dear imgui: Renderer + Platform Backend for Marmalade + IwGx -// Marmalade code: Copyright (C) 2015 by Giovanni Zito (this file is part of Dear ImGui) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'CIwTexture*' as ImTextureID. Read the FAQ about ImTextureID! -// Missing features: -// [ ] Renderer: Clipping rectangles are not honored. - -// 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 - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. -// 2021-05-19: Renderer: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) -// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. -// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). -// 2018-11-30: Misc: Setting up io.BackendPlatformName/io.BackendRendererName so they can be displayed in the About Window. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_Marmalade_RenderDrawData() in the .h file so you can call it yourself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. - -#include "imgui.h" -#include "imgui_impl_marmalade.h" - -#include -#include -#include -#include -#include - -// Data -static double g_Time = 0.0f; -static bool g_MousePressed[3] = { false, false, false }; -static CIwTexture* g_FontTexture = NULL; -static char* g_ClipboardText = NULL; -static bool g_osdKeyboardEnabled = false; - -// use this setting to scale the interface - e.g. on device you could use 2 or 3 scale factor -static ImVec2 g_RenderScale = ImVec2(1.0f, 1.0f); - -// Render function. -void ImGui_Marmalade_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized - if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) - return; - - // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - const int nVert = cmd_list->VtxBuffer.Size; - CIwFVec2* pVertStream = IW_GX_ALLOC(CIwFVec2, nVert); - CIwFVec2* pUVStream = IW_GX_ALLOC(CIwFVec2, nVert); - CIwColour* pColStream = IW_GX_ALLOC(CIwColour, nVert); - - for (int i = 0; i < nVert; i++) - { - // FIXME-OPT: optimize multiplication on GPU using vertex shader/projection matrix. - pVertStream[i].x = cmd_list->VtxBuffer[i].pos.x * g_RenderScale.x; - pVertStream[i].y = cmd_list->VtxBuffer[i].pos.y * g_RenderScale.y; - pUVStream[i].x = cmd_list->VtxBuffer[i].uv.x; - pUVStream[i].y = cmd_list->VtxBuffer[i].uv.y; - pColStream[i] = cmd_list->VtxBuffer[i].col; - } - - IwGxSetVertStreamScreenSpace(pVertStream, nVert); - IwGxSetUVStream(pUVStream); - IwGxSetColStream(pColStream, nVert); - IwGxSetNormStream(0); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - // FIXME: Not honoring ClipRect fields. - CIwMaterial* pCurrentMaterial = IW_GX_ALLOC_MATERIAL(); - pCurrentMaterial->SetShadeMode(CIwMaterial::SHADE_FLAT); - pCurrentMaterial->SetCullMode(CIwMaterial::CULL_NONE); - pCurrentMaterial->SetFiltering(false); - pCurrentMaterial->SetAlphaMode(CIwMaterial::ALPHA_BLEND); - pCurrentMaterial->SetDepthWriteMode(CIwMaterial::DEPTH_WRITE_NORMAL); - pCurrentMaterial->SetAlphaTestMode(CIwMaterial::ALPHATEST_DISABLED); - pCurrentMaterial->SetTexture((CIwTexture*)pcmd->GetTexID()); - IwGxSetMaterial(pCurrentMaterial); - IwGxDrawPrims(IW_GX_TRI_LIST, (uint16*)(idx_buffer + pcmd->IdxOffset), pcmd->ElemCount); - } - } - IwGxFlush(); - } - - // TODO: restore modified state (i.e. mvp matrix) -} - -static const char* ImGui_Marmalade_GetClipboardText(void* /*user_data*/) -{ - if (!s3eClipboardAvailable()) - return NULL; - - if (int size = s3eClipboardGetText(NULL, 0)) - { - if (g_ClipboardText) - delete[] g_ClipboardText; - g_ClipboardText = new char[size]; - g_ClipboardText[0] = '\0'; - s3eClipboardGetText(g_ClipboardText, size); - } - - return g_ClipboardText; -} - -static void ImGui_Marmalade_SetClipboardText(void* /*user_data*/, const char* text) -{ - if (s3eClipboardAvailable()) - s3eClipboardSetText(text); -} - -int32 ImGui_Marmalade_PointerButtonEventCallback(void* system_data, void* user_data) -{ - // pEvent->m_Button is of type s3ePointerButton and indicates which mouse - // button was pressed. For touchscreen this should always have the value - // S3E_POINTER_BUTTON_SELECT - s3ePointerEvent* pEvent = (s3ePointerEvent*)system_data; - - if (pEvent->m_Pressed == 1) - { - if (pEvent->m_Button == S3E_POINTER_BUTTON_LEFTMOUSE) - g_MousePressed[0] = true; - if (pEvent->m_Button == S3E_POINTER_BUTTON_RIGHTMOUSE) - g_MousePressed[1] = true; - if (pEvent->m_Button == S3E_POINTER_BUTTON_MIDDLEMOUSE) - g_MousePressed[2] = true; - if (pEvent->m_Button == S3E_POINTER_BUTTON_MOUSEWHEELUP) - io.MouseWheel += pEvent->m_y; - if (pEvent->m_Button == S3E_POINTER_BUTTON_MOUSEWHEELDOWN) - io.MouseWheel += pEvent->m_y; - } - - return 0; -} - -int32 ImGui_Marmalade_KeyCallback(void* system_data, void* user_data) -{ - ImGuiIO& io = ImGui::GetIO(); - s3eKeyboardEvent* e = (s3eKeyboardEvent*)system_data; - if (e->m_Pressed == 1) - io.KeysDown[e->m_Key] = true; - if (e->m_Pressed == 0) - io.KeysDown[e->m_Key] = false; - - io.KeyCtrl = s3eKeyboardGetState(s3eKeyLeftControl) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightControl) == S3E_KEY_STATE_DOWN; - io.KeyShift = s3eKeyboardGetState(s3eKeyLeftShift) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightShift) == S3E_KEY_STATE_DOWN; - io.KeyAlt = s3eKeyboardGetState(s3eKeyLeftAlt) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightAlt) == S3E_KEY_STATE_DOWN; - io.KeySuper = s3eKeyboardGetState(s3eKeyLeftWindows) == S3E_KEY_STATE_DOWN || s3eKeyboardGetState(s3eKeyRightWindows) == S3E_KEY_STATE_DOWN; - - return 0; -} - -int32 ImGui_Marmalade_CharCallback(void* system_data, void* user_data) -{ - ImGuiIO& io = ImGui::GetIO(); - s3eKeyboardCharEvent* e = (s3eKeyboardCharEvent*)system_data; - io.AddInputCharacter((unsigned int)e->m_Char); - - return 0; -} - -bool ImGui_Marmalade_CreateDeviceObjects() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // Upload texture to graphics system - g_FontTexture = new CIwTexture(); - g_FontTexture->SetModifiable(true); - CIwImage& image = g_FontTexture->GetImage(); - image.SetFormat(CIwImage::ARGB_8888); - image.SetWidth(width); - image.SetHeight(height); - image.SetBuffers(); // allocates and own buffers - image.ReadTexels(pixels); - g_FontTexture->SetMipMapping(false); - g_FontTexture->SetFiltering(false); - g_FontTexture->Upload(); - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)g_FontTexture); - - return true; -} - -void ImGui_Marmalade_InvalidateDeviceObjects() -{ - if (g_ClipboardText) - { - delete[] g_ClipboardText; - g_ClipboardText = NULL; - } - - if (g_FontTexture) - { - ImGui::GetIO().Fonts->SetTexID(0); - delete g_FontTexture; - g_FontTexture = NULL; - } -} - -bool ImGui_Marmalade_Init(bool install_callbacks) -{ - ImGuiIO& io = ImGui::GetIO(); - io.BackendPlatformName = io.BackendRendererName = "imgui_impl_marmalade"; - - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. - io.KeyMap[ImGuiKey_Tab] = s3eKeyTab - io.KeyMap[ImGuiKey_LeftArrow] = s3eKeyLeft; - io.KeyMap[ImGuiKey_RightArrow] = s3eKeyRight; - io.KeyMap[ImGuiKey_UpArrow] = s3eKeyUp; - io.KeyMap[ImGuiKey_DownArrow] = s3eKeyDown; - io.KeyMap[ImGuiKey_PageUp] = s3eKeyPageUp; - io.KeyMap[ImGuiKey_PageDown] = s3eKeyPageDown; - io.KeyMap[ImGuiKey_Home] = s3eKeyHome; - io.KeyMap[ImGuiKey_End] = s3eKeyEnd; - io.KeyMap[ImGuiKey_Insert] = s3eKeyInsert; - io.KeyMap[ImGuiKey_Delete] = s3eKeyDelete; - io.KeyMap[ImGuiKey_Backspace] = s3eKeyBackspace; - io.KeyMap[ImGuiKey_Space] = s3eKeySpace; - io.KeyMap[ImGuiKey_Enter] = s3eKeyEnter; - io.KeyMap[ImGuiKey_Escape] = s3eKeyEsc; - io.KeyMap[ImGuiKey_KeyPadEnter] = s3eKeyNumPadEnter; - io.KeyMap[ImGuiKey_A] = s3eKeyA; - io.KeyMap[ImGuiKey_C] = s3eKeyC; - io.KeyMap[ImGuiKey_V] = s3eKeyV; - io.KeyMap[ImGuiKey_X] = s3eKeyX; - io.KeyMap[ImGuiKey_Y] = s3eKeyY; - io.KeyMap[ImGuiKey_Z] = s3eKeyZ; - - io.SetClipboardTextFn = ImGui_Marmalade_SetClipboardText; - io.GetClipboardTextFn = ImGui_Marmalade_GetClipboardText; - - if (install_callbacks) - { - s3ePointerRegister(S3E_POINTER_BUTTON_EVENT, ImGui_Marmalade_PointerButtonEventCallback, 0); - s3eKeyboardRegister(S3E_KEYBOARD_KEY_EVENT, ImGui_Marmalade_KeyCallback, 0); - s3eKeyboardRegister(S3E_KEYBOARD_CHAR_EVENT, ImGui_Marmalade_CharCallback, 0); - } - - return true; -} - -void ImGui_Marmalade_Shutdown() -{ - ImGui_Marmalade_InvalidateDeviceObjects(); -} - -void ImGui_Marmalade_NewFrame() -{ - if (!g_FontTexture) - ImGui_Marmalade_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w = IwGxGetScreenWidth(), h = IwGxGetScreenHeight(); - io.DisplaySize = ImVec2((float)w, (float)h); - // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. - io.DisplayFramebufferScale = g_scale; - - // Setup time step - double current_time = s3eTimerGetUST() / 1000.0f; - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); - g_Time = current_time; - - double mouse_x, mouse_y; - mouse_x = s3ePointerGetX(); - mouse_y = s3ePointerGetY(); - io.MousePos = ImVec2((float)mouse_x / g_scale.x, (float)mouse_y / g_scale.y); // Mouse position (set to -FLT_MAX,-FLT_MAX if no mouse / on another screen, etc.) - - for (int i = 0; i < 3; i++) - { - io.MouseDown[i] = g_MousePressed[i] || s3ePointerGetState((s3ePointerButton)i) != S3E_POINTER_STATE_UP; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - g_MousePressed[i] = false; - } - - // TODO: Hide OS mouse cursor if ImGui is drawing it - // s3ePointerSetInt(S3E_POINTER_HIDE_CURSOR,(io.MouseDrawCursor ? 0 : 1)); - - // Show/hide OSD keyboard - if (io.WantTextInput) - { - // Some text input widget is active? - if (!g_osdKeyboardEnabled) - { - g_osdKeyboardEnabled = true; - s3eKeyboardSetInt(S3E_KEYBOARD_GET_CHAR, 1); // show OSD keyboard - } - } - else - { - // No text input widget is active - if (g_osdKeyboardEnabled) - { - g_osdKeyboardEnabled = false; - s3eKeyboardSetInt(S3E_KEYBOARD_GET_CHAR, 0); // hide OSD keyboard - } - } -} diff --git a/backends/imgui_impl_marmalade.h b/backends/imgui_impl_marmalade.h deleted file mode 100644 index 87aaa478..00000000 --- a/backends/imgui_impl_marmalade.h +++ /dev/null @@ -1,28 +0,0 @@ -// dear imgui: Renderer + Platform Backend for Marmalade + IwGx -// Marmalade code: Copyright (C) 2015 by Giovanni Zito (this file is part of Dear ImGui) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'CIwTexture*' as ImTextureID. Read the FAQ about ImTextureID! - -// 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 - -#pragma once -#include "imgui.h" // IMGUI_IMPL_API - -IMGUI_IMPL_API bool ImGui_Marmalade_Init(bool install_callbacks); -IMGUI_IMPL_API void ImGui_Marmalade_Shutdown(); -IMGUI_IMPL_API void ImGui_Marmalade_NewFrame(); -IMGUI_IMPL_API void ImGui_Marmalade_RenderDrawData(ImDrawData* draw_data); - -// Use if you want to reset your rendering device without losing Dear ImGui state. -IMGUI_IMPL_API void ImGui_Marmalade_InvalidateDeviceObjects(); -IMGUI_IMPL_API bool ImGui_Marmalade_CreateDeviceObjects(); - -// Callbacks (installed by default if you enable 'install_callbacks' during initialization) -// You can also handle inputs yourself and use those as a reference. -IMGUI_IMPL_API int32 ImGui_Marmalade_PointerButtonEventCallback(void* system_data, void* user_data); -IMGUI_IMPL_API int32 ImGui_Marmalade_KeyCallback(void* system_data, void* user_data); -IMGUI_IMPL_API int32 ImGui_Marmalade_CharCallback(void* system_data, void* user_data); diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index f061d70c..5bd2e836 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -12,7 +12,7 @@ your application or engine to easily integrate Dear ImGui.** Each backend is typ e.g. DirectX11 ([imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)), OpenGL/WebGL ([imgui_impl_opengl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp), Vulkan ([imgui_impl_vulkan.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp), etc. - For some high-level frameworks, a single backend usually handle both 'Platform' and 'Renderer' parts.
- e.g. Allegro 5 ([imgui_impl_allegro5.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_allegro5.cpp)), Marmalade ([imgui_impl_marmalade.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_marmalade.cpp)). If you end up creating a custom backend for your engine, you may want to do the same. + e.g. Allegro 5 ([imgui_impl_allegro5.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_allegro5.cpp)). If you end up creating a custom backend for your engine, you may want to do the same. An application usually combines 1 Platform backend + 1 Renderer backend + main Dear ImGui sources. For example, the [example_win32_directx11](https://github.com/ocornut/imgui/tree/master/examples/example_win32_directx11) application combines imgui_impl_win32.cpp + imgui_impl_dx11.cpp. There are 20+ examples in the [examples/](https://github.com/ocornut/imgui/blob/master/examples/) folder. See [EXAMPLES.MD](https://github.com/ocornut/imgui/blob/master/docs/EXAMPLES.md) for details. @@ -82,14 +82,13 @@ List of Renderer Backends: List of high-level Frameworks Backends (combining Platform + Renderer): imgui_impl_allegro5.cpp - imgui_impl_marmalade.cpp Emscripten is also supported. The [example_emscripten_opengl3](https://github.com/ocornut/imgui/tree/master/examples/example_emscripten_opengl3) app uses imgui_impl_sdl.cpp + imgui_impl_opengl3.cpp, but other combos are possible. ### Backends for third-party frameworks, graphics API or other languages -See https://github.com/ocornut/imgui/wiki/Bindings for the full list. +See https://github.com/ocornut/imgui/wiki/Bindings for the full list (e.g. Adventure Game Studio, Cinder, Cocos2d-x, Game Maker Studio2, Godot, LÖVE+LUA, Magnum, Monogame, Ogre, openFrameworks, OpenSceneGraph, SFML, Sokol, Unity, Unreal Engine and many others). ### Recommended Backends diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 25d91ed6..8f45f073 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -40,6 +40,8 @@ Breaking Changes: Please open an issue if you think you really need this function. (#3841) - Backends: OSX: Added NSView* parameter to ImGui_ImplOSX_Init(). (#4759) [@stuartcarnie] Updated Apple+Metal and Apple+GL example applications accordingly. +- Backends: Marmalade: Removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. (#368, #375) + Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings Other Changes: diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 63b64d2e..8321aa1e 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -141,10 +141,6 @@ GLUT (e.g., FreeGLUT on Linux/Windows, GLUT framework on OSX) + OpenGL2 example. = main.cpp + imgui_impl_glut.cpp + imgui_impl_opengl2.cpp
Note that GLUT/FreeGLUT is largely obsolete software, prefer using GLFW or SDL. -[example_marmalade/](https://github.com/ocornut/imgui/blob/master/examples/example_marmalade/)
-Marmalade example using IwGx.
-= main.cpp + imgui_impl_marmalade.cpp - [example_null/](https://github.com/ocornut/imgui/blob/master/examples/example_null/)
Null example, compile and link imgui, create context, run headless with no inputs and no graphics output.
= main.cpp
diff --git a/docs/README.md b/docs/README.md index 58fcdb20..3190f7d0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -118,7 +118,7 @@ Integrating Dear ImGui within your custom engine is a matter of 1) wiring mouse/ Officially maintained backends/bindings (in repository): - Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, SDL_Renderer, Vulkan, WebGPU. - Platforms: GLFW, SDL2, Win32, Glut, OSX, Android. -- Frameworks: Emscripten, Allegro5, Marmalade. +- Frameworks: Allegro5, Emscripten. [Third-party backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) wiki page: - Languages: C, C# and: Beef, ChaiScript, Crystal, D, Go, Haskell, Haxe/hxcpp, Java, JavaScript, Julia, Kotlin, Lobster, Lua, Odin, Pascal, PureBasic, Python, Ruby, Rust, Swift... diff --git a/examples/example_marmalade/data/app.icf b/examples/example_marmalade/data/app.icf deleted file mode 100644 index fcd6585a..00000000 --- a/examples/example_marmalade/data/app.icf +++ /dev/null @@ -1,32 +0,0 @@ -# This file is for configuration settings for your -# application. -# -# The syntax is similar to windows .ini files ie -# -# [GroupName] -# Setting = Value -# -# Which can be read by your application using -# e.g s3eConfigGetString("GroupName", "Setting", string) -# -# All settings must be documented in .config.txt files. -# New settings specific to this application should be -# documented in app.config.txt -# -# Some conditional operations are also permitted, see the -# S3E documentation for details. - -[S3E] -MemSize=6000000 -MemSizeDebug=6000000 -DispFixRot=FixedLandscape - -# emulate iphone 5 resolution, change these settings to emulate other display resolution -WinWidth=1136 -WinHeight=640 - -[GX] -DataCacheSize=131070 - -[Util] -#MemoryBreakpoint=1282 diff --git a/examples/example_marmalade/main.cpp b/examples/example_marmalade/main.cpp deleted file mode 100644 index e97cb534..00000000 --- a/examples/example_marmalade/main.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// Dear ImGui: standalone example application for Marmalade -// 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 - -// Copyright (C) 2015 by Giovanni Zito -// This file is part of Dear ImGui - -#include "imgui.h" -#include "imgui_impl_marmalade.h" -#include - -#include -#include -#include - -int main(int, char**) -{ - IwGxInit(); - - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsClassic(); - - // Setup Platform/Renderer backends - ImGui_Marmalade_Init(true); - - // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Read 'docs/FONTS.md' for more instructions and details. - // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); - //IM_ASSERT(font != NULL); - - // Our state - bool show_demo_window = true; - bool show_another_window = false; - ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - - // Main loop - while (true) - { - if (s3eDeviceCheckQuitRequest()) - break; - - // Poll and handle inputs - // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. - // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. - // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. - // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - s3eKeyboardUpdate(); - s3ePointerUpdate(); - - // Start the Dear ImGui frame - ImGui_Marmalade_NewFrame(); - ImGui::NewFrame(); - - // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). - if (show_demo_window) - ImGui::ShowDemoWindow(&show_demo_window); - - // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. - { - static float f = 0.0f; - static int counter = 0; - - ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. - - ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) - ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state - ImGui::Checkbox("Another Window", &show_another_window); - - ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f - ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color - - if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) - counter++; - ImGui::SameLine(); - ImGui::Text("counter = %d", counter); - - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::End(); - } - - // 3. Show another simple window. - if (show_another_window) - { - ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) - ImGui::Text("Hello from another window!"); - if (ImGui::Button("Close Me")) - show_another_window = false; - ImGui::End(); - } - - // Rendering - ImGui::Render(); - IwGxSetColClear(clear_color.x * 255, clear_color.y * 255, clear_color.z * 255, clear_color.w * 255); - IwGxClear(); - ImGui_Marmalade_RenderDrawData(ImGui::GetDrawData()); - IwGxSwapBuffers(); - - s3eDeviceYield(0); - } - - // Cleanup - ImGui_Marmalade_Shutdown(); - ImGui::DestroyContext(); - IwGxTerminate(); - - return 0; -} diff --git a/examples/example_marmalade/marmalade_example.mkb b/examples/example_marmalade/marmalade_example.mkb deleted file mode 100644 index 4e765f16..00000000 --- a/examples/example_marmalade/marmalade_example.mkb +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env mkb - -# ImGui - standalone example application for Marmalade -# Copyright (C) 2015 by Giovanni Zito -# This file is part of ImGui -# https://github.com/ocornut/imgui - -define IMGUI_DISABLE_INCLUDE_IMCONFIG_H -define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS -define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS -define _snprintf=snprintf - -options -{ - optimise-speed=1 -} - -includepaths -{ - ../.. - ../../backends -} - -subprojects -{ - iwgx -} - -files -{ - (.) - ["imgui"] - ../../imgui.cpp - ../../imgui_demo.cpp - ../../imgui_draw.cpp - ../../imgui_tables.cpp - ../../imgui_widgets.cpp - ../../imconfig.h - ../../imgui.h - ../../imgui_internal.h - - ["imgui","Marmalade backend"] - ../../backends/imgui_impl_marmalade.h - ../../backends/imgui_impl_marmalade.cpp - main.cpp - -} diff --git a/imgui.cpp b/imgui.cpp index 77ea282d..79c9fe64 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -380,6 +380,7 @@ 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. + - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): From 0636f9adacca0ca92aa6c9646e4515ad2c1ff0ae Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Dec 2021 19:45:58 +0100 Subject: [PATCH 212/332] InputText: fixed Shift+Delete from not cutting into clipboard. (#4818, #1541) --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8f45f073..f02157bb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -79,6 +79,7 @@ Other Changes: - InputText: made double-click select word, triple-line select line. Word delimitation logic differs slightly from the one used by CTRL+arrows. (#2244) - InputText: fixed ReadOnly flag preventing callbacks from receiving the text buffer. (#4762) [@actondev] +- InputText: fixed Shift+Delete from not cutting into clipboard. (#4818, #1541) [@corporateshark] - InputTextMultiline: fixed incorrect padding when FrameBorder > 0. (#3781, #4794) - InputTextMultiline: fixed vertical tracking with large values of FramePadding.y. (#3781, #4794) - Separator: fixed cover all columns while called inside a table. (#4787) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cfdbb96e..0de14bf8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4317,7 +4317,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) { if (!state->HasSelection()) From 4bad852a785d6361ab0744c28f3ca3dbb847a8fc Mon Sep 17 00:00:00 2001 From: Sean Ridenour Date: Mon, 20 Dec 2021 21:27:48 -0700 Subject: [PATCH 213/332] Backends: SDL_Renderer: Handle change to SDL_RenderGeometryRaw() function signature in SDL 2.0.19 (#4819) --- backends/imgui_impl_sdlrenderer.cpp | 7 ++++++- docs/CHANGELOG.txt | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index 1aab5b48..ca4c432f 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -18,6 +18,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG +// 2021-12-21: Update SDL_RenderGeometryRaw() format to work with SDL 2.0.19. // 2021-12-03: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2021-10-06: Backup and restore modified ClipRect/Viewport. // 2021-09-21: Initial version. @@ -174,7 +175,11 @@ void ImGui_ImplSDLRenderer_RenderDrawData(ImDrawData* draw_data) const float* xy = (const float*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, pos)); const float* uv = (const float*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, uv)); - const int* color = (const int*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, col)); +#if SDL_VERSION_ATLEAST(2,0,19) + const SDL_Color* color = (const SDL_Color*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, col)); // SDL 2.0.19+ +#else + const int* color = (const int*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, col)); // SDL 2.0.17 and 2.0.18 +#endif // Bind texture, Draw SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f02157bb..9caacb13 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -117,6 +117,7 @@ Other Changes: - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] +- Backends: SDL_Renderer: Fix for SDL 2.0.19+ RenderGeometryRaw() API signature change. (#4819) [@sridenour] - Backends: OSX: Generally fix keyboard support. Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space). Don't set mouse cursor shape unconditionally. Handle two fingers scroll cancel event. (#4759, #4253, #1873) [@stuartcarnie] From 612b787b0d594d878117fe05732eee8e01e45cff Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Dec 2021 15:05:10 +0100 Subject: [PATCH 214/332] Menus: fixed top-level menu from not consistently using style.PopupRounding. (#4788) + Stack tool default size. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 +++- imgui_widgets.cpp | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9caacb13..b9b7ad7d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -74,6 +74,7 @@ Other Changes: - Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) [@xndcn] - Menus: fixed menu inside a popup/modal not inhibiting hovering of items in the popup/modal. (#3496, #4797) - Menus: fixed sub-menu items inside a popups from closing the popup. +- Menus: fixed top-level menu from not consistently using style.PopupRounding. (#4788) - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs diff --git a/imgui.cpp b/imgui.cpp index 79c9fe64..9be374c3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12530,6 +12530,9 @@ void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* dat // Stack Tool: Display UI void ImGui::ShowStackToolWindow(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) { End(); @@ -12537,7 +12540,6 @@ void ImGui::ShowStackToolWindow(bool* p_open) } // Display hovered/active status - ImGuiContext& g = *GImGui; const ImGuiID hovered_id = g.HoveredIdPreviousFrame; const ImGuiID active_id = g.ActiveId; #ifdef IMGUI_ENABLE_TEST_ENGINE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0de14bf8..021c31b7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7050,7 +7050,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) if (menu_is_open) { SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + PopStyleVar(); } else { From 512c54bbc062c41c74f8a8bd8ff1fd6bebd1e6d0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 22 Dec 2021 15:12:57 +0100 Subject: [PATCH 215/332] Version 1.86 --- docs/CHANGELOG.txt | 14 ++++++-------- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 15 insertions(+), 17 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b9b7ad7d..c8ad7bed 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -31,7 +31,7 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.86 WIP (In Progress) + VERSION 1.86 (Released 2021-12-22) ----------------------------------------------------------------------- Breaking Changes: @@ -39,11 +39,9 @@ Breaking Changes: - Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. (#3841) - Backends: OSX: Added NSView* parameter to ImGui_ImplOSX_Init(). (#4759) [@stuartcarnie] - Updated Apple+Metal and Apple+GL example applications accordingly. -- Backends: Marmalade: Removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. (#368, #375) +- Backends: Marmalade: Removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example app. (#368, #375) Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings - Other Changes: - Added an assertion for the common user mistake of using "" as an identifier at the root level of a window @@ -53,12 +51,12 @@ Other Changes: - Added GetMouseClickedCount() function, returning the number of successive clicks. (#3229) [@kudaba] (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, but it allows testing for triple clicks and more). -- Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) +- Modals: fixed issue hovering popups inside a child windows inside a modal. (#4676, #4527) - Modals, Popups, Windows: changes how appearing windows are interrupting popups and modals. (#4317) [@rokups] - appearing windows created from within the begin stack of a popup/modal will no longer close it. - appearing windows created not within the begin stack of a modal will no longer close the modal, and automatically appear behind it. -- Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) +- Fixed IsWindowFocused()/IsWindowHovered() issues with child windows inside popups. (#4676) - Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of using the _NavEnableKeyboard configuration flag. This is part of an effort to generalize the use of keyboard inputs. (#4023, #787). Note that while this is active you can also moving windows (with arrow) and resize (shift+arrows). @@ -98,8 +96,8 @@ Other Changes: - Clipper: fixed invalid state when number of frozen table row is smaller than ItemCount. - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) -- Fonts: fixed infinite loop in ImFontGlyphRangesBuilder::AddRanges() when passing UINT16_MAX without - the IMGUI_USE_WCHAR32 compile-time option. (#4802) [@SlavicPotato] +- Fonts: fixed infinite loop in ImFontGlyphRangesBuilder::AddRanges() when passing UINT16_MAX or UINT32_MAX + without the IMGUI_USE_WCHAR32 compile-time option. (#4802) [@SlavicPotato] - Metrics: Added a node showing windows in submission order and showing the Begin() stack. - Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] diff --git a/imgui.cpp b/imgui.cpp index 9be374c3..d7653d41 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 WIP +// dear imgui, v1.86 // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index ef32123a..e986e6c0 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.86 WIP +// dear imgui, v1.86 // (headers) // Help: @@ -63,8 +63,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18522 +#define IMGUI_VERSION "1.86" +#define IMGUI_VERSION_NUM 18600 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b517aa2e..9cbc503a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 WIP +// dear imgui, v1.86 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 3095d832..bf1da15b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 WIP +// dear imgui, v1.86 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 17a15930..73d58f6b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.86 WIP +// dear imgui, v1.86 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 1c611809..8a3fe93c 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 WIP +// dear imgui, v1.86 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 021c31b7..f199ad40 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 WIP +// dear imgui, v1.86 // (widgets code) /* From 980deb4c9eaf0702e97be1309d9cd10aaf8467af Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 26 Dec 2021 19:51:10 +0100 Subject: [PATCH 216/332] Add missing include guard to avoid declaring ImFontAtlasGetBuilderForStbTruetype() function. https://github.com/cimgui/cimgui/issues/193 https://github.com/imgui-rs/imgui-rs/pull/582 --- imgui.h | 2 +- imgui_internal.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index e986e6c0..f254f681 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86" -#define IMGUI_VERSION_NUM 18600 +#define IMGUI_VERSION_NUM 18601 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 73d58f6b..9444472b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2815,7 +2815,9 @@ struct ImFontBuilderIO }; // Helper for font builder +#ifdef IMGUI_ENABLE_STB_TRUETYPE IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype(); +#endif 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); From 89a28209e89f69a5eda560d2ef9c13915c95616a Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 26 Dec 2021 20:02:02 +0100 Subject: [PATCH 217/332] Version 1.87 WIP + Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init, added in 1.86 (#4468, #4830) --- backends/imgui_impl_opengl3_loader.h | 2 +- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 9 files changed, 19 insertions(+), 10 deletions(-) diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index 8452301e..e24760df 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -437,7 +437,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); /* gl3w internal state */ union GL3WProcs { - GL3WglProc ptr[53]; + GL3WglProc ptr[54]; struct { PFNGLACTIVETEXTUREPROC ActiveTexture; PFNGLATTACHSHADERPROC AttachShader; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c8ad7bed..82d14664 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -30,6 +30,15 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! + +----------------------------------------------------------------------- + VERSION 1.87 WIP (In Progress) +----------------------------------------------------------------------- + +- Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init, added in 1.86 (#4468, #4830) [@dymk] + It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. + + ----------------------------------------------------------------------- VERSION 1.86 (Released 2021-12-22) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index d7653d41..048c171c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 +// dear imgui, v1.87 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index f254f681..eebe5a21 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.86 +// dear imgui, v1.87 WIP // (headers) // Help: @@ -63,8 +63,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.86" -#define IMGUI_VERSION_NUM 18601 +#define IMGUI_VERSION "1.87 WIP" +#define IMGUI_VERSION_NUM 18602 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9cbc503a..1f9b0bc9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 +// dear imgui, v1.87 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index bf1da15b..c147bfda 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 +// dear imgui, v1.87 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 9444472b..029b0863 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.86 +// dear imgui, v1.87 WIP // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 8a3fe93c..b5c9c2f2 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 +// dear imgui, v1.87 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f199ad40..41d4b6cc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.86 +// dear imgui, v1.87 WIP // (widgets code) /* From dbeea7220f16e4a3c9ca2d000bea688d9eea55fe Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 Dec 2021 12:41:34 +0100 Subject: [PATCH 218/332] Backends: Silence overzealous warnings. (#4834) Add comments about SetDragDropPayload() return value. (#4835) --- backends/imgui_impl_glfw.cpp | 31 ++++++++++++++++++++----------- backends/imgui_impl_opengl3.cpp | 16 +++++++++++++++- imgui.cpp | 1 + imgui.h | 2 +- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 1be2c524..2c5a6cd8 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -44,6 +44,16 @@ #include "imgui.h" #include "imgui_impl_glfw.h" +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#endif + // GLFW #include #ifdef _WIN32 @@ -51,15 +61,10 @@ #define GLFW_EXPOSE_NATIVE_WIN32 #include // for glfwGetWin32Window #endif -#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING -#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED -#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity -#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale -#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface -#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? -#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR +#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? +#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else -#define GLFW_HAS_NEW_CURSORS (0) +#define GLFW_HAS_NEW_CURSORS (0) #endif // GLFW data @@ -398,8 +403,8 @@ static void ImGui_ImplGlfw_UpdateGamepads() return; // Update gamepad inputs - #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } - #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } + #define MAP_BUTTON(NAV_NO, BUTTON_NO) do { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } while (0) + #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } while (0) int axes_count = 0, buttons_count = 0; const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); @@ -440,7 +445,7 @@ void ImGui_ImplGlfw_NewFrame() glfwGetFramebufferSize(bd->Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); if (w > 0 && h > 0) - io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); // Setup time step double current_time = glfwGetTime(); @@ -453,3 +458,7 @@ void ImGui_ImplGlfw_NewFrame() // Update game controllers (if enabled and available) ImGui_ImplGlfw_UpdateGamepads(); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 0ed8348d..32778c1d 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -96,6 +96,16 @@ #include // intptr_t #endif +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#endif + // GL includes #if defined(IMGUI_IMPL_OPENGL_ES2) #include @@ -464,7 +474,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) continue; // Apply scissor/clipping rectangle (Y is inverted in OpenGL) - glScissor((int)clip_min.x, (int)(fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); + glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); // Bind texture, Draw glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); @@ -791,3 +801,7 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; } ImGui_ImplOpenGL3_DestroyFontsTexture(); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/imgui.cpp b/imgui.cpp index 048c171c..15673c22 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10622,6 +10622,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s } payload.DataFrameCount = g.FrameCount; + // Return whether the payload has been accepted return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); } diff --git a/imgui.h b/imgui.h index eebe5a21..afe05799 100644 --- a/imgui.h +++ b/imgui.h @@ -807,7 +807,7 @@ namespace ImGui // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip, see #1725) // - An item can be both drag source and drop target. IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource() - IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. + IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted. IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! 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. From 41e39ea6e1480fb6f3afacec093d58c94250050b Mon Sep 17 00:00:00 2001 From: luigifcruz Date: Thu, 23 Dec 2021 17:04:35 -0300 Subject: [PATCH 219/332] Backends: Metal: Add Metal C++ bindings support. (#4824, #4746) --- backends/imgui_impl_metal.h | 38 +++++++++++++++++++++++++++++++++++ backends/imgui_impl_metal.mm | 39 +++++++++++++++++++++++++++++++++++- docs/CHANGELOG.txt | 4 +++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_metal.h b/backends/imgui_impl_metal.h index c0c60181..469516b4 100644 --- a/backends/imgui_impl_metal.h +++ b/backends/imgui_impl_metal.h @@ -12,6 +12,12 @@ #include "imgui.h" // IMGUI_IMPL_API +//----------------------------------------------------------------------------- +// ObjC API +//----------------------------------------------------------------------------- + +#ifdef __OBJC__ + @class MTLRenderPassDescriptor; @protocol MTLDevice, MTLCommandBuffer, MTLRenderCommandEncoder; @@ -27,3 +33,35 @@ IMGUI_IMPL_API bool ImGui_ImplMetal_CreateFontsTexture(id device); IMGUI_IMPL_API void ImGui_ImplMetal_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(id device); IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); + +#endif + +//----------------------------------------------------------------------------- +// C++ API +//----------------------------------------------------------------------------- + +// Enable Metal C++ binding support with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file +// More info about using Metal from C++: https://developer.apple.com/metal/cpp/ + +#ifdef IMGUI_IMPL_METAL_CPP + +#include + +#ifndef __OBJC__ + +IMGUI_IMPL_API bool ImGui_ImplMetal_Init(MTL::Device* device); +IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTL::RenderPassDescriptor* renderPassDescriptor); +IMGUI_IMPL_API void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, + MTL::CommandBuffer* commandBuffer, + MTL::RenderCommandEncoder* commandEncoder); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplMetal_CreateFontsTexture(MTL::Device* device); +IMGUI_IMPL_API void ImGui_ImplMetal_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device); +IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); + +#endif + +#endif diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 26ddc922..1b156091 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -12,6 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-30: Metal: Added Metal C++ support. Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. // 2021-08-24: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted. (#4464) // 2021-05-19: Metal: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: Metal: Change blending equation to preserve alpha in output buffer. @@ -77,7 +78,43 @@ static MetalContext *g_sharedMetalContext = nil; -#pragma mark - ImGui API implementation +#ifdef IMGUI_IMPL_METAL_CPP + +#pragma mark - Dear ImGui Metal C++ Backend API + +bool ImGui_ImplMetal_Init(MTL::Device* device) +{ + return ImGui_ImplMetal_Init((id)(device)); +} + +void ImGui_ImplMetal_NewFrame(MTL::RenderPassDescriptor* renderPassDescriptor) +{ + ImGui_ImplMetal_NewFrame((MTLRenderPassDescriptor*)(renderPassDescriptor)); +} + +void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, + MTL::CommandBuffer* commandBuffer, + MTL::RenderCommandEncoder* commandEncoder) +{ + ImGui_ImplMetal_RenderDrawData(draw_data, + (id)(commandBuffer), + (id)(commandEncoder)); + +} + +bool ImGui_ImplMetal_CreateFontsTexture(MTL::Device* device) +{ + return ImGui_ImplMetal_CreateFontsTexture((id)(device)); +} + +bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device) +{ + return ImGui_ImplMetal_CreateDeviceObjects((id)(device)); +} + +#endif // #ifdef IMGUI_IMPL_METAL_CPP + +#pragma mark - Dear ImGui Metal Backend API bool ImGui_ImplMetal_Init(id device) { diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 82d14664..9767f0ba 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,8 +35,10 @@ HOW TO UPDATE? VERSION 1.87 WIP (In Progress) ----------------------------------------------------------------------- -- Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init, added in 1.86 (#4468, #4830) [@dymk] +- Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. +- Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] + Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. ----------------------------------------------------------------------- From 9c8f288d1a9038e7f62ccb76328bb8f328c0b89d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 30 Dec 2021 21:46:09 +0100 Subject: [PATCH 220/332] Viewports: Fixed a CTRL+TAB crash with viewports enabled (#4023, #787) (amend 1dc3af3, 23ef6c1, 657073a) + Expose FindHoveredViewportFromPlatformWindowStack() in imgui_internal.h --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 4 ++-- imgui_internal.h | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 93922230..fb5df301 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -108,6 +108,11 @@ Other changes: - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. +Docking+Viewports Branch: + +- Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in + its own viewport (regression from 1.86). (#4023, #787) + ----------------------------------------------------------------------- VERSION 1.86 (Released 2021-12-22) diff --git a/imgui.cpp b/imgui.cpp index fee88ba9..5c415aec 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4700,7 +4700,7 @@ static void ImGui::RenderDimmedBackgrounds() { // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); - if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport; viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL; @@ -12143,7 +12143,7 @@ void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) // If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. // A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. // B) It requires Platform_GetWindowFocus to be implemented by backend. -static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos) +ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos) { ImGuiContext& g = *GImGui; ImGuiViewportP* best_candidate = NULL; diff --git a/imgui_internal.h b/imgui_internal.h index 90e62713..5215d8b3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2694,7 +2694,8 @@ namespace ImGui IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); - IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); + IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); + IMGUI_API ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos); // Settings IMGUI_API void MarkIniSettingsDirty(); From 83d22f4e480c6f71ffc1514c2453feed0fce2733 Mon Sep 17 00:00:00 2001 From: Jonathan Hoffstadt Date: Thu, 30 Dec 2021 14:37:31 -0600 Subject: [PATCH 221/332] Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback().(#4843, #4844) --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 4 +--- imgui_tables.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9767f0ba..11c4e555 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,8 @@ HOW TO UPDATE? VERSION 1.87 WIP (In Progress) ----------------------------------------------------------------------- +- Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in + last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c147bfda..423f8cd2 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1733,9 +1733,7 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list) for (int i = 1; i < _Count; i++) { ImDrawChannel& ch = _Channels[i]; - - // Equivalent of PopUnusedDrawCmd() for this channel's cmdbuffer and except we don't need to test for UserCallback. - if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0) + if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd() ch._CmdBuffer.pop_back(); if (ch._CmdBuffer.Size > 0 && last_cmd != NULL) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index b5c9c2f2..88717bd5 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2353,7 +2353,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) // Don't attempt to merge if there are multiple draw calls within the column ImDrawChannel* src_channel = &splitter->_Channels[channel_no]; - if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0) + if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback != NULL) // Equivalent of PopUnusedDrawCmd() src_channel->_CmdBuffer.pop_back(); if (src_channel->_CmdBuffer.Size != 1) continue; From 92d7869fc7c491da602c0a808c4bbf70d26b80d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Jan 2022 16:27:51 +0100 Subject: [PATCH 222/332] Happy new year! --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 780533dc..4023e0ca 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2021 Omar Cornut +Copyright (c) 2014-2022 Omar Cornut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2402958aec9ef3303293540c15d4bd133ae3a007 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Jan 2022 16:37:33 +0100 Subject: [PATCH 223/332] Commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex --- docs/CHANGELOG.txt | 11 +++++++++++ docs/README.md | 2 +- imgui.cpp | 6 ++++++ imgui.h | 46 +++++++++++++++++++--------------------------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 11c4e555..ffb21621 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,17 @@ HOW TO UPDATE? VERSION 1.87 WIP (In Progress) ----------------------------------------------------------------------- +Breaking Changes: + +- Commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) + - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() + - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x + - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); + - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect + - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex + +Other Changes: + - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] diff --git a/docs/README.md b/docs/README.md index 3190f7d0..6569f454 100644 --- a/docs/README.md +++ b/docs/README.md @@ -132,7 +132,7 @@ Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas. ### Upcoming Changes -Some of the goals for 2021 are: +Some of the goals for 2022 are: - Work on Docking (see [#2109](https://github.com/ocornut/imgui/issues/2109), in public [docking](https://github.com/ocornut/imgui/tree/docking) branch) - Work on Multi-Viewport / Multiple OS windows. (see [#1542](https://github.com/ocornut/imgui/issues/1542), in public [docking](https://github.com/ocornut/imgui/tree/docking) branch looking for feedback) - Work on gamepad/keyboard controls. (see [#787](https://github.com/ocornut/imgui/issues/787)) diff --git a/imgui.cpp b/imgui.cpp index 15673c22..abecbcc4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -380,6 +380,12 @@ 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. + - 2022/03/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) + - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() + - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x + - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); + - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect + - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. diff --git a/imgui.h b/imgui.h index afe05799..9999c2f6 100644 --- a/imgui.h +++ b/imgui.h @@ -980,9 +980,7 @@ enum ImGuiWindowFlags_ 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] - //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) + //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // [Obsolete] --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) }; // Flags for ImGui::InputText() @@ -1599,9 +1597,7 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] -#endif + // ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] }; // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. @@ -2498,8 +2494,8 @@ 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 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - 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); } - inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } + 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) #endif // [Internal helpers] @@ -2728,10 +2724,9 @@ struct ImFontAtlas int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ + // [Obsolete] + //typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ -#endif }; // Font runtime data and rendering @@ -2858,24 +2853,21 @@ namespace ImGui static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } // OBSOLETED in 1.77 (from June 2020) static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } - // OBSOLETED in 1.72 (from April 2019) - static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } - // OBSOLETED in 1.71 (from June 2019) - static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } - // OBSOLETED in 1.70 (from May 2019) - static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) - //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) - //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) - //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) - //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) - //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) - //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019) + //static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019) + //static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // OBSOLETED in 1.70 (from May 2019) + //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) + //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) + //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) + //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) + //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) + //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) } // OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() From b0a6cd6305fe145bb9241f2f8385f4f4eb28ffde Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Jan 2022 21:16:51 +0100 Subject: [PATCH 224/332] Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, (#4857) (2nd amend) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ffb21621..95b5a334 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Breaking Changes: Other Changes: +- Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, + which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] diff --git a/imgui.cpp b/imgui.cpp index abecbcc4..b2afc3c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4475,6 +4475,7 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 draw_list->CmdBuffer.pop_back(); draw_list->CmdBuffer.push_front(cmd); draw_list->PopClipRect(); + draw_list->_PopUnusedDrawCmd(); // Since are past the calls to AddDrawListToDrawData() we don't have a _PopUnusedDrawCmd() running on commands. } } @@ -4528,6 +4529,7 @@ static void ImGui::RenderDimmedBackgrounds() window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); window->DrawList->PopClipRect(); + window->DrawList->_PopUnusedDrawCmd(); // Since are past the calls to AddDrawListToDrawData() we don't have a _PopUnusedDrawCmd() running on commands. } } From efa50f72a70ffea211ff27bbf0f973afc46767ff Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Jan 2022 22:14:19 +0100 Subject: [PATCH 225/332] Backends: Metal: Ignore ImDrawCmd where ElemCount == 0. (#4857) --- backends/imgui_impl_metal.mm | 3 +++ docs/CHANGELOG.txt | 2 ++ 2 files changed, 5 insertions(+) diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 1b156091..358103f1 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -12,6 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-03: Metal: Ignore ImDrawCmd where ElemCount == 0 (very rare but can technically be manufactured by user code). // 2021-12-30: Metal: Added Metal C++ support. Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. // 2021-08-24: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted. (#4464) // 2021-05-19: Metal: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) @@ -552,6 +553,8 @@ void ImGui_ImplMetal_DestroyDeviceObjects() if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; + if (pcmd->ElemCount == 0) // drawIndexedPrimitives() validation doesn't accept this + continue; // Apply scissor/clipping rectangle MTLScissorRect scissorRect = diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 95b5a334..38b71a1e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,6 +54,8 @@ Other Changes: It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. +- Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but + can theorically be created by user code manipulating a ImDrawList. (#4857) ----------------------------------------------------------------------- From c5a3cae83a1f81e07141fbe07bbf34e900271ddd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 4 Jan 2022 12:44:10 +0100 Subject: [PATCH 226/332] Reordered some IO fields. Misc comments. Removed beta marker from tables comments. --- imgui.h | 128 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/imgui.h b/imgui.h index 9999c2f6..1d54b44b 100644 --- a/imgui.h +++ b/imgui.h @@ -524,7 +524,8 @@ namespace ImGui // 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. - // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', + // the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). @@ -711,7 +712,6 @@ namespace ImGui IMGUI_API bool IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0); // return true if the popup is open. // Tables - // [BETA API] API may evolve slightly! If you use this, please update to the next version when it comes out! // - Full-featured replacement for old Columns API. // - See Demo->Tables for demo code. // - See top of imgui_tables.cpp for general commentary. @@ -1115,8 +1115,7 @@ enum ImGuiTabItemFlags_ }; // Flags for ImGui::BeginTable() -// [BETA API] API may evolve slightly! If you use this, please update to the next version when it comes out! -// - Important! Sizing policies have complex and subtle side effects, more so than you would expect. +// - 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. @@ -1124,8 +1123,8 @@ enum ImGuiTabItemFlags_ // - 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 will generally obtain their requested width (unless the table cannot fit them all). -// - Stretch Columns will share the remaining width. +// - 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). @@ -1239,7 +1238,7 @@ enum ImGuiTableColumnFlags_ enum ImGuiTableRowFlags_ { ImGuiTableRowFlags_None = 0, - ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted different for auto column width) + 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() @@ -1383,7 +1382,7 @@ enum ImGuiKeyModFlags_ ImGuiKeyModFlags_Ctrl = 1 << 0, ImGuiKeyModFlags_Shift = 1 << 1, ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 + ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key }; // Gamepad/Keyboard navigation @@ -1393,29 +1392,29 @@ enum ImGuiKeyModFlags_ enum ImGuiNavInput_ { // Gamepad Mapping - ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) - ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) - ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) - ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) - ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) + ImGuiNavInput_Activate, // Activate / Open / Toggle / Tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) + ImGuiNavInput_Cancel, // Cancel / Close / Exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) + ImGuiNavInput_Input, // Text input / On-Screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) + ImGuiNavInput_Menu, // Tap: Toggle menu / Hold: Focus, Move, Resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) + ImGuiNavInput_DpadLeft, // Move / Tweak / Resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) ImGuiNavInput_DpadRight, // ImGuiNavInput_DpadUp, // ImGuiNavInput_DpadDown, // - ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down + ImGuiNavInput_LStickLeft, // Scroll / Move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down ImGuiNavInput_LStickRight, // ImGuiNavInput_LStickUp, // ImGuiNavInput_LStickDown, // - ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_TweakFast, // faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) + ImGuiNavInput_FocusPrev, // Focus Next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) + ImGuiNavInput_FocusNext, // Focus Prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) + ImGuiNavInput_TweakSlow, // Slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) + ImGuiNavInput_TweakFast, // Faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. - ImGuiNavInput_KeyLeft_, // move left // = Arrow keys - ImGuiNavInput_KeyRight_, // move right - ImGuiNavInput_KeyUp_, // move up - ImGuiNavInput_KeyDown_, // move down + ImGuiNavInput_KeyLeft_, // Move left // = Arrow keys + ImGuiNavInput_KeyRight_, // Move right + ImGuiNavInput_KeyUp_, // Move up + ImGuiNavInput_KeyDown_, // Move down ImGuiNavInput_COUNT, ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyLeft_ }; @@ -1880,18 +1879,18 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - bool KeyCtrl; // Keyboard modifier pressed: Control - bool KeyShift; // Keyboard modifier pressed: Shift - bool KeyAlt; // Keyboard modifier pressed: Alt - bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows + bool KeyCtrl; // Keyboard modifier down: Control + bool KeyShift; // Keyboard modifier down: Shift + bool KeyAlt; // Keyboard modifier down: Alt + bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows bool KeysDown[512]; // 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). float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). - // Functions + // Input Functions + IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string - IMGUI_API void AddFocusEvent(bool focused); // Notifies Dear ImGui when hosting platform windows lose or gain input focus IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually IMGUI_API void ClearInputKeys(); // [Internal] Release all keys @@ -1901,50 +1900,51 @@ struct ImGuiIO // generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!) //------------------------------------------------------------------ - bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). - bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). - bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. - bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! - bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. - int MetricsRenderVertices; // Vertices output during last call to Render() - 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. + bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). + bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). + bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). + bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. + bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! + bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. + bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). + float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. + int MetricsRenderVertices; // Vertices output during last call to Render() + 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. //------------------------------------------------------------------ // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ - bool WantCaptureMouseUnlessPopupClose;// Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. - ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() - ImGuiKeyModFlags KeyModsPrev; // Previous key mods - ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) - ImVec2 MouseClickedPos[5]; // Position at time of clicking - double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) - ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down - ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. - bool MouseReleased[5]; // Mouse button went from Down to !Down - 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. - 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 - ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point - float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point - float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) - float KeysDownDurationPrev[512]; // Previous duration the key has been down + ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() + ImGuiKeyModFlags KeyModsPrev; // Key mods flags (from previous frame) + float KeysDownDuration[512]; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held) + float KeysDownDurationPrev[512]; // Duration the key has been down (from previous frame) + + 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) + ImVec2 MouseClickedPos[5]; // Position at time of clicking + double MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) + ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down + ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. + bool MouseReleased[5]; // Mouse button went from Down to !Down + 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. + 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 + ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; - float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. bool AppFocusLost; - ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 - ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. + ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() + ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); }; From 206b9ead8f60e1900f08995f35ed50a4e2642cec Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 4 Jan 2022 12:49:34 +0100 Subject: [PATCH 227/332] IO: removed unused MouseDragMaxDistanceAbs(). Using a shortcut variable in UpdateMouseInputs(). --- imgui.cpp | 60 +++++++++++++++++++++++++++---------------------------- imgui.h | 3 +-- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b2afc3c2..6bb4fe40 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3747,62 +3747,60 @@ static bool IsWindowActiveAndVisible(ImGuiWindow* window) static void ImGui::UpdateMouseInputs() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) - if (IsMousePosValid(&g.IO.MousePos)) - g.IO.MousePos = g.MouseLastValidPos = ImFloor(g.IO.MousePos); + if (IsMousePosValid(&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(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) - g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; + if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) + io.MouseDelta = io.MousePos - io.MousePosPrev; else - g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + io.MouseDelta = ImVec2(0.0f, 0.0f); // 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 (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) + if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; - g.IO.MousePosPrev = g.IO.MousePos; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + io.MousePosPrev = io.MousePos; + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { - g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; - g.IO.MouseClickedCount[i] = 0; // Will be filled below - g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; - g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; - g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - if (g.IO.MouseClicked[i]) + io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; + io.MouseClickedCount[i] = 0; // Will be filled below + io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; + io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; + io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; + if (io.MouseClicked[i]) { bool is_repeated_click = false; - if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) + if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) { - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) + ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) is_repeated_click = true; } if (is_repeated_click) - g.IO.MouseClickedLastCount[i]++; + io.MouseClickedLastCount[i]++; else - g.IO.MouseClickedLastCount[i] = 1; - g.IO.MouseClickedTime[i] = g.Time; - g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseClickedCount[i] = g.IO.MouseClickedLastCount[i]; - g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; + io.MouseClickedLastCount[i] = 1; + io.MouseClickedTime[i] = g.Time; + io.MouseClickedPos[i] = io.MousePos; + io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; + io.MouseDragMaxDistanceSqr[i] = 0.0f; } - else if (g.IO.MouseDown[i]) + else if (io.MouseDown[i]) { // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); - g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); - g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); + float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; + io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); } // We provide io.MouseDoubleClicked[] as a legacy service - g.IO.MouseDoubleClicked[i] = (g.IO.MouseClickedCount[i] == 2); + io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation - if (g.IO.MouseClicked[i]) + if (io.MouseClicked[i]) g.NavDisableMouseHover = false; } } diff --git a/imgui.h b/imgui.h index 1d54b44b..d2edfa36 100644 --- a/imgui.h +++ b/imgui.h @@ -1937,8 +1937,7 @@ struct ImGuiIO bool MouseDownOwnedUnlessPopupClose[5]; //Track if button was clicked inside a dear imgui window. 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 - ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point - float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. From 28eabcb0994be436d5ab3bf62761f35a2a4eea1a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 4 Jan 2022 14:53:30 +0100 Subject: [PATCH 228/332] Internals: refactor: mouse input functions into their own section. --- imgui.cpp | 474 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 242 insertions(+), 232 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6bb4fe40..32fd8c76 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -70,6 +70,7 @@ CODE // [SECTION] STYLING // [SECTION] RENDER HELPERS // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +// [SECTION] INPUTS // [SECTION] ERROR CHECKING // [SECTION] LAYOUT // [SECTION] SCROLLING @@ -4767,238 +4768,6 @@ static void FindHoveredWindow() g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; } -// Test if mouse cursor is hovering given rectangle -// NB- Rectangle is clipped by our current clip setting -// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) -{ - ImGuiContext& g = *GImGui; - - // Clip - ImRect rect_clipped(r_min, r_max); - 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)) - return false; - return true; -} - -int ImGui::GetKeyIndex(ImGuiKey imgui_key) -{ - IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); - ImGuiContext& g = *GImGui; - return g.IO.KeyMap[imgui_key]; -} - -// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! -// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! -bool ImGui::IsKeyDown(int user_key_index) -{ - if (user_key_index < 0) - return false; - ImGuiContext& g = *GImGui; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - return g.IO.KeysDown[user_key_index]; -} - -// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) -// t1 = current time (e.g.: g.Time) -// An event is triggered at: -// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N -int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) -{ - if (t1 == 0.0f) - return 1; - if (t0 >= t1) - return 0; - if (repeat_rate <= 0.0f) - return (t0 < repeat_delay) && (t1 >= repeat_delay); - const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); - const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); - const int count = count_t1 - count_t0; - return count; -} - -int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) -{ - ImGuiContext& g = *GImGui; - if (key_index < 0) - return 0; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[key_index]; - return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); -} - -bool ImGui::IsKeyPressed(int user_key_index, bool repeat) -{ - ImGuiContext& g = *GImGui; - if (user_key_index < 0) - return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[user_key_index]; - if (t == 0.0f) - return true; - if (repeat && t > g.IO.KeyRepeatDelay) - return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; - return false; -} - -bool ImGui::IsKeyReleased(int user_key_index) -{ - ImGuiContext& g = *GImGui; - if (user_key_index < 0) return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; -} - -bool ImGui::IsMouseDown(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDown[button]; -} - -bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - const float t = g.IO.MouseDownDuration[button]; - if (t == 0.0f) - return true; - - if (repeat && t > g.IO.KeyRepeatDelay) - { - // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold. - int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f); - if (amount > 0) - return true; - } - return false; -} - -bool ImGui::IsMouseReleased(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseReleased[button]; -} - -bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseClickedCount[button] == 2; -} - -int ImGui::GetMouseClickedCount(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseClickedCount[button]; -} - -// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. -// [Internal] This doesn't test if the button is pressed -bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; -} - -bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (!g.IO.MouseDown[button]) - return false; - return IsMouseDragPastThreshold(button, lock_threshold); -} - -ImVec2 ImGui::GetMousePos() -{ - ImGuiContext& g = *GImGui; - return g.IO.MousePos; -} - -// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! -ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() -{ - ImGuiContext& g = *GImGui; - if (g.BeginPopupStack.Size > 0) - return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos; - return g.IO.MousePos; -} - -// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. -bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) -{ - // The assert is only to silence a false-positive in XCode Static Analysis. - // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). - IM_ASSERT(GImGui != NULL); - const float MOUSE_INVALID = -256000.0f; - ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; - return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; -} - -bool ImGui::IsAnyMouseDown() -{ - ImGuiContext& g = *GImGui; - for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) - if (g.IO.MouseDown[n]) - return true; - return false; -} - -// Return the delta from the initial clicking position while the mouse button is clicked or was just released. -// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. -// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window. -ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) - if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) - return g.IO.MousePos - g.IO.MouseClickedPos[button]; - return ImVec2(0.0f, 0.0f); -} - -void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr - g.IO.MouseClickedPos[button] = g.IO.MousePos; -} - -ImGuiMouseCursor ImGui::GetMouseCursor() -{ - return GImGui->MouseCursor; -} - -void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) -{ - GImGui->MouseCursor = cursor_type; -} - -void ImGui::CaptureKeyboardFromApp(bool capture) -{ - GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0; -} - -void ImGui::CaptureMouseFromApp(bool capture) -{ - GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0; -} - bool ImGui::IsItemActive() { ImGuiContext& g = *GImGui; @@ -7460,6 +7229,247 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) } +//----------------------------------------------------------------------------- +// [SECTION] INPUTS +//----------------------------------------------------------------------------- + +// Test if mouse cursor is hovering given rectangle +// NB- Rectangle is clipped by our current clip setting +// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) +bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) +{ + ImGuiContext& g = *GImGui; + + // Clip + ImRect rect_clipped(r_min, r_max); + 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)) + return false; + return true; +} + +int ImGui::GetKeyIndex(ImGuiKey imgui_key) +{ + IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); + ImGuiContext& g = *GImGui; + return g.IO.KeyMap[imgui_key]; +} + +// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! +// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! +bool ImGui::IsKeyDown(int user_key_index) +{ + if (user_key_index < 0) + return false; + ImGuiContext& g = *GImGui; + IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + return g.IO.KeysDown[user_key_index]; +} + +// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) +// t1 = current time (e.g.: g.Time) +// An event is triggered at: +// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N +int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) +{ + if (t1 == 0.0f) + return 1; + if (t0 >= t1) + return 0; + if (repeat_rate <= 0.0f) + return (t0 < repeat_delay) && (t1 >= repeat_delay); + const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); + const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); + const int count = count_t1 - count_t0; + return count; +} + +int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) +{ + ImGuiContext& g = *GImGui; + if (key_index < 0) + return 0; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + const float t = g.IO.KeysDownDuration[key_index]; + return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); +} + +bool ImGui::IsKeyPressed(int user_key_index, bool repeat) +{ + ImGuiContext& g = *GImGui; + if (user_key_index < 0) + return false; + IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + const float t = g.IO.KeysDownDuration[user_key_index]; + if (t == 0.0f) + return true; + if (repeat && t > g.IO.KeyRepeatDelay) + return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; + return false; +} + +bool ImGui::IsKeyReleased(int user_key_index) +{ + ImGuiContext& g = *GImGui; + if (user_key_index < 0) return false; + IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; +} + +bool ImGui::IsMouseDown(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDown[button]; +} + +bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + const float t = g.IO.MouseDownDuration[button]; + if (t == 0.0f) + return true; + + if (repeat && t > g.IO.KeyRepeatDelay) + { + // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold. + int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f); + if (amount > 0) + return true; + } + return false; +} + +bool ImGui::IsMouseReleased(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseReleased[button]; +} + +bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button] == 2; +} + +int ImGui::GetMouseClickedCount(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button]; +} + +// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. +// [Internal] This doesn't test if the button is pressed +bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; +} + +bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (!g.IO.MouseDown[button]) + return false; + return IsMouseDragPastThreshold(button, lock_threshold); +} + +ImVec2 ImGui::GetMousePos() +{ + ImGuiContext& g = *GImGui; + return g.IO.MousePos; +} + +// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! +ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() +{ + ImGuiContext& g = *GImGui; + if (g.BeginPopupStack.Size > 0) + return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos; + return g.IO.MousePos; +} + +// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. +bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) +{ + // The assert is only to silence a false-positive in XCode Static Analysis. + // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). + IM_ASSERT(GImGui != NULL); + const float MOUSE_INVALID = -256000.0f; + ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; + return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; +} + +bool ImGui::IsAnyMouseDown() +{ + ImGuiContext& g = *GImGui; + for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) + if (g.IO.MouseDown[n]) + return true; + return false; +} + +// Return the delta from the initial clicking position while the mouse button is clicked or was just released. +// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. +// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window. +ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) + if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; + return ImVec2(0.0f, 0.0f); +} + +void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr + g.IO.MouseClickedPos[button] = g.IO.MousePos; +} + +ImGuiMouseCursor ImGui::GetMouseCursor() +{ + ImGuiContext& g = *GImGui; + return g.MouseCursor; +} + +void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) +{ + ImGuiContext& g = *GImGui; + g.MouseCursor = cursor_type; +} + +void ImGui::CaptureKeyboardFromApp(bool capture) +{ + ImGuiContext& g = *GImGui; + g.WantCaptureKeyboardNextFrame = capture ? 1 : 0; +} + +void ImGui::CaptureMouseFromApp(bool capture) +{ + ImGuiContext& g = *GImGui; + g.WantCaptureMouseNextFrame = capture ? 1 : 0; +} + + //----------------------------------------------------------------------------- // [SECTION] ERROR CHECKING //----------------------------------------------------------------------------- From 04bc0b0bb832233b568746cf03f419de81502846 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jan 2022 12:15:58 +0100 Subject: [PATCH 229/332] Fix IdxBuffer related comments. (#4845, #4863) --- imgui.cpp | 19 ++++++++++++------- imgui.h | 8 ++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 32fd8c76..453e6a72 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -295,6 +295,7 @@ CODE // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. + ImVec2 clip_off = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -309,9 +310,11 @@ CODE } else { - // The texture for the draw call is specified by pcmd->GetTexID(). - // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; // We are using scissoring to clip some objects. All low-level graphics API should support it. // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches @@ -322,14 +325,16 @@ CODE // - In the interest of supporting multi-viewport applications (see 'docking' branch on github), // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) - ImVec2 pos = draw_data->DisplayPos; - MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y)); + MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y); + + // The texture for the draw call is specified by pcmd->GetTexID(). + // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. + MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); // Render 'pcmd->ElemCount/3' indexed triangles. // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices. - MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); + MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset); } - idx_buffer += pcmd->ElemCount; } } } diff --git a/imgui.h b/imgui.h index d2edfa36..552137c3 100644 --- a/imgui.h +++ b/imgui.h @@ -2278,16 +2278,16 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c #define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1) // Typically, 1 command = 1 GPU draw call (unless command is a callback) -// - VtxOffset/IdxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, -// those fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. -// Pre-1.71 backends will typically ignore the VtxOffset/IdxOffset fields. +// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, +// this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices. +// Backends made for <1.71. will typically ignore the VtxOffset fields. // - The ClipRect/TextureId/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for). struct ImDrawCmd { ImVec4 ClipRect; // 4*4 // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates ImTextureID TextureId; // 4-8 // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. unsigned int VtxOffset; // 4 // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices. - unsigned int IdxOffset; // 4 // Start offset in index buffer. Always equal to sum of ElemCount drawn so far. + unsigned int IdxOffset; // 4 // Start offset in index buffer. unsigned int ElemCount; // 4 // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. ImDrawCallback UserCallback; // 4-8 // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. void* UserCallbackData; // 4-8 // The draw callback code can access this. From 3a90dc3893444bc0811c2181f61fe0673b270a05 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jan 2022 13:03:48 +0100 Subject: [PATCH 230/332] Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API. Ref #2589, #2598, #3108, #3113, #3653, #4642 --- backends/imgui_impl_glfw.cpp | 4 +++- backends/imgui_impl_sdl.cpp | 3 ++- backends/imgui_impl_win32.cpp | 3 ++- docs/CHANGELOG.txt | 11 +++++++++ docs/FAQ.md | 4 ++-- imgui.cpp | 44 +++++++++++++++++++++-------------- imgui.h | 25 +++++++++++++++++--- 7 files changed, 68 insertions(+), 26 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 2c5a6cd8..2504ca87 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -246,8 +246,10 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.ClipboardUserData = bd->Window; + + // Set platform dependent data in viewport #if defined(_WIN32) - io.ImeWindowHandle = (void*)glfwGetWin32Window(bd->Window); + ImGui::GetMainViewport()->PlatformHandleRaw = (void*)glfwGetWin32Window(bd->Window); #endif // Create mouse cursors diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index b725e0ac..82561bea 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -230,11 +230,12 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); + // Set platform dependent data in viewport #ifdef _WIN32 SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(window, &info)) - io.ImeWindowHandle = info.info.win.window; + ImGui::GetMainViewport()->PlatformHandleRaw = (void*)info.info.win.window; #else (void)window; #endif diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index ce8f9d55..e1444efc 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -122,7 +122,8 @@ bool ImGui_ImplWin32_Init(void* hwnd) bd->Time = perf_counter; bd->LastMouseCursor = ImGuiMouseCursor_COUNT; - io.ImeWindowHandle = hwnd; + // Set platform dependent data in viewport + ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. io.KeyMap[ImGuiKey_Tab] = VK_TAB; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 38b71a1e..305864e8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,14 @@ Breaking Changes: - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex +- Removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn() for IME support. + Because this field was mostly only ever used by Dear ImGui internally, not by backends nor the vast majority + of user code, this should only affect a very small fraction for users who are already very IME-aware. +- Obsoleted 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. + This removes an incompatibility between 'master' and 'multi-viewports' backends and toward enabling + better support for IME. Updated backends accordingly. Because the old field is set by existing backends, + we are keeping it (marked as obsolete). + Other Changes: @@ -50,6 +58,9 @@ Other Changes: which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] +- Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API, + now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. +- Platform IME: moved io.ImeWindowHandle to GetMainViewport()->PlatformHandleRaw. - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] diff --git a/docs/FAQ.md b/docs/FAQ.md index 009027c1..f6fc96c4 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -607,8 +607,8 @@ Text input: it is up to your application to pass the right character code by cal The applications in examples/ are doing that. Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state. -Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for -the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly. +Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to ImGui::GetMainViewport()->PlatformHandleRaw +in order for the default the default implementation of io.SetPlatformImeDataFn() to set your Microsoft IME position correctly. ##### [Return to Index](#index) diff --git a/imgui.cpp b/imgui.cpp index 453e6a72..349a5654 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -386,6 +386,7 @@ 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. + - 2022/03/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/03/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x @@ -919,7 +920,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti // Platform Dependents default implementation for IO functions static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); +static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data); namespace ImGui { @@ -1136,8 +1137,7 @@ ImGuiIO::ImGuiIO() GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; - ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; - ImeWindowHandle = NULL; + SetPlatformImeDataFn = SetPlatformImeDataFn_DefaultImpl; // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); @@ -4553,9 +4553,11 @@ void ImGui::EndFrame() ErrorCheckEndFrameSanityChecks(); // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) + if (g.IO.SetPlatformImeDataFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) { - g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); + ImGuiPlatformImeData data; + data.InputPos = g.PlatformImePos; + g.IO.SetPlatformImeDataFn(GetMainViewport(), &data); g.PlatformImeLastPos = g.PlatformImePos; } @@ -11500,25 +11502,31 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text) #pragma comment(lib, "imm32") #endif -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data) { // Notify OS Input Method Editor of text input position - ImGuiIO& io = ImGui::GetIO(); - if (HWND hwnd = (HWND)io.ImeWindowHandle) - if (HIMC himc = ::ImmGetContext(hwnd)) - { - COMPOSITIONFORM cf; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y; - cf.dwStyle = CFS_FORCE_POSITION; - ::ImmSetCompositionWindow(himc, &cf); - ::ImmReleaseContext(hwnd, himc); - } + HWND hwnd = (HWND)viewport->PlatformHandleRaw; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (hwnd == 0) + hwnd = (HWND)ImGui::GetIO().ImeWindowHandle; +#endif + if (hwnd == 0) + return; + + if (HIMC himc = ::ImmGetContext(hwnd)) + { + COMPOSITIONFORM cf; + cf.ptCurrentPos.x = (LONG)data->InputPos.x; + cf.ptCurrentPos.y = (LONG)data->InputPos.y; + cf.dwStyle = CFS_FORCE_POSITION; + ::ImmSetCompositionWindow(himc, &cf); + ::ImmReleaseContext(hwnd, himc); + } } #else -static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} +static void SetPlatformImeDataFn_De(int, int) {} #endif diff --git a/imgui.h b/imgui.h index 552137c3..4f7a5333 100644 --- a/imgui.h +++ b/imgui.h @@ -35,6 +35,7 @@ Index of this file: // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) +// [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData) // [SECTION] Obsolete functions and types */ @@ -64,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18602 +#define IMGUI_VERSION_NUM 18603 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -154,6 +155,7 @@ struct ImGuiInputTextCallbackData; // Shared state of InputText() when using cu struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations +struct ImGuiPlatformImeData; // Platform IME data for io.SetPlatformImeDataFn() function. struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage struct ImGuiStyle; // Runtime data for styling/colors @@ -1868,8 +1870,12 @@ 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 (*ImeSetInputScreenPosFn)(int x, int y); - void* ImeWindowHandle; // = NULL // (Windows) Set this to your HWND to get automatic IME cursor positioning. + 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 //------------------------------------------------------------------ // Input - Fill before calling NewFrame() @@ -2809,6 +2815,9 @@ struct ImGuiViewport ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) + // Platform/Backend Dependent Data + void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms) + ImGuiViewport() { memset(this, 0, sizeof(*this)); } // Helpers @@ -2816,6 +2825,16 @@ struct ImGuiViewport ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); } }; +//----------------------------------------------------------------------------- +// [SECTION] Platform Dependent Interfaces +//----------------------------------------------------------------------------- + +// (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function. +struct ImGuiPlatformImeData +{ + ImVec2 InputPos; // Position of the input cursor +}; + //----------------------------------------------------------------------------- // [SECTION] Obsolete functions and types // (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) From 2706c9d66ee69c3a7cdba4a1959af1f7319deb13 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jan 2022 13:15:49 +0100 Subject: [PATCH 231/332] Platform IME: reworked internals to simplify adding new features. --- imgui.cpp | 15 ++++++--------- imgui_internal.h | 7 ++++--- imgui_widgets.cpp | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 349a5654..2e9bc281 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4146,7 +4146,9 @@ void ImGui::NewFrame() g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; - g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default + + // Platform IME data: reset for the frame + g.PlatformImeDataPrev = g.PlatformImeData; // Mouse wheel scrolling, scale UpdateMouseWheel(); @@ -4552,14 +4554,9 @@ void ImGui::EndFrame() ErrorCheckEndFrameSanityChecks(); - // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.IO.SetPlatformImeDataFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) - { - ImGuiPlatformImeData data; - data.InputPos = g.PlatformImePos; - g.IO.SetPlatformImeDataFn(GetMainViewport(), &data); - g.PlatformImeLastPos = g.PlatformImePos; - } + // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) + if (g.IO.SetPlatformImeDataFn && memcmp(&g.PlatformImeData, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) + g.IO.SetPlatformImeDataFn(GetMainViewport(), &g.PlatformImeData); // Hide implicit/fallback "Debug" window if it hasn't been used g.WithinFrameScopeWithImplicitWindow = false; diff --git a/imgui_internal.h b/imgui_internal.h index 029b0863..572a77b4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1705,8 +1705,8 @@ struct ImGuiContext ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once // Platform support - ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor - ImVec2 PlatformImeLastPos; + 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 @@ -1870,7 +1870,8 @@ struct ImGuiContext TooltipOverrideCount = 0; TooltipSlowDelay = 0.50f; - PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); + PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); + PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission PlatformLocaleDecimalPoint = '.'; SettingsLoaded = false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 41d4b6cc..eb464dcc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4760,7 +4760,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) if (!is_readonly) - g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); } } else From 1cbfe93520d73605b92110a51ac1f8636a50cea3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jan 2022 13:27:48 +0100 Subject: [PATCH 232/332] Platform IME: [windows] call ImmSetCandidateWindow() to position candidate window. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 305864e8..4c427303 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -61,6 +61,7 @@ Other Changes: - Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API, now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. - Platform IME: moved io.ImeWindowHandle to GetMainViewport()->PlatformHandleRaw. +- Platform IME: [windows] call ImmSetCandidateWindow() to position candidate window. - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] diff --git a/imgui.cpp b/imgui.cpp index 2e9bc281..ebde53df 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11512,11 +11512,16 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatf if (HIMC himc = ::ImmGetContext(hwnd)) { - COMPOSITIONFORM cf; - cf.ptCurrentPos.x = (LONG)data->InputPos.x; - cf.ptCurrentPos.y = (LONG)data->InputPos.y; - cf.dwStyle = CFS_FORCE_POSITION; - ::ImmSetCompositionWindow(himc, &cf); + COMPOSITIONFORM composition_form = {}; + composition_form.ptCurrentPos.x = (LONG)data->InputPos.x; + composition_form.ptCurrentPos.y = (LONG)data->InputPos.y; + composition_form.dwStyle = CFS_FORCE_POSITION; + ::ImmSetCompositionWindow(himc, &composition_form); + CANDIDATEFORM candidate_form = {}; + candidate_form.dwStyle = CFS_CANDIDATEPOS; + candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x; + candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y; + ::ImmSetCandidateWindow(himc, &candidate_form); ::ImmReleaseContext(hwnd, himc); } } From 29a8ee0826e2de4f77368bedca248c5de10dc7dc Mon Sep 17 00:00:00 2001 From: actboy168 Date: Wed, 5 Jan 2022 13:20:54 +0100 Subject: [PATCH 233/332] Platform IME: add ImGuiPlatformImeData::WantVisible, hide IME when not used. (#2589) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 3 +++ imgui.h | 3 +++ imgui_widgets.cpp | 3 +++ 4 files changed, 10 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4c427303..9fb0e9cd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -61,6 +61,7 @@ Other Changes: - Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API, now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. - Platform IME: moved io.ImeWindowHandle to GetMainViewport()->PlatformHandleRaw. +- Platform IME: add ImGuiPlatformImeData::WantVisible, hide IME composition window when not used. (#2589) [@actboy168] - Platform IME: [windows] call ImmSetCandidateWindow() to position candidate window. - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. diff --git a/imgui.cpp b/imgui.cpp index ebde53df..7a3f9f13 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4149,6 +4149,7 @@ void ImGui::NewFrame() // Platform IME data: reset for the frame g.PlatformImeDataPrev = g.PlatformImeData; + g.PlatformImeData.WantVisible = false; // Mouse wheel scrolling, scale UpdateMouseWheel(); @@ -11510,6 +11511,8 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatf if (hwnd == 0) return; + ::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0); + if (HIMC himc = ::ImmGetContext(hwnd)) { COMPOSITIONFORM composition_form = {}; diff --git a/imgui.h b/imgui.h index 4f7a5333..3279f5f0 100644 --- a/imgui.h +++ b/imgui.h @@ -2832,7 +2832,10 @@ struct ImGuiViewport // (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function. struct ImGuiPlatformImeData { + bool WantVisible; // A widget wants the IME to be visible ImVec2 InputPos; // Position of the input cursor + + ImGuiPlatformImeData() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index eb464dcc..be7fe47a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4760,7 +4760,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) if (!is_readonly) + { + g.PlatformImeData.WantVisible = true; g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + } } } else From de36ff043e5445357f62e9a2c14a71eed037785e Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Wed, 5 Jan 2022 13:38:51 +0100 Subject: [PATCH 234/332] Platform IME: add ImGuiPlatformImeData::InputLineHeight (unused by win32). (#3113) --- docs/CHANGELOG.txt | 1 + imgui.h | 1 + imgui_widgets.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9fb0e9cd..ebb169e8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,7 @@ Other Changes: now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. - Platform IME: moved io.ImeWindowHandle to GetMainViewport()->PlatformHandleRaw. - Platform IME: add ImGuiPlatformImeData::WantVisible, hide IME composition window when not used. (#2589) [@actboy168] +- Platform IME: add ImGuiPlatformImeData::InputLineHeight. (#3113) [@liuliu] - Platform IME: [windows] call ImmSetCandidateWindow() to position candidate window. - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. diff --git a/imgui.h b/imgui.h index 3279f5f0..16e000d9 100644 --- a/imgui.h +++ b/imgui.h @@ -2834,6 +2834,7 @@ struct ImGuiPlatformImeData { bool WantVisible; // A widget wants the IME to be visible ImVec2 InputPos; // Position of the input cursor + float InputLineHeight; // Line height ImGuiPlatformImeData() { memset(this, 0, sizeof(*this)); } }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index be7fe47a..e83305cb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4763,6 +4763,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { g.PlatformImeData.WantVisible = true; g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + g.PlatformImeData.InputLineHeight = g.FontSize; } } } From 4d023bd7fe2d3438f604ef0d4a77c2da8a7d9ce4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jan 2022 14:55:17 +0100 Subject: [PATCH 235/332] Fix for using IMGUI_DISABLE_OBSOLETE_FUNCTIONS --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 7a3f9f13..564021b0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11531,7 +11531,7 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatf #else -static void SetPlatformImeDataFn_De(int, int) {} +static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeData*) {} #endif From 100ede57643f39306760440487c527c9032266c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jan 2022 16:54:03 +0100 Subject: [PATCH 236/332] Backends: GLFW: Fix CTRL+A, CTRL+Y, CTRL+Z to match keyboard layout. Converting GLFW untranslated keycodes back to translated keycodes. (#456, #2625) --- backends/imgui_impl_glfw.cpp | 21 +++++++++++++++++++++ docs/CHANGELOG.txt | 3 +++ 2 files changed, 24 insertions(+) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 2504ca87..d11efb62 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -66,6 +67,7 @@ #else #define GLFW_HAS_NEW_CURSORS (0) #endif +#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName() // GLFW data enum GlfwClientApi @@ -147,6 +149,25 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int a if (bd->PrevUserCallbackKey != NULL && window == bd->Window) bd->PrevUserCallbackKey(window, key, scancode, action, mods); +#if GLFW_HAS_GET_KEY_NAME + // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. + // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) + // See https://github.com/glfw/glfw/issues/1502 for details. + // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). + // This won't cover edge cases but this is at least going to cover common cases. + const char* key_name = glfwGetKeyName(key, scancode); + if (key_name && key_name[0] != 0 && key_name[1] == 0) + { + const char char_names[] = "'-=[]\\,;\'./"; + const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; + IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); + if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } + else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); } + else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } + } + // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); +#endif + ImGuiIO& io = ImGui::GetIO(); if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)) { diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ebb169e8..9eae1552 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,9 @@ Other Changes: - Platform IME: add ImGuiPlatformImeData::WantVisible, hide IME composition window when not used. (#2589) [@actboy168] - Platform IME: add ImGuiPlatformImeData::InputLineHeight. (#3113) [@liuliu] - Platform IME: [windows] call ImmSetCandidateWindow() to position candidate window. +- Backends: GLFW: fix CTRL+A, CTRL+Z, CTRL+Y shortcuts to match user keyboard layout. We are now converting GLFW + untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and + facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] From afffcd5810d030e24056c1a61f27a6eb632f50ed Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Dec 2021 18:25:46 +0100 Subject: [PATCH 237/332] Inputs: rename ImGuiKey_KeyPadEnter > ImGuiKey_KeypadEnter (#2625) --- backends/imgui_impl_allegro5.cpp | 2 +- backends/imgui_impl_android.cpp | 4 ++-- backends/imgui_impl_glfw.cpp | 2 +- backends/imgui_impl_glut.cpp | 2 +- backends/imgui_impl_osx.mm | 2 +- backends/imgui_impl_sdl.cpp | 2 +- backends/imgui_impl_win32.cpp | 2 +- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 +++-- imgui.h | 5 ++++- imgui_widgets.cpp | 2 +- 11 files changed, 17 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 8eb63614..62deba93 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -314,7 +314,7 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) io.KeyMap[ImGuiKey_Space] = ALLEGRO_KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = ALLEGRO_KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = ALLEGRO_KEY_ESCAPE; - io.KeyMap[ImGuiKey_KeyPadEnter] = ALLEGRO_KEY_PAD_ENTER; + io.KeyMap[ImGuiKey_KeypadEnter] = ALLEGRO_KEY_PAD_ENTER; io.KeyMap[ImGuiKey_A] = ALLEGRO_KEY_A; io.KeyMap[ImGuiKey_C] = ALLEGRO_KEY_C; io.KeyMap[ImGuiKey_V] = ALLEGRO_KEY_V; diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index aae8e6b8..6a454a4e 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -11,7 +11,7 @@ // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs @@ -139,7 +139,7 @@ bool ImGui_ImplAndroid_Init(ANativeWindow* window) io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE; io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER; io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE; - io.KeyMap[ImGuiKey_KeyPadEnter] = AKEYCODE_NUMPAD_ENTER; + io.KeyMap[ImGuiKey_KeypadEnter] = AKEYCODE_NUMPAD_ENTER; io.KeyMap[ImGuiKey_A] = AKEYCODE_A; io.KeyMap[ImGuiKey_C] = AKEYCODE_C; io.KeyMap[ImGuiKey_V] = AKEYCODE_V; diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index d11efb62..26172c7b 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -256,7 +256,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER; + io.KeyMap[ImGuiKey_KeypadEnter] = GLFW_KEY_KP_ENTER; io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 85bea776..2964d801 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -65,7 +65,7 @@ bool ImGui_ImplGLUT_Init() io.KeyMap[ImGuiKey_Space] = ' '; io.KeyMap[ImGuiKey_Enter] = 13; // == CTRL+M io.KeyMap[ImGuiKey_Escape] = 27; - io.KeyMap[ImGuiKey_KeyPadEnter] = 13; // == CTRL+M + io.KeyMap[ImGuiKey_KeypadEnter] = 13; // == CTRL+M io.KeyMap[ImGuiKey_A] = 'A'; io.KeyMap[ImGuiKey_C] = 'C'; io.KeyMap[ImGuiKey_V] = 'V'; diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index e1222fa7..9acd8baa 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -232,7 +232,7 @@ bool ImGui_ImplOSX_Init(NSView* view) io.KeyMap[ImGuiKey_Space] = kVK_Space; io.KeyMap[ImGuiKey_Enter] = kVK_Return; io.KeyMap[ImGuiKey_Escape] = kVK_Escape; - io.KeyMap[ImGuiKey_KeyPadEnter] = kVK_ANSI_KeypadEnter; + io.KeyMap[ImGuiKey_KeypadEnter] = kVK_ANSI_KeypadEnter; io.KeyMap[ImGuiKey_A] = kVK_ANSI_A; io.KeyMap[ImGuiKey_C] = kVK_ANSI_C; io.KeyMap[ImGuiKey_V] = kVK_ANSI_V; diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 82561bea..3116d142 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -207,7 +207,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE; io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; - io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_KP_ENTER; + io.KeyMap[ImGuiKey_KeypadEnter] = SDL_SCANCODE_KP_ENTER; io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index e1444efc..c34cd460 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -141,7 +141,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.KeyMap[ImGuiKey_Space] = VK_SPACE; io.KeyMap[ImGuiKey_Enter] = VK_RETURN; io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; - io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN; + io.KeyMap[ImGuiKey_KeypadEnter] = VK_RETURN; io.KeyMap[ImGuiKey_A] = 'A'; io.KeyMap[ImGuiKey_C] = 'C'; io.KeyMap[ImGuiKey_V] = 'V'; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9eae1552..1b0ba48d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,7 @@ HOW TO UPDATE? Breaking Changes: +- Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) - Commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x diff --git a/imgui.cpp b/imgui.cpp index 564021b0..6cae9357 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -386,8 +386,9 @@ 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. - - 2022/03/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - - 2022/03/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) + - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. + - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. + - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); diff --git a/imgui.h b/imgui.h index 16e000d9..dcb4e045 100644 --- a/imgui.h +++ b/imgui.h @@ -1367,7 +1367,7 @@ enum ImGuiKey_ ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, - ImGuiKey_KeyPadEnter, + ImGuiKey_KeypadEnter, ImGuiKey_A, // for text edit CTRL+A: select all ImGuiKey_C, // for text edit CTRL+C: copy ImGuiKey_V, // for text edit CTRL+V: paste @@ -1375,6 +1375,9 @@ enum ImGuiKey_ ImGuiKey_Y, // for text edit CTRL+Y: redo ImGuiKey_Z, // for text edit CTRL+Z: undo ImGuiKey_COUNT +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 +#endif }; // To test io.KeyMods (which is a combination of individual fields io.KeyCtrl, io.KeyShift, io.KeyAlt set by user/backend) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e83305cb..93f4e246 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4305,7 +4305,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; // 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 is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter); + const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeypadEnter); const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); From 3b669293019af511008654e2a8da059f3e51f8b4 Mon Sep 17 00:00:00 2001 From: thedmd Date: Sat, 11 Dec 2021 23:53:38 +0100 Subject: [PATCH 238/332] Inputs: Extra Keys / AddKeyEvent(): Added ImGuiKey values, io.AddKeyEvent(), GetKeyName(), IMGUI_DISABLE_OBSOLETE_KEYIO. Obsoleted GetKeyIndex(), io.KeyMap[], io.KeysDown[]. (#2625, #4858, #2787) --- imconfig.h | 1 + imgui.cpp | 236 ++++++++++++++++++++++++++++++++++++---------- imgui.h | 169 +++++++++++++++++++++++++++++---- imgui_demo.cpp | 11 ++- imgui_internal.h | 11 ++- imgui_widgets.cpp | 64 +++++++------ 6 files changed, 385 insertions(+), 107 deletions(-) diff --git a/imconfig.h b/imconfig.h index 7082c550..7d563d4d 100644 --- a/imconfig.h +++ b/imconfig.h @@ -28,6 +28,7 @@ //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS +//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.86: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. //---- Disable all of Dear ImGui or don't implement standard windows. // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. diff --git a/imgui.cpp b/imgui.cpp index 6cae9357..091e6a69 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -923,6 +923,9 @@ static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data); +// ImGuiKey <-> user key index mapping functions +static int GetKeyDataIndexInternal(int imgui_key_or_user_key_index); + namespace ImGui { // Navigation @@ -955,6 +958,7 @@ static void UpdateDebugToolStackQueries(); // Misc static void UpdateSettings(); +static void UpdateKeyboardInputs(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); 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); @@ -1108,8 +1112,10 @@ ImGuiIO::ImGuiIO() 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; UserData = NULL; @@ -1145,7 +1151,7 @@ ImGuiIO::ImGuiIO() MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); 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(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; } @@ -1212,15 +1218,38 @@ void ImGuiIO::ClearInputCharacters() void ImGuiIO::ClearInputKeys() { +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO memset(KeysDown, 0, sizeof(KeysDown)); - for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++) - KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f; +#endif + for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++) + { + KeysData[n].Down = false; + KeysData[n].DownDuration = -1.0f; + KeysData[n].DownDurationPrev = -1.0f; + } KeyCtrl = KeyShift = KeyAlt = KeySuper = false; KeyMods = KeyModsPrev = ImGuiKeyModFlags_None; for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++) NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; } +// FIXME: In the current version this is setting key data immediately. This will evolve into a trickling queue. +void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode) +{ + IM_UNUSED(native_keycode); + IM_UNUSED(native_scancode); + + int key_index = GetKeyDataIndexInternal(key); + if (key_index < 0) + return; + + KeysData[key_index].Down = down; + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + KeysDown[key_index] = KeysData[key_index].Down; +#endif +} + void ImGuiIO::AddFocusEvent(bool focused) { // We intentionally overwrite this and process in NewFrame(), in order to give a chance @@ -3220,7 +3249,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdUsingMouseWheel = false; g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask = 0x00; + g.ActiveIdUsingKeyInputMask.ClearAllBits(); } void ImGui::ClearActiveID() @@ -3751,6 +3780,26 @@ static bool IsWindowActiveAndVisible(ImGuiWindow* window) return (window->Active) && (!window->Hidden); } +static void ImGui::UpdateKeyboardInputs() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + + // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools + io.KeyMods = GetMergedKeyModFlags(); + + // Update keys + for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) + { + ImGuiKeyData& key_data = io.KeysData[i]; +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + key_data.Down = io.KeysDown[i]; +#endif + 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; + } +} + static void ImGui::UpdateMouseInputs() { ImGuiContext& g = *GImGui; @@ -4096,7 +4145,7 @@ void ImGui::NewFrame() { g.ActiveIdUsingNavDirMask = 0x00; g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask = 0x00; + g.ActiveIdUsingKeyInputMask.ClearAllBits(); } // Drag and drop @@ -4120,11 +4169,7 @@ void ImGui::NewFrame() } // Update keyboard input state - // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools - g.IO.KeyMods = GetMergedKeyModFlags(); - memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) - g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + UpdateKeyboardInputs(); // Update gamepad/keyboard navigation NavUpdate(); @@ -4891,7 +4936,7 @@ void ImGui::SetActiveIdUsingNavAndKeys() IM_ASSERT(g.ActiveId != 0); g.ActiveIdUsingNavDirMask = ~(ImU32)0; g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + g.ActiveIdUsingKeyInputMask.SetAllBits(); NavMoveRequestCancel(); } @@ -7258,22 +7303,93 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c return true; } -int ImGui::GetKeyIndex(ImGuiKey imgui_key) + +static int GetKeyDataIndexInternal(int imgui_key_or_user_key_index) { - IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); + if (imgui_key_or_user_key_index < 0) + return -1; + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + if (imgui_key_or_user_key_index >= ImGuiKey_LegacyNativeKey_BEGIN && imgui_key_or_user_key_index < ImGuiKey_LegacyNativeKey_END) + return imgui_key_or_user_key_index; + ImGuiContext& g = *GImGui; - return g.IO.KeyMap[imgui_key]; + if (imgui_key_or_user_key_index >= ImGuiKey_NamedKey_BEGIN && imgui_key_or_user_key_index < ImGuiKey_NamedKey_END) + return g.IO.KeyMap[imgui_key_or_user_key_index]; +#else + if (imgui_key_or_user_key_index >= ImGuiKey_NamedKey_BEGIN && imgui_key_or_user_key_index < ImGuiKey_NamedKey_END) + return imgui_key_or_user_key_index - ImGuiKey_NamedKey_BEGIN; +#endif + + return -1; +} + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO +static ImGuiKey GetImGuiKeyFromLegacyKey(int user_key_index) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(user_key_index >= 0 && user_key_index < ImGuiKey_LegacyNativeKey_END); + for (int imgui_key = ImGuiKey_NamedKey_BEGIN; imgui_key < ImGuiKey_NamedKey_END; ++imgui_key) + if (g.IO.KeyMap[imgui_key] == user_key_index) + return imgui_key; + return ImGuiKey_None; +} +#endif + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO +int ImGui::GetKeyIndex(ImGuiKey key) +{ + IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); + return GetKeyDataIndexInternal(key); +} +#endif + +// Those names a provided for debugging purpose and are not meant to be saved persistently not compared. +static const char* const GKeyNames[] = +{ + "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown", + "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape", + "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", "LeftShift", "LeftControl", + "LeftAlt", "LeftSuper", "RightShift", "RightControl", "RightAlt", "RightSuper", "Menu", + "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" +}; +IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); + +const char* ImGui::GetKeyName(ImGuiKey key) +{ +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); +#else + if (key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END) + if ((key = GetImGuiKeyFromLegacyKey(key)) == ImGuiKey_None) + return "N/A"; +#endif + if (key == ImGuiKey_None) + return "None"; + + return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; } // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! -bool ImGui::IsKeyDown(int user_key_index) +bool ImGui::IsKeyDown(ImGuiKey key) { - if (user_key_index < 0) +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); +#endif + + int key_index = GetKeyDataIndexInternal(key); + if (key_index < 0) return false; ImGuiContext& g = *GImGui; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - return g.IO.KeysDown[user_key_index]; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); + return g.IO.KeysData[key_index].Down; } // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) @@ -7294,36 +7410,52 @@ int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, flo return count; } -int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) +int ImGui::GetKeyPressedAmount(int key, float repeat_delay, float repeat_rate) { - ImGuiContext& g = *GImGui; +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); +#endif + + int key_index = GetKeyDataIndexInternal(key); if (key_index < 0) return 0; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[key_index]; + ImGuiContext& g = *GImGui; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); + const float t = g.IO.KeysData[key_index].DownDuration; return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); } -bool ImGui::IsKeyPressed(int user_key_index, bool repeat) +bool ImGui::IsKeyPressed(int key, bool repeat) { - ImGuiContext& g = *GImGui; - if (user_key_index < 0) +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); +#endif + + int key_index = GetKeyDataIndexInternal(key); + if (key_index < 0) return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[user_key_index]; + ImGuiContext& g = *GImGui; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); + const float t = g.IO.KeysData[key_index].DownDuration; if (t == 0.0f) return true; if (repeat && t > g.IO.KeyRepeatDelay) - return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; + return GetKeyPressedAmount(key, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; return false; } -bool ImGui::IsKeyReleased(int user_key_index) +bool ImGui::IsKeyReleased(int key) { +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); +#endif + + int key_index = GetKeyDataIndexInternal(key); + if (key_index < 0) + return false; ImGuiContext& g = *GImGui; - if (user_key_index < 0) return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); + return g.IO.KeysData[key_index].DownDurationPrev >= 0.0f && !g.IO.KeysData[key_index].Down; } bool ImGui::IsMouseDown(ImGuiMouseButton button) @@ -7522,12 +7654,14 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO for (int n = 0; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP) if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); +#endif // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) @@ -9576,7 +9710,7 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput { ImVec2 delta(0.0f, 0.0f); if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) - delta += ImVec2((float)IsKeyDown(GetKeyIndex(ImGuiKey_RightArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_LeftArrow)), (float)IsKeyDown(GetKeyIndex(ImGuiKey_DownArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_UpArrow))); + delta += ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow)); if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) @@ -9612,7 +9746,7 @@ static void ImGui::NavUpdate() // Update Keyboard->Nav inputs mapping if (nav_keyboard_active) { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0) + #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(_KEY)) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0) NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); @@ -9830,7 +9964,7 @@ void ImGui::NavUpdateCreateMoveRequest() // [DEBUG] Always send a request #if IMGUI_DEBUG_NAV_SCORING - if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C)) g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None) { @@ -9894,7 +10028,7 @@ void ImGui::NavUpdateCreateTabbingRequest() if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) return; - const bool tab_pressed = IsKeyPressedMap(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt; + const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt; if (!tab_pressed) return; @@ -10055,16 +10189,14 @@ static void ImGui::NavUpdateCancelRequest() static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiWindow* window = g.NavWindow; if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; - const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); - const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); - const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); - const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); + const bool page_up_held = IsKeyDown(ImGuiKey_PageUp) && !IsActiveIdUsingKey(ImGuiKey_PageUp); + const bool page_down_held = IsKeyDown(ImGuiKey_PageDown) && !IsActiveIdUsingKey(ImGuiKey_PageDown); + const bool home_pressed = IsKeyPressed(ImGuiKey_Home) && !IsActiveIdUsingKey(ImGuiKey_Home); + const bool end_pressed = IsKeyPressed(ImGuiKey_End) && !IsActiveIdUsingKey(ImGuiKey_End); if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out return 0.0f; @@ -10074,9 +10206,9 @@ static float ImGui::NavUpdatePageUpPageDown() if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) { // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) + if (IsKeyPressed(ImGuiKey_PageUp, true)) SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) + else if (IsKeyPressed(ImGuiKey_PageDown, true)) SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); else if (home_pressed) SetScrollY(window, 0.0f); @@ -10088,14 +10220,14 @@ static float ImGui::NavUpdatePageUpPageDown() ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); float nav_scoring_rect_offset_y = 0.0f; - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) + if (IsKeyPressed(ImGuiKey_PageUp, true)) { 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; } - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) + 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) @@ -10260,7 +10392,7 @@ static void ImGui::NavUpdateWindowing() // Start CTRL+Tab or Square+L/R window selection const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { @@ -10302,7 +10434,7 @@ static void ImGui::NavUpdateWindowing() { // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f - if (IsKeyPressedMap(ImGuiKey_Tab, true)) + if (IsKeyPressed(ImGuiKey_Tab, true)) NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1); if (!io.KeyCtrl) apply_focus_window = g.NavWindowingTarget; @@ -11942,7 +12074,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(); Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask); + + int active_id_using_key_input_count = 0; + for (int n = 0; n < g.ActiveIdUsingKeyInputMask.Size; n++) + active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask.TestBit(n) ? 1 : 0; + Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count); 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("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); Unindent(); @@ -12460,7 +12596,7 @@ void ImGui::UpdateDebugToolItemPicker() const ImGuiID hovered_id = g.HoveredIdPreviousFrame; SetMouseCursor(ImGuiMouseCursor_Hand); - if (IsKeyPressedMap(ImGuiKey_Escape)) + if (IsKeyPressed(ImGuiKey_Escape)) g.DebugItemPickerActive = false; if (IsMouseClicked(0) && hovered_id) { diff --git a/imgui.h b/imgui.h index dcb4e045..55ef8dee 100644 --- a/imgui.h +++ b/imgui.h @@ -884,13 +884,20 @@ namespace ImGui IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); // Inputs Utilities: Keyboard - // - For 'int user_key_index' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[]. - // - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index. - IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] - IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. - IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate - IMGUI_API bool IsKeyReleased(int user_key_index); // was key released (went from Down to !Down)? - IMGUI_API int GetKeyPressedAmount(int key_index, 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 + // Without IMGUI_DISABLE_OBSOLETE_KEYIO: + // - For 'ImGuiKey key' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[]. + // - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index. + // With: IMGUI_DISABLE_OBSOLETE_KEYIO: + // - `ImGuiKey key` will assert when key < 512 will be passed, previously reserved as user keys indices + // - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined) +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] +#endif + IMGUI_API const char* GetKeyName(ImGuiKey key); // returns English name of the key + IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. == io.KeysData[key - ImGuiKey_FirstKey].Down. + 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 int GetKeyPressedAmount(int 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 void CaptureKeyboardFromApp(bool want_capture_keyboard_value = true); // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call. // Inputs Utilities: Mouse @@ -1349,10 +1356,17 @@ enum ImGuiSortDirection_ ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. }; -// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array enum ImGuiKey_ { - ImGuiKey_Tab, + ImGuiKey_None = 0, + + // Reserve range used by legacy io.KeyMap[]. Prior to 1.86 we required user to fill io.KeysDown[512] using their own native index. + // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) + ImGuiKey_LegacyNativeKey_BEGIN = 0, + ImGuiKey_LegacyNativeKey_END = 512, // First index after valid range + ImGuiKey_NamedKey_BEGIN = 512, + + ImGuiKey_Tab = 512, ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, @@ -1367,14 +1381,110 @@ enum ImGuiKey_ ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, + ImGuiKey_Apostrophe, // ' + ImGuiKey_Comma, // , + ImGuiKey_Minus, // - + ImGuiKey_Period, // . + ImGuiKey_Slash, // / + ImGuiKey_Semicolon, // ; + ImGuiKey_Equal, // = + ImGuiKey_LeftBracket, // [ + ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backlash) + ImGuiKey_RightBracket, // ] + ImGuiKey_GraveAccent, // ` + ImGuiKey_CapsLock, + ImGuiKey_ScrollLock, + ImGuiKey_NumLock, + ImGuiKey_PrintScreen, + ImGuiKey_Pause, + ImGuiKey_Keypad0, + ImGuiKey_Keypad1, + ImGuiKey_Keypad2, + ImGuiKey_Keypad3, + ImGuiKey_Keypad4, + ImGuiKey_Keypad5, + ImGuiKey_Keypad6, + ImGuiKey_Keypad7, + ImGuiKey_Keypad8, + ImGuiKey_Keypad9, + ImGuiKey_KeypadDecimal, + ImGuiKey_KeypadDivide, + ImGuiKey_KeypadMultiply, + ImGuiKey_KeypadSubtract, + ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, - ImGuiKey_A, // for text edit CTRL+A: select all - ImGuiKey_C, // for text edit CTRL+C: copy - ImGuiKey_V, // for text edit CTRL+V: paste - ImGuiKey_X, // for text edit CTRL+X: cut - ImGuiKey_Y, // for text edit CTRL+Y: redo - ImGuiKey_Z, // for text edit CTRL+Z: undo - ImGuiKey_COUNT + ImGuiKey_KeypadEqual, + ImGuiKey_LeftShift, + ImGuiKey_LeftControl, + ImGuiKey_LeftAlt, + ImGuiKey_LeftSuper, + ImGuiKey_RightShift, + ImGuiKey_RightControl, + ImGuiKey_RightAlt, + ImGuiKey_RightSuper, + ImGuiKey_Menu, + ImGuiKey_0, + ImGuiKey_1, + ImGuiKey_2, + ImGuiKey_3, + ImGuiKey_4, + ImGuiKey_5, + ImGuiKey_6, + ImGuiKey_7, + ImGuiKey_8, + ImGuiKey_9, + ImGuiKey_A, + ImGuiKey_B, + ImGuiKey_C, + ImGuiKey_D, + ImGuiKey_E, + ImGuiKey_F, + ImGuiKey_G, + ImGuiKey_H, + ImGuiKey_I, + ImGuiKey_J, + ImGuiKey_K, + ImGuiKey_L, + ImGuiKey_M, + ImGuiKey_N, + ImGuiKey_O, + ImGuiKey_P, + ImGuiKey_Q, + ImGuiKey_R, + ImGuiKey_S, + ImGuiKey_T, + 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_COUNT, // No valid ImGuiKey is ever greater than this value + + ImGuiKey_NamedKey_END = ImGuiKey_COUNT, + ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + ImGuiKey_KeyIndex_BEGIN = ImGuiKey_LegacyNativeKey_BEGIN, + ImGuiKey_KeyIndex_END = ImGuiKey_LegacyNativeKey_END, +#else + ImGuiKey_KeyIndex_BEGIN = ImGuiKey_NamedKey_BEGIN, + ImGuiKey_KeyIndex_END = ImGuiKey_NamedKey_END, +#endif + ImGuiKey_KeyIndex_COUNT = ImGuiKey_KeyIndex_END - ImGuiKey_KeyIndex_BEGIN + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 #endif @@ -1817,6 +1927,13 @@ struct ImGuiStyle // Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. //----------------------------------------------------------------------------- +struct ImGuiKeyData +{ + bool Down; // True for if key is down + float DownDuration; // Duration the keyboard key has been down (0.0f == just pressed) + float DownDurationPrev; // Previous duration the key has been down +}; + struct ImGuiIO { //------------------------------------------------------------------ @@ -1833,7 +1950,6 @@ struct ImGuiIO 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. - int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. float KeyRepeatDelay; // = 0.250f // 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. void* UserData; // = NULL // Store your own data for retrieval by callbacks. @@ -1892,10 +2008,10 @@ struct ImGuiIO bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows - bool KeysDown[512]; // 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). float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). // Input Functions + IMGUI_API void AddKeyEvent(ImGuiKey key, bool down, int native_keycode = -1, int native_scancode = -1); // Notify Dear ImGui of key down/up event IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate @@ -1903,6 +2019,16 @@ struct ImGuiIO IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually IMGUI_API void ClearInputKeys(); // [Internal] Release all keys + //------------------------------------------------------------------ + // Legacy: before 1.86, we required backend to fill io.KeyMap[] (imgui->native map) during Init 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(). + //------------------------------------------------------------------ + +#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[512]; // [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). +#endif + //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is @@ -1930,8 +2056,7 @@ struct ImGuiIO ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() ImGuiKeyModFlags KeyModsPrev; // Key mods flags (from previous frame) - float KeysDownDuration[512]; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held) - float KeysDownDurationPrev[512]; // Duration the key has been down (from previous frame) + ImGuiKeyData KeysData[ImGuiKey_KeyIndex_COUNT]; // Key state for all known keys. 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) @@ -2851,6 +2976,10 @@ struct ImGuiPlatformImeData #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.XX (from YYYY) +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + static inline int GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and user_key_index was merged together and user_key_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; } +#endif // 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) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1f9b0bc9..1b37a2c5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -454,7 +454,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); ImGui::Text("<>"); } - if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space))) + if (ImGui::IsKeyPressed(ImGuiKey_Space)) io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); @@ -5695,9 +5695,9 @@ static void ShowDemoWindowMisc() IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State"); if (ImGui::TreeNode("Keyboard & Navigation State")) { - ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyDown(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); } - ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } - ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); } + ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { ImGuiKey key = i + ImGuiKey_KeyIndex_BEGIN; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X) (%.02f secs)", ImGui::GetKeyName(key), i, i, io.KeysData[i].DownDuration); } } + ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { ImGuiKey key = i + ImGuiKey_KeyIndex_BEGIN; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), i, i); } } + ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { ImGuiKey key = i + ImGuiKey_KeyIndex_BEGIN; if (ImGui::IsKeyReleased(key)){ ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), i, i); } } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. @@ -5868,6 +5868,9 @@ void ImGui::ShowAboutWindow(bool* p_open) #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS"); #endif +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_KEYIO"); +#endif #ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS"); #endif diff --git a/imgui_internal.h b/imgui_internal.h index 572a77b4..761ed8be 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -543,6 +543,7 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran template struct IMGUI_API ImBitArray { + static const int Size = BITCOUNT; ImU32 Storage[(BITCOUNT + 31) >> 5]; ImBitArray() { ClearAllBits(); } void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } @@ -552,6 +553,8 @@ struct IMGUI_API ImBitArray void ClearBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArrayClearBit(Storage, n); } void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) }; +template +const int ImBitArray::Size; // Helper: ImBitVector // Store 1-bit per value. @@ -1553,7 +1556,7 @@ struct ImGuiContext bool ActiveIdUsingMouseWheel; // Active widget will want to read mouse wheel. Blocks scrolling the underlying window. ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs. - ImU64 ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. + ImBitArray ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) @@ -1789,7 +1792,7 @@ struct ImGuiContext ActiveIdUsingMouseWheel = false; ActiveIdUsingNavDirMask = 0x00; ActiveIdUsingNavInputMask = 0x00; - ActiveIdUsingKeyInputMask = 0x00; + ActiveIdUsingKeyInputMask.ClearAllBits(); ActiveIdClickOffset = ImVec2(-1, -1); ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; @@ -2594,9 +2597,9 @@ namespace ImGui IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } - inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; } + inline bool IsActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask.TestBit(key - ImGuiKey_NamedKey_BEGIN); } + inline void SetActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key - ImGuiKey_NamedKey_BEGIN); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); - inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { ImGuiContext& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; } inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; } inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); } IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 93f4e246..ad9d44da 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4117,11 +4117,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); - g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Home) | ((ImU64)1 << ImGuiKey_End); + SetActiveIdUsingKey(ImGuiKey_Home); + SetActiveIdUsingKey(ImGuiKey_End); if (is_multiline) - g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_PageUp) | ((ImU64)1 << ImGuiKey_PageDown); - if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. - g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Tab); + { + SetActiveIdUsingKey(ImGuiKey_PageUp); + SetActiveIdUsingKey(ImGuiKey_PageDown); + } + if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. + { + SetActiveIdUsingKey(ImGuiKey_Tab); + } } // 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) @@ -4251,7 +4257,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // It is ill-defined whether the backend needs to send a \t character when pressing the TAB keys. // Win32 and GLFW naturally do it but not SDL. const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); - if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly) + if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly) if (!io.InputQueueCharacters.contains('\t')) { unsigned int c = '\t'; // Insert TAB @@ -4298,27 +4304,27 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_shift_key_only = (io.KeyMods == ImGuiKeyModFlags_Shift); const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl); - const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_readonly; - const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; + const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly; + const bool is_undo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable); + const bool is_redo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable; // 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 is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeypadEnter); - const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); - const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); - - if (IsKeyPressedMap(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 (IsKeyPressedMap(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 (IsKeyPressedMap(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); } - else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } - else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } - else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) + const bool is_validate_enter = IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_KeypadEnter); + const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressed(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_cancel = IsKeyPressed(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); + + 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); } + else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; } + else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } + else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly) { if (!state->HasSelection()) { @@ -4357,7 +4363,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); state->ClearSelection(); } - else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) + else if (is_shortcut_key && IsKeyPressed(ImGuiKey_A)) { state->SelectAll(); state->CursorFollow = true; @@ -4463,18 +4469,18 @@ 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_COUNT; - if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + ImGuiKey event_key = ImGuiKey_None; + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressed(ImGuiKey_Tab)) { event_flag = ImGuiInputTextFlags_CallbackCompletion; event_key = ImGuiKey_Tab; } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow)) { event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_UpArrow; } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow)) { event_flag = ImGuiInputTextFlags_CallbackHistory; event_key = ImGuiKey_DownArrow; From bf08c13e9baf5170d575552e05ff2cf10181e01b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Jan 2022 20:52:52 +0100 Subject: [PATCH 239/332] Inputs: Extra Keys / AddKeyEvent(): bidirectional mapping, basic CI, simplify backends, asserts on misuses, tested backward compat. (#2625, #4858, #2787) (edit: simplified backends merged into previous commits to make history clearer) --- .github/workflows/build.yml | 12 ++ docs/CHANGELOG.txt | 29 +++++ imconfig.h | 2 +- imgui.cpp | 211 ++++++++++++++++++++---------------- imgui.h | 105 +++++++++--------- imgui_demo.cpp | 19 +++- imgui_internal.h | 10 +- 7 files changed, 235 insertions(+), 153 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 13744f54..a667bd58 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -301,6 +301,18 @@ jobs: EOF g++ -I. -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (with IMGUI_DISABLE_OBSOLETE_KEYIO) + run: | + cat > example_single_file.cpp <<'EOF' + + #define IMGUI_DISABLE_OBSOLETE_KEYIO + #define IMGUI_IMPLEMENTATION + #include "misc/single_file/imgui_single_file.h" + #include "examples/example_null/main.cpp" + + EOF + g++ -I. -Wall -Wformat -o example_single_file example_single_file.cpp + - name: Build example_null (with IMGUI_DISABLE_DEMO_WINDOWS and IMGUI_DISABLE_METRICS_WINDOW) run: | cat > example_single_file.cpp <<'EOF' diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1b0ba48d..e5070d2c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,34 @@ HOW TO UPDATE? Breaking Changes: +- Reworked IO keyboard input system. (#2625, #3724) [@thedmd, @ocornut] + - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. + - Added io.AddKeyModEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. + - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. + - Added full range of key enums in ImGuiKey (e.g. ImGuiKey_F1). + - Added GetKeyName() helper function. + - Obsoleted GetKeyIndex(): it is now unnecessary and will now return the same value. + - All keyboard related functions taking 'int user_key_index' now take 'ImGuiKey key': + - IsKeyDown(), IsKeyPressed(), IsKeyReleased(), GetKeyPressedAmount(). + - All backends were updated to use io.AddKeyEvent(). + - Backward compatibility: + - Old backends populating those arrays will still work! (for a while) + - Calling e.g. IsKeyPressed(MY_NATIVE_KEY_XXX) will still work! (for a while) + - Those legacy arrays will only be disabled if '#define IMGUI_DISABLE_OBSOLETE_KEYIO' is set in your imconfig. + In a few versions, IMGUI_DISABLE_OBSOLETE_FUNCTIONS will automatically enable IMGUI_DISABLE_OBSOLETE_KEYIO, + so this will be moved into the regular obsolescence path. + - Transition guide: + - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) + - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) + - Backend writing to io.KeyMap[],KeysDown[] -> backend should call io.AddKeyEvent(), if legacy indexing is desired, call io.SetKeyEventNativeData() + - Basically the trick we took advantage of is that we previously only supported native keycode from 0 to 511, + so ImGuiKey values can still express a legacy native keycode, and new named keys are all >= 512. + - This will enable a few things in the future: + - Access to portable keys allows for backed-agnostic keyboard input code. Until now it was difficult to + share code using keyboard accross project because of this gap. (#2625, #3724) + - Access to full key ranges will allow us to develop a proper keyboard shortcut system. (#456) + - io.AddKeyEvent() will later be turned into a trickling IO queue (for all inputs) to handle very low framerate better. (#2525, #2787, #3383) + - io.SetKeyEventNativeData() include native keycode/scancode which will later be exposed. (#3141, #2959) - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) - Commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() @@ -68,6 +96,7 @@ Other Changes: - Backends: GLFW: fix CTRL+A, CTRL+Z, CTRL+Y shortcuts to match user keyboard layout. We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) +- Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32, Android: Updated to use io.AddKeyEvent() with full key range. (#2625) [@thedmd] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] diff --git a/imconfig.h b/imconfig.h index 7d563d4d..774b8f0e 100644 --- a/imconfig.h +++ b/imconfig.h @@ -28,7 +28,7 @@ //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS -//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.86: 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 will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. //---- Disable all of Dear ImGui or don't implement standard windows. // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. diff --git a/imgui.cpp b/imgui.cpp index 091e6a69..591bca0a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -348,7 +348,7 @@ CODE - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Keyboard: - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. - NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. + NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. @@ -386,6 +386,10 @@ 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. + - 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. + - 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() - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) @@ -923,9 +927,6 @@ static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data); -// ImGuiKey <-> user key index mapping functions -static int GetKeyDataIndexInternal(int imgui_key_or_user_key_index); - namespace ImGui { // Navigation @@ -1151,8 +1152,9 @@ ImGuiIO::ImGuiIO() MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); 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; + for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; + BackendUsingLegacyKeyArrays = (ImS8)-1; } // Pass in translated ASCII characters for text input. @@ -1233,20 +1235,49 @@ void ImGuiIO::ClearInputKeys() NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; } +// Queue a new key down/up event. +// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) +// - bool down: Is the key down? use false to signify a key release. // FIXME: In the current version this is setting key data immediately. This will evolve into a trickling queue. -void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode) +void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) { - IM_UNUSED(native_keycode); - IM_UNUSED(native_scancode); - - int key_index = GetKeyDataIndexInternal(key); - if (key_index < 0) + //if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } + if (key == ImGuiKey_None) return; + IM_ASSERT(ImGui::IsNamedKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. + + // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); + if (BackendUsingLegacyKeyArrays == -1) + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) + IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); +#endif + BackendUsingLegacyKeyArrays = 0; - KeysData[key_index].Down = down; + // Write key + const int keydata_index = (key - ImGuiKey_KeysData_OFFSET); + KeysData[keydata_index].Down = down; +} +// [Optional] Call add AddKeyEvent(). +// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices. +// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. +void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) +{ + IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 + IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey(native_legacy_index)); // >= 0 && <= 511 + IM_UNUSED(native_keycode); // Yet unused + IM_UNUSED(native_scancode); // Yet unused + + // Build native->imgui map so old user code can still call key functions with native 0..511 values. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - KeysDown[key_index] = KeysData[key_index].Down; + const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode; + if (ImGui::IsLegacyKey(legacy_key)) + KeyMap[legacy_key] = key; +#else + IM_UNUSED(key); + IM_UNUSED(native_legacy_index); #endif } @@ -3788,13 +3819,44 @@ static void ImGui::UpdateKeyboardInputs() // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools io.KeyMods = GetMergedKeyModFlags(); + // Import legacy keys or verify they are not used +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + if (io.BackendUsingLegacyKeyArrays == 0) + { + // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written too. + for (int n = 0; n < IM_ARRAYSIZE(io.KeysDown); n++) + IM_ASSERT(io.KeysDown[n] == false && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); + } + else + { + if (g.FrameCount == 0) + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!"); + + // Build reverse KeyMap (Named -> Legacy) + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) + if (io.KeyMap[n] != -1) + { + IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n])); + io.KeyMap[io.KeyMap[n]] = n; + } + + // Import legacy keys into new ones + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1) + { + const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n); + IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key)); + io.KeysData[key].Down = io.KeysDown[n]; + io.BackendUsingLegacyKeyArrays = 1; + } + } +#endif + // Update keys for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { ImGuiKeyData& key_data = io.KeysData[i]; -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - key_data.Down = io.KeysDown[i]; -#endif 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; } @@ -7303,44 +7365,30 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c return true; } - -static int GetKeyDataIndexInternal(int imgui_key_or_user_key_index) +const ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) { - if (imgui_key_or_user_key_index < 0) - return -1; - -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - if (imgui_key_or_user_key_index >= ImGuiKey_LegacyNativeKey_BEGIN && imgui_key_or_user_key_index < ImGuiKey_LegacyNativeKey_END) - return imgui_key_or_user_key_index; - ImGuiContext& g = *GImGui; - if (imgui_key_or_user_key_index >= ImGuiKey_NamedKey_BEGIN && imgui_key_or_user_key_index < ImGuiKey_NamedKey_END) - return g.IO.KeyMap[imgui_key_or_user_key_index]; + int index; +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END); + if (IsLegacyKey(key)) + index = (g.IO.KeyMap[key] != -1) ? g.IO.KeyMap[key] : key; // Remap native->imgui or imgui->native + else + index = key; #else - if (imgui_key_or_user_key_index >= ImGuiKey_NamedKey_BEGIN && imgui_key_or_user_key_index < ImGuiKey_NamedKey_END) - return imgui_key_or_user_key_index - ImGuiKey_NamedKey_BEGIN; + IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); + index = key - ImGuiKey_NamedKey_BEGIN; #endif - - return -1; + return &g.IO.KeysData[index]; } -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO -static ImGuiKey GetImGuiKeyFromLegacyKey(int user_key_index) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(user_key_index >= 0 && user_key_index < ImGuiKey_LegacyNativeKey_END); - for (int imgui_key = ImGuiKey_NamedKey_BEGIN; imgui_key < ImGuiKey_NamedKey_END; ++imgui_key) - if (g.IO.KeyMap[imgui_key] == user_key_index) - return imgui_key; - return ImGuiKey_None; -} -#endif - #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO int ImGui::GetKeyIndex(ImGuiKey key) { - IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); - return GetKeyDataIndexInternal(key); + ImGuiContext& g = *GImGui; + IM_ASSERT(IsNamedKey(key)); + const ImGuiKeyData* key_data = GetKeyData(key); + return (int)(key_data - g.IO.KeysData); } #endif @@ -7364,11 +7412,16 @@ IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); const char* ImGui::GetKeyName(ImGuiKey key) { #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); + IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); #else - if (key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END) - if ((key = GetImGuiKeyFromLegacyKey(key)) == ImGuiKey_None) + if (IsLegacyKey(key)) + { + ImGuiIO& io = GetIO(); + if (io.KeyMap[key] == -1) return "N/A"; + IM_ASSERT(IsNamedKey((ImGuiKey)io.KeyMap[key])); + key = (ImGuiKey)io.KeyMap[key]; + } #endif if (key == ImGuiKey_None) return "None"; @@ -7376,20 +7429,12 @@ const char* ImGui::GetKeyName(ImGuiKey key) return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; } -// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! -// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! +// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes. +// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87) bool ImGui::IsKeyDown(ImGuiKey key) { -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); -#endif - - int key_index = GetKeyDataIndexInternal(key); - if (key_index < 0) - return false; - ImGuiContext& g = *GImGui; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); - return g.IO.KeysData[key_index].Down; + const ImGuiKeyData* key_data = GetKeyData(key); + return key_data->Down; } // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) @@ -7410,33 +7455,19 @@ int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, flo return count; } -int ImGui::GetKeyPressedAmount(int key, float repeat_delay, float repeat_rate) +int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate) { -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); -#endif - - int key_index = GetKeyDataIndexInternal(key); - if (key_index < 0) - return 0; ImGuiContext& g = *GImGui; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); - const float t = g.IO.KeysData[key_index].DownDuration; + const ImGuiKeyData* key_data = GetKeyData(key); + const float t = key_data->DownDuration; return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); } -bool ImGui::IsKeyPressed(int key, bool repeat) +bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat) { -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); -#endif - - int key_index = GetKeyDataIndexInternal(key); - if (key_index < 0) - return false; ImGuiContext& g = *GImGui; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); - const float t = g.IO.KeysData[key_index].DownDuration; + const ImGuiKeyData* key_data = GetKeyData(key); + const float t = key_data->DownDuration; if (t == 0.0f) return true; if (repeat && t > g.IO.KeyRepeatDelay) @@ -7444,18 +7475,10 @@ bool ImGui::IsKeyPressed(int key, bool repeat) return false; } -bool ImGui::IsKeyReleased(int key) +bool ImGui::IsKeyReleased(ImGuiKey key) { -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code."); -#endif - - int key_index = GetKeyDataIndexInternal(key); - if (key_index < 0) - return false; - ImGuiContext& g = *GImGui; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysData)); - return g.IO.KeysData[key_index].DownDurationPrev >= 0.0f && !g.IO.KeysData[key_index].Down; + const ImGuiKeyData* key_data = GetKeyData(key); + return key_data->DownDurationPrev >= 0.0f && !key_data->Down; } bool ImGui::IsMouseDown(ImGuiMouseButton button) @@ -7655,11 +7678,11 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - for (int n = 0; n < ImGuiKey_COUNT; n++) - IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++) + IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)"); // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP) - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); #endif diff --git a/imgui.h b/imgui.h index 55ef8dee..77a9eb32 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18603 +#define IMGUI_VERSION_NUM 18604 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -152,6 +152,7 @@ struct ImColor; // Helper functions to create a color that c struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) +struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations @@ -173,7 +174,7 @@ typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction -typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier (ImGui-side enum) +typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier @@ -884,20 +885,16 @@ namespace ImGui IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); // Inputs Utilities: Keyboard - // Without IMGUI_DISABLE_OBSOLETE_KEYIO: - // - For 'ImGuiKey key' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[]. - // - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index. - // With: IMGUI_DISABLE_OBSOLETE_KEYIO: - // - `ImGuiKey key` will assert when key < 512 will be passed, previously reserved as user keys indices + // Without IMGUI_DISABLE_OBSOLETE_KEYIO: (legacy support) + // - For 'ImGuiKey key' you can still use your legacy native/user indices according to how your backend/engine stored them in io.KeysDown[]. + // With IMGUI_DISABLE_OBSOLETE_KEYIO: (this is the way forward) + // - Any use of 'ImGuiKey' will assert when key < 512 will be passed, previously reserved as native/user keys indices // - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined) -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] -#endif - IMGUI_API const char* GetKeyName(ImGuiKey key); // returns English name of the key - IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. == io.KeysData[key - ImGuiKey_FirstKey].Down. + 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 int GetKeyPressedAmount(int 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 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 CaptureKeyboardFromApp(bool want_capture_keyboard_value = true); // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call. // Inputs Utilities: Mouse @@ -1358,15 +1355,8 @@ enum ImGuiSortDirection_ enum ImGuiKey_ { - ImGuiKey_None = 0, - - // Reserve range used by legacy io.KeyMap[]. Prior to 1.86 we required user to fill io.KeysDown[512] using their own native index. - // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) - ImGuiKey_LegacyNativeKey_BEGIN = 0, - ImGuiKey_LegacyNativeKey_END = 512, // First index after valid range - ImGuiKey_NamedKey_BEGIN = 512, - - ImGuiKey_Tab = 512, + ImGuiKey_None = 0, + ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, @@ -1473,17 +1463,20 @@ enum ImGuiKey_ ImGuiKey_F12, ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value - ImGuiKey_NamedKey_END = ImGuiKey_COUNT, - ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, - -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - ImGuiKey_KeyIndex_BEGIN = ImGuiKey_LegacyNativeKey_BEGIN, - ImGuiKey_KeyIndex_END = ImGuiKey_LegacyNativeKey_END, + // Legacy range used by legacy io.KeyMap[]. Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index. + // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) + ImGuiKey_LegacyNativeKey_BEGIN = 0, + ImGuiKey_LegacyNativeKey_END = 512, // First index after valid range + ImGuiKey_NamedKey_BEGIN = 512, + ImGuiKey_NamedKey_END = ImGuiKey_COUNT, + ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys + ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0] #else - ImGuiKey_KeyIndex_BEGIN = ImGuiKey_NamedKey_BEGIN, - ImGuiKey_KeyIndex_END = ImGuiKey_NamedKey_END, + ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys + ImGuiKey_KeysData_OFFSET = ImGuiKey_LegacyNativeKey_BEGIN // First key stored in KeysData[0] #endif - ImGuiKey_KeyIndex_COUNT = ImGuiKey_KeyIndex_END - ImGuiKey_KeyIndex_BEGIN #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 @@ -1501,7 +1494,7 @@ enum ImGuiKeyModFlags_ }; // Gamepad/Keyboard navigation -// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. +// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). // Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets. enum ImGuiNavInput_ @@ -1525,7 +1518,7 @@ enum ImGuiNavInput_ ImGuiNavInput_TweakFast, // Faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. - // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. + // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from keyboard keys instead of io.NavInputs[]. ImGuiNavInput_KeyLeft_, // Move left // = Arrow keys ImGuiNavInput_KeyRight_, // Move right ImGuiNavInput_KeyUp_, // Move up @@ -1538,7 +1531,7 @@ enum ImGuiNavInput_ enum ImGuiConfigFlags_ { ImGuiConfigFlags_None = 0, - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[]. + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.AddKeyEvent() calls ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui backend to fill io.NavInputs[]. Backend also needs to set ImGuiBackendFlags_HasGamepad. ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth. ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. @@ -1927,11 +1920,13 @@ struct ImGuiStyle // Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. //----------------------------------------------------------------------------- +// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. +// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and not io.KeysData[key]->DownDuration. struct ImGuiKeyData { - bool Down; // True for if key is down - float DownDuration; // Duration the keyboard key has been down (0.0f == just pressed) - float DownDurationPrev; // Previous duration the key has been down + bool Down; // True for if key is down + float DownDuration; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held) + float DownDurationPrev; // Last frame duration the key has been down }; struct ImGuiIO @@ -2011,23 +2006,14 @@ struct ImGuiIO float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). // Input Functions - IMGUI_API void AddKeyEvent(ImGuiKey key, bool down, int native_keycode = -1, int native_scancode = -1); // Notify Dear ImGui of key down/up event + IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually IMGUI_API void ClearInputKeys(); // [Internal] Release all keys - - //------------------------------------------------------------------ - // Legacy: before 1.86, we required backend to fill io.KeyMap[] (imgui->native map) during Init 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(). - //------------------------------------------------------------------ - -#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[512]; // [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). -#endif + 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. //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -2050,13 +2036,20 @@ struct ImGuiIO 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(). +#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[512]; // [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). +#endif + //------------------------------------------------------------------ // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() ImGuiKeyModFlags KeyModsPrev; // Key mods flags (from previous frame) - ImGuiKeyData KeysData[ImGuiKey_KeyIndex_COUNT]; // Key state for all known keys. + 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) @@ -2076,6 +2069,7 @@ struct ImGuiIO float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. bool AppFocusLost; + ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[] ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. @@ -2973,13 +2967,18 @@ 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. //----------------------------------------------------------------------------- -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { - // OBSOLETED in 1.XX (from YYYY) -#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - static inline int GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and user_key_index was merged together and user_key_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; } +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key] +#else + static inline int 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.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) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1b37a2c5..589a92a9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5639,6 +5639,8 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } +namespace ImGui { extern const ImGuiKeyData* GetKeyData(ImGuiKey key); } + static void ShowDemoWindowMisc() { IMGUI_DEMO_MARKER("Filtering"); @@ -5695,15 +5697,26 @@ static void ShowDemoWindowMisc() IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State"); if (ImGui::TreeNode("Keyboard & Navigation State")) { - ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { ImGuiKey key = i + ImGuiKey_KeyIndex_BEGIN; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X) (%.02f secs)", ImGui::GetKeyName(key), i, i, io.KeysData[i].DownDuration); } } - ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { ImGuiKey key = i + ImGuiKey_KeyIndex_BEGIN; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), i, i); } } - ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { ImGuiKey key = i + ImGuiKey_KeyIndex_BEGIN; if (ImGui::IsKeyReleased(key)){ ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), i, i); } } + // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allow displaying the data for old/new backends. + // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes. +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + struct funcs { static bool IsNativeDupe(ImGuiKey) { return false; } }; +#else + struct funcs { static bool IsNativeDupe(ImGuiKey key) { return key < ImGuiKey_LegacyNativeKey_END && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array +#endif + ImGui::Text("Keys down:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X) (%.02f secs)", ImGui::GetKeyName(key), key, key, ImGui::GetKeyData(key)->DownDuration); } } + ImGui::Text("Keys pressed:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), key, key); } } + ImGui::Text("Keys released:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), key, key); } } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]); } ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Capture override")) + { ImGui::Button("Hovering me sets the\nkeyboard capture flag"); if (ImGui::IsItemHovered()) ImGui::CaptureKeyboardFromApp(true); diff --git a/imgui_internal.h b/imgui_internal.h index 761ed8be..d725c90f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2593,16 +2593,22 @@ namespace ImGui // 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 IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } + IMGUI_API const ImGuiKeyData* GetKeyData(ImGuiKey key); IMGUI_API void SetItemUsingMouseWheel(); IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } - inline bool IsActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask.TestBit(key - ImGuiKey_NamedKey_BEGIN); } - inline void SetActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key - ImGuiKey_NamedKey_BEGIN); } + inline bool IsActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(IsNamedKey(key)); ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask.TestBit(key - ImGuiKey_NamedKey_BEGIN); } + inline void SetActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(IsNamedKey(key)); ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key - ImGuiKey_NamedKey_BEGIN); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; } inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); } IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags(); +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } +#endif // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); From 746c9f76e4330417a6ffca539877a501826c00ed Mon Sep 17 00:00:00 2001 From: thedmd Date: Wed, 2 May 2018 20:42:37 +0200 Subject: [PATCH 240/332] Backends: Win32: Update to use io.AddEventKey() will full key map (#2625, #4858) --- backends/imgui_impl_win32.cpp | 237 +++++++++++++++++++++++++++------- backends/imgui_impl_win32.h | 6 +- 2 files changed, 196 insertions(+), 47 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index c34cd460..ebd8a313 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -3,9 +3,9 @@ // Implemented features: // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // 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. @@ -33,6 +33,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. // 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. @@ -125,30 +126,6 @@ bool ImGui_ImplWin32_Init(void* hwnd) // Set platform dependent data in viewport ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd; - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_Tab] = VK_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = VK_UP; - io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; - io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; - io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; - io.KeyMap[ImGuiKey_Home] = VK_HOME; - io.KeyMap[ImGuiKey_End] = VK_END; - io.KeyMap[ImGuiKey_Insert] = VK_INSERT; - io.KeyMap[ImGuiKey_Delete] = VK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = VK_BACK; - io.KeyMap[ImGuiKey_Space] = VK_SPACE; - io.KeyMap[ImGuiKey_Enter] = VK_RETURN; - io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; - io.KeyMap[ImGuiKey_KeypadEnter] = VK_RETURN; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - // Dynamically load XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD const char* xinput_dll_names[] = @@ -222,6 +199,43 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() return true; } +static bool IsVkDown(int vk) +{ + return (::GetKeyState(vk) & 0x8000) != 0; +} + +static void ImGui_ImplWin32_AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode = -1) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(key, down); + io.SetKeyEventNativeData(key, native_keycode, native_scancode); // To support legacy indexing (<1.87 user code) + IM_UNUSED(native_scancode); +} + +static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds() +{ + // Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one. + if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, false, VK_LSHIFT); + if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, false, VK_RSHIFT); + + // Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW). + if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftSuper, false, VK_LWIN); + if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN)) + ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightSuper, false, VK_RWIN); +} + +static void ImGui_ImplWin32_UpdateKeyModifiers() +{ + ImGuiIO& io = ImGui::GetIO(); + io.KeyShift = IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT); + io.KeyCtrl = IsVkDown(VK_LCONTROL) || IsVkDown(VK_RCONTROL); + io.KeyAlt = IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU); + io.KeySuper = IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN); +} + static void ImGui_ImplWin32_UpdateMousePos() { ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); @@ -323,6 +337,12 @@ void ImGui_ImplWin32_NewFrame() io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; bd->Time = current_time; + // Process workarounds for known Windows key handling issues + ImGui_ImplWin32_ProcessKeyEventsWorkarounds(); + + // Update key modifiers + ImGui_ImplWin32_UpdateKeyModifiers(); + // Update OS mouse position ImGui_ImplWin32_UpdateMousePos(); @@ -338,6 +358,122 @@ void ImGui_ImplWin32_NewFrame() ImGui_ImplWin32_UpdateGamepads(); } +// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255) +#define IM_VK_KEYPAD_ENTER (VK_RETURN + 256) + +// Map VK_xxx to ImGuiKey_xxx. +static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) +{ + switch (wParam) + { + case VK_TAB: return ImGuiKey_Tab; + case VK_LEFT: return ImGuiKey_LeftArrow; + case VK_RIGHT: return ImGuiKey_RightArrow; + case VK_UP: return ImGuiKey_UpArrow; + case VK_DOWN: return ImGuiKey_DownArrow; + case VK_PRIOR: return ImGuiKey_PageUp; + case VK_NEXT: return ImGuiKey_PageDown; + case VK_HOME: return ImGuiKey_Home; + case VK_END: return ImGuiKey_End; + case VK_INSERT: return ImGuiKey_Insert; + case VK_DELETE: return ImGuiKey_Delete; + case VK_BACK: return ImGuiKey_Backspace; + case VK_SPACE: return ImGuiKey_Space; + case VK_RETURN: return ImGuiKey_Enter; + case VK_ESCAPE: return ImGuiKey_Escape; + case VK_OEM_7: return ImGuiKey_Apostrophe; + case VK_OEM_COMMA: return ImGuiKey_Comma; + case VK_OEM_MINUS: return ImGuiKey_Minus; + case VK_OEM_PERIOD: return ImGuiKey_Period; + case VK_OEM_2: return ImGuiKey_Slash; + case VK_OEM_1: return ImGuiKey_Semicolon; + case VK_OEM_PLUS: return ImGuiKey_Equal; + case VK_OEM_4: return ImGuiKey_LeftBracket; + case VK_OEM_5: return ImGuiKey_Backslash; + case VK_OEM_6: return ImGuiKey_RightBracket; + case VK_OEM_3: return ImGuiKey_GraveAccent; + case VK_CAPITAL: return ImGuiKey_CapsLock; + case VK_SCROLL: return ImGuiKey_ScrollLock; + case VK_NUMLOCK: return ImGuiKey_NumLock; + case VK_SNAPSHOT: return ImGuiKey_PrintScreen; + case VK_PAUSE: return ImGuiKey_Pause; + case VK_NUMPAD0: return ImGuiKey_Keypad0; + case VK_NUMPAD1: return ImGuiKey_Keypad1; + case VK_NUMPAD2: return ImGuiKey_Keypad2; + case VK_NUMPAD3: return ImGuiKey_Keypad3; + case VK_NUMPAD4: return ImGuiKey_Keypad4; + case VK_NUMPAD5: return ImGuiKey_Keypad5; + case VK_NUMPAD6: return ImGuiKey_Keypad6; + case VK_NUMPAD7: return ImGuiKey_Keypad7; + case VK_NUMPAD8: return ImGuiKey_Keypad8; + case VK_NUMPAD9: return ImGuiKey_Keypad9; + case VK_DECIMAL: return ImGuiKey_KeypadDecimal; + case VK_DIVIDE: return ImGuiKey_KeypadDivide; + case VK_MULTIPLY: return ImGuiKey_KeypadMultiply; + case VK_SUBTRACT: return ImGuiKey_KeypadSubtract; + case VK_ADD: return ImGuiKey_KeypadAdd; + case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter; + case VK_LSHIFT: return ImGuiKey_LeftShift; + case VK_LCONTROL: return ImGuiKey_LeftControl; + case VK_LMENU: return ImGuiKey_LeftAlt; + case VK_LWIN: return ImGuiKey_LeftSuper; + case VK_RSHIFT: return ImGuiKey_RightShift; + case VK_RCONTROL: return ImGuiKey_RightControl; + case VK_RMENU: return ImGuiKey_RightAlt; + case VK_RWIN: return ImGuiKey_RightSuper; + case VK_APPS: return ImGuiKey_Menu; + case '0': return ImGuiKey_0; + case '1': return ImGuiKey_1; + case '2': return ImGuiKey_2; + case '3': return ImGuiKey_3; + case '4': return ImGuiKey_4; + case '5': return ImGuiKey_5; + case '6': return ImGuiKey_6; + case '7': return ImGuiKey_7; + case '8': return ImGuiKey_8; + case '9': return ImGuiKey_9; + case 'A': return ImGuiKey_A; + case 'B': return ImGuiKey_B; + case 'C': return ImGuiKey_C; + case 'D': return ImGuiKey_D; + case 'E': return ImGuiKey_E; + case 'F': return ImGuiKey_F; + case 'G': return ImGuiKey_G; + case 'H': return ImGuiKey_H; + case 'I': return ImGuiKey_I; + case 'J': return ImGuiKey_J; + case 'K': return ImGuiKey_K; + case 'L': return ImGuiKey_L; + case 'M': return ImGuiKey_M; + case 'N': return ImGuiKey_N; + case 'O': return ImGuiKey_O; + case 'P': return ImGuiKey_P; + case 'Q': return ImGuiKey_Q; + case 'R': return ImGuiKey_R; + case 'S': return ImGuiKey_S; + case 'T': return ImGuiKey_T; + case 'U': return ImGuiKey_U; + case 'V': return ImGuiKey_V; + case 'W': return ImGuiKey_W; + case 'X': return ImGuiKey_X; + case 'Y': return ImGuiKey_Y; + case 'Z': return ImGuiKey_Z; + case VK_F1: return ImGuiKey_F1; + case VK_F2: return ImGuiKey_F2; + case VK_F3: return ImGuiKey_F3; + case VK_F4: return ImGuiKey_F4; + case VK_F5: return ImGuiKey_F5; + case VK_F6: return ImGuiKey_F6; + case VK_F7: return ImGuiKey_F7; + case VK_F8: return ImGuiKey_F8; + case VK_F9: return ImGuiKey_F9; + case VK_F10: return ImGuiKey_F10; + case VK_F11: return ImGuiKey_F11; + case VK_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} + // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E @@ -424,26 +560,39 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_SYSKEYDOWN: case WM_SYSKEYUP: { - bool down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); + const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); + if (wParam < 256) - io.KeysDown[wParam] = down; - if (wParam == VK_CONTROL) - { - io.KeysDown[VK_LCONTROL] = ((::GetKeyState(VK_LCONTROL) & 0x8000) != 0); - io.KeysDown[VK_RCONTROL] = ((::GetKeyState(VK_RCONTROL) & 0x8000) != 0); - io.KeyCtrl = io.KeysDown[VK_LCONTROL] || io.KeysDown[VK_RCONTROL]; - } - if (wParam == VK_SHIFT) - { - io.KeysDown[VK_LSHIFT] = ((::GetKeyState(VK_LSHIFT) & 0x8000) != 0); - io.KeysDown[VK_RSHIFT] = ((::GetKeyState(VK_RSHIFT) & 0x8000) != 0); - io.KeyShift = io.KeysDown[VK_LSHIFT] || io.KeysDown[VK_RSHIFT]; - } - if (wParam == VK_MENU) { - io.KeysDown[VK_LMENU] = ((::GetKeyState(VK_LMENU) & 0x8000) != 0); - io.KeysDown[VK_RMENU] = ((::GetKeyState(VK_RMENU) & 0x8000) != 0); - io.KeyAlt = io.KeysDown[VK_LMENU] || io.KeysDown[VK_RMENU]; + // Obtain virtual key code + // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.) + int vk = (int)wParam; + if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) + vk = IM_VK_KEYPAD_ENTER; + + // Submit key event + const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); + const int scancode = (int)LOBYTE(HIWORD(lParam)); + if (key != ImGuiKey_None) + ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode); + + // Submit individual left/right modifier events + if (vk == VK_SHIFT) + { + // Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds() + if (IsVkDown(VK_LSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); } + if (IsVkDown(VK_RSHIFT) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); } + } + else if (vk == VK_CONTROL) + { + if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftControl, is_key_down, VK_LCONTROL, scancode); } + if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightControl, is_key_down, VK_RCONTROL, scancode); } + } + else if (vk == VK_MENU) + { + if (IsVkDown(VK_LMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); } + if (IsVkDown(VK_RMENU) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); } + } } return 0; } diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 768fe169..a04b6a71 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -3,11 +3,11 @@ // Implemented features: // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs From ecd212c01d2236ba2d23cbadb87a644281591e36 Mon Sep 17 00:00:00 2001 From: thedmd Date: Sun, 12 Dec 2021 12:00:31 +0100 Subject: [PATCH 241/332] Backends: GLFW: Update to use io.AddEventKey() will full key map (#2625, #4858) --- backends/imgui_impl_glfw.cpp | 192 ++++++++++++++++++++++++++--------- backends/imgui_impl_glfw.h | 4 +- 2 files changed, 147 insertions(+), 49 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 26172c7b..90295d6c 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -5,9 +5,9 @@ // Implemented features: // [X] Platform: Clipboard support. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). -// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). // 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. @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). @@ -122,6 +123,119 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) glfwSetClipboardString((GLFWwindow*)user_data, text); } +static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) +{ + switch (key) + { + case GLFW_KEY_TAB: return ImGuiKey_Tab; + case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; + case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow; + case GLFW_KEY_UP: return ImGuiKey_UpArrow; + case GLFW_KEY_DOWN: return ImGuiKey_DownArrow; + case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp; + case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case GLFW_KEY_HOME: return ImGuiKey_Home; + case GLFW_KEY_END: return ImGuiKey_End; + case GLFW_KEY_INSERT: return ImGuiKey_Insert; + case GLFW_KEY_DELETE: return ImGuiKey_Delete; + case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace; + case GLFW_KEY_SPACE: return ImGuiKey_Space; + case GLFW_KEY_ENTER: return ImGuiKey_Enter; + case GLFW_KEY_ESCAPE: return ImGuiKey_Escape; + case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe; + case GLFW_KEY_COMMA: return ImGuiKey_Comma; + case GLFW_KEY_MINUS: return ImGuiKey_Minus; + case GLFW_KEY_PERIOD: return ImGuiKey_Period; + case GLFW_KEY_SLASH: return ImGuiKey_Slash; + case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case GLFW_KEY_EQUAL: return ImGuiKey_Equal; + case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash; + case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock; + case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock; + case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case GLFW_KEY_PAUSE: return ImGuiKey_Pause; + case GLFW_KEY_KP_0: return ImGuiKey_Keypad0; + case GLFW_KEY_KP_1: return ImGuiKey_Keypad1; + case GLFW_KEY_KP_2: return ImGuiKey_Keypad2; + case GLFW_KEY_KP_3: return ImGuiKey_Keypad3; + case GLFW_KEY_KP_4: return ImGuiKey_Keypad4; + case GLFW_KEY_KP_5: return ImGuiKey_Keypad5; + case GLFW_KEY_KP_6: return ImGuiKey_Keypad6; + case GLFW_KEY_KP_7: return ImGuiKey_Keypad7; + case GLFW_KEY_KP_8: return ImGuiKey_Keypad8; + case GLFW_KEY_KP_9: return ImGuiKey_Keypad9; + case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd; + case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; + case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; + case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftControl; + case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; + case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; + case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightControl; + case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; + case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; + case GLFW_KEY_MENU: return ImGuiKey_Menu; + case GLFW_KEY_0: return ImGuiKey_0; + case GLFW_KEY_1: return ImGuiKey_1; + case GLFW_KEY_2: return ImGuiKey_2; + case GLFW_KEY_3: return ImGuiKey_3; + case GLFW_KEY_4: return ImGuiKey_4; + case GLFW_KEY_5: return ImGuiKey_5; + case GLFW_KEY_6: return ImGuiKey_6; + case GLFW_KEY_7: return ImGuiKey_7; + case GLFW_KEY_8: return ImGuiKey_8; + case GLFW_KEY_9: return ImGuiKey_9; + case GLFW_KEY_A: return ImGuiKey_A; + case GLFW_KEY_B: return ImGuiKey_B; + case GLFW_KEY_C: return ImGuiKey_C; + case GLFW_KEY_D: return ImGuiKey_D; + case GLFW_KEY_E: return ImGuiKey_E; + case GLFW_KEY_F: return ImGuiKey_F; + case GLFW_KEY_G: return ImGuiKey_G; + case GLFW_KEY_H: return ImGuiKey_H; + case GLFW_KEY_I: return ImGuiKey_I; + case GLFW_KEY_J: return ImGuiKey_J; + case GLFW_KEY_K: return ImGuiKey_K; + case GLFW_KEY_L: return ImGuiKey_L; + case GLFW_KEY_M: return ImGuiKey_M; + case GLFW_KEY_N: return ImGuiKey_N; + case GLFW_KEY_O: return ImGuiKey_O; + case GLFW_KEY_P: return ImGuiKey_P; + case GLFW_KEY_Q: return ImGuiKey_Q; + case GLFW_KEY_R: return ImGuiKey_R; + case GLFW_KEY_S: return ImGuiKey_S; + case GLFW_KEY_T: return ImGuiKey_T; + case GLFW_KEY_U: return ImGuiKey_U; + case GLFW_KEY_V: return ImGuiKey_V; + case GLFW_KEY_W: return ImGuiKey_W; + case GLFW_KEY_X: return ImGuiKey_X; + case GLFW_KEY_Y: return ImGuiKey_Y; + case GLFW_KEY_Z: return ImGuiKey_Z; + case GLFW_KEY_F1: return ImGuiKey_F1; + case GLFW_KEY_F2: return ImGuiKey_F2; + case GLFW_KEY_F3: return ImGuiKey_F3; + case GLFW_KEY_F4: return ImGuiKey_F4; + case GLFW_KEY_F5: return ImGuiKey_F5; + case GLFW_KEY_F6: return ImGuiKey_F6; + case GLFW_KEY_F7: return ImGuiKey_F7; + case GLFW_KEY_F8: return ImGuiKey_F8; + case GLFW_KEY_F9: return ImGuiKey_F9; + case GLFW_KEY_F10: return ImGuiKey_F10; + case GLFW_KEY_F11: return ImGuiKey_F11; + case GLFW_KEY_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} + void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); @@ -143,12 +257,8 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo io.MouseWheel += (float)yoffset; } -void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) +static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackKey != NULL && window == bd->Window) - bd->PrevUserCallbackKey(window, key, scancode, action, mods); - #if GLFW_HAS_GET_KEY_NAME // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) @@ -167,25 +277,24 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int a } // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); #endif + return key; +} - ImGuiIO& io = ImGui::GetIO(); - if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)) - { - if (action == GLFW_PRESS) - io.KeysDown[key] = true; - if (action == GLFW_RELEASE) - io.KeysDown[key] = false; - } +void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackKey != NULL && window == bd->Window) + bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); - // Modifiers are not reliable across systems - io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; - io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; - io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; -#ifdef _WIN32 - io.KeySuper = false; -#else - io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; -#endif + if (action != GLFW_PRESS && action != GLFW_RELEASE) + return; + + keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); + + ImGuiIO& io = ImGui::GetIO(); + ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode); + io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); + io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) } void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) @@ -240,30 +349,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->Window = window; bd->Time = 0.0; - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; - io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; - io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_KeypadEnter] = GLFW_KEY_KP_ENTER; - io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; - io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; - io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; - io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; - io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; - io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.ClipboardUserData = bd->Window; @@ -455,6 +540,16 @@ static void ImGui_ImplGlfw_UpdateGamepads() io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; } +static void ImGui_ImplGlfw_UpdateKeyModifiers() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + io.KeyShift = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); + io.KeyCtrl = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); + io.KeyAlt = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); + io.KeySuper = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); +} + void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); @@ -475,6 +570,9 @@ void ImGui_ImplGlfw_NewFrame() io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; + // Update key modifiers + ImGui_ImplGlfw_UpdateKeyModifiers(); + ImGui_ImplGlfw_UpdateMousePosAndButtons(); ImGui_ImplGlfw_UpdateMouseCursor(); diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 5e1fb06d..84fa4b8b 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -4,9 +4,9 @@ // Implemented features: // [X] Platform: Clipboard support. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. -// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). // 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. From fe646ea59131fb9bd792de8843173b7e6f6b9e51 Mon Sep 17 00:00:00 2001 From: thedmd Date: Sun, 12 Dec 2021 11:57:37 +0100 Subject: [PATCH 242/332] Backends: SDL2: Update to use io.AddEventKey() will full key map (#2625, #4858) + created localized key (using keycode instead of scancode) (#456) Legacy indexing stills uses Scancode --- backends/imgui_impl_sdl.cpp | 170 ++++++++++++++++++++++++++++-------- backends/imgui_impl_sdl.h | 4 +- docs/CHANGELOG.txt | 7 +- 3 files changed, 139 insertions(+), 42 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 3116d142..c50caab0 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -4,10 +4,10 @@ // (Prefer SDL 2.0.5+ for full feature support.) // Implemented features: -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Clipboard support. -// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // Missing features: // [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+) // 2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary. @@ -105,6 +106,119 @@ static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) SDL_SetClipboardText(text); } +static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) +{ + switch (keycode) + { + case SDLK_TAB: return ImGuiKey_Tab; + case SDLK_LEFT: return ImGuiKey_LeftArrow; + case SDLK_RIGHT: return ImGuiKey_RightArrow; + case SDLK_UP: return ImGuiKey_UpArrow; + case SDLK_DOWN: return ImGuiKey_DownArrow; + case SDLK_PAGEUP: return ImGuiKey_PageUp; + case SDLK_PAGEDOWN: return ImGuiKey_PageDown; + case SDLK_HOME: return ImGuiKey_Home; + case SDLK_END: return ImGuiKey_End; + case SDLK_INSERT: return ImGuiKey_Insert; + case SDLK_DELETE: return ImGuiKey_Delete; + case SDLK_BACKSPACE: return ImGuiKey_Backspace; + case SDLK_SPACE: return ImGuiKey_Space; + case SDLK_RETURN: return ImGuiKey_Enter; + case SDLK_ESCAPE: return ImGuiKey_Escape; + case SDLK_QUOTE: return ImGuiKey_Apostrophe; + case SDLK_COMMA: return ImGuiKey_Comma; + case SDLK_MINUS: return ImGuiKey_Minus; + case SDLK_PERIOD: return ImGuiKey_Period; + case SDLK_SLASH: return ImGuiKey_Slash; + case SDLK_SEMICOLON: return ImGuiKey_Semicolon; + case SDLK_EQUALS: return ImGuiKey_Equal; + case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket; + case SDLK_BACKSLASH: return ImGuiKey_Backslash; + case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket; + case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent; + case SDLK_CAPSLOCK: return ImGuiKey_CapsLock; + case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock; + case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock; + case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen; + case SDLK_PAUSE: return ImGuiKey_Pause; + case SDLK_KP_0: return ImGuiKey_Keypad0; + case SDLK_KP_1: return ImGuiKey_Keypad1; + case SDLK_KP_2: return ImGuiKey_Keypad2; + case SDLK_KP_3: return ImGuiKey_Keypad3; + case SDLK_KP_4: return ImGuiKey_Keypad4; + case SDLK_KP_5: return ImGuiKey_Keypad5; + case SDLK_KP_6: return ImGuiKey_Keypad6; + case SDLK_KP_7: return ImGuiKey_Keypad7; + case SDLK_KP_8: return ImGuiKey_Keypad8; + case SDLK_KP_9: return ImGuiKey_Keypad9; + case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal; + case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract; + case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd; + case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter; + case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual; + case SDLK_LSHIFT: return ImGuiKey_LeftShift; + case SDLK_LCTRL: return ImGuiKey_LeftControl; + case SDLK_LALT: return ImGuiKey_LeftAlt; + case SDLK_LGUI: return ImGuiKey_LeftSuper; + case SDLK_RSHIFT: return ImGuiKey_RightShift; + case SDLK_RCTRL: return ImGuiKey_RightControl; + case SDLK_RALT: return ImGuiKey_RightAlt; + case SDLK_RGUI: return ImGuiKey_RightSuper; + case SDLK_MENU: return ImGuiKey_Menu; + case SDLK_0: return ImGuiKey_0; + case SDLK_1: return ImGuiKey_1; + case SDLK_2: return ImGuiKey_2; + case SDLK_3: return ImGuiKey_3; + case SDLK_4: return ImGuiKey_4; + case SDLK_5: return ImGuiKey_5; + case SDLK_6: return ImGuiKey_6; + case SDLK_7: return ImGuiKey_7; + case SDLK_8: return ImGuiKey_8; + case SDLK_9: return ImGuiKey_9; + case SDLK_a: return ImGuiKey_A; + case SDLK_b: return ImGuiKey_B; + case SDLK_c: return ImGuiKey_C; + case SDLK_d: return ImGuiKey_D; + case SDLK_e: return ImGuiKey_E; + case SDLK_f: return ImGuiKey_F; + case SDLK_g: return ImGuiKey_G; + case SDLK_h: return ImGuiKey_H; + case SDLK_i: return ImGuiKey_I; + case SDLK_j: return ImGuiKey_J; + case SDLK_k: return ImGuiKey_K; + case SDLK_l: return ImGuiKey_L; + case SDLK_m: return ImGuiKey_M; + case SDLK_n: return ImGuiKey_N; + case SDLK_o: return ImGuiKey_O; + case SDLK_p: return ImGuiKey_P; + case SDLK_q: return ImGuiKey_Q; + case SDLK_r: return ImGuiKey_R; + case SDLK_s: return ImGuiKey_S; + case SDLK_t: return ImGuiKey_T; + case SDLK_u: return ImGuiKey_U; + case SDLK_v: return ImGuiKey_V; + case SDLK_w: return ImGuiKey_W; + case SDLK_x: return ImGuiKey_X; + case SDLK_y: return ImGuiKey_Y; + case SDLK_z: return ImGuiKey_Z; + case SDLK_F1: return ImGuiKey_F1; + case SDLK_F2: return ImGuiKey_F2; + case SDLK_F3: return ImGuiKey_F3; + case SDLK_F4: return ImGuiKey_F4; + case SDLK_F5: return ImGuiKey_F5; + case SDLK_F6: return ImGuiKey_F6; + case SDLK_F7: return ImGuiKey_F7; + case SDLK_F8: return ImGuiKey_F8; + case SDLK_F9: return ImGuiKey_F9; + case SDLK_F10: return ImGuiKey_F10; + case SDLK_F11: return ImGuiKey_F11; + case SDLK_F12: return ImGuiKey_F12; + } + return ImGuiKey_None; +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -140,17 +254,9 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_KEYDOWN: case SDL_KEYUP: { - int key = event->key.keysym.scancode; - IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)); - io.KeysDown[key] = (event->type == SDL_KEYDOWN); - io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); - io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); - io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); -#ifdef _WIN32 - io.KeySuper = false; -#else - io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); -#endif + ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym); + io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); + io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; } case SDL_WINDOWEVENT: @@ -191,30 +297,6 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) bd->Window = window; bd->MouseCanUseGlobalState = mouse_can_use_global_state; - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. - io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; - io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN; - io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP; - io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN; - io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; - io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; - io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; - io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE; - io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; - io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; - io.KeyMap[ImGuiKey_KeypadEnter] = SDL_SCANCODE_KP_ENTER; - io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; - io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; - io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; - io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X; - io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; - io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; - io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; io.ClipboardUserData = NULL; @@ -419,6 +501,17 @@ static void ImGui_ImplSDL2_UpdateGamepads() #undef MAP_ANALOG } +static void ImGui_ImplSDL2_UpdateKeyModifiers() +{ + SDL_Keymod keymod = SDL_GetModState(); + + ImGuiIO& io = ImGui::GetIO(); + io.KeyShift = (keymod & KMOD_SHIFT) != 0; + io.KeyCtrl = (keymod & KMOD_CTRL) != 0; + io.KeyAlt = (keymod & KMOD_ALT) != 0; + io.KeySuper = (keymod & KMOD_GUI) != 0; +} + void ImGui_ImplSDL2_NewFrame() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -442,6 +535,9 @@ void ImGui_ImplSDL2_NewFrame() io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; + // Update key modifiers + ImGui_ImplSDL2_UpdateKeyModifiers(); + ImGui_ImplSDL2_UpdateMousePosAndButtons(); ImGui_ImplSDL2_UpdateMouseCursor(); diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h index d2439eb7..3db454c5 100644 --- a/backends/imgui_impl_sdl.h +++ b/backends/imgui_impl_sdl.h @@ -3,10 +3,10 @@ // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // Implemented features: -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Clipboard support. -// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // Missing features: // [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e5070d2c..3153a670 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -93,9 +93,10 @@ Other Changes: - Platform IME: add ImGuiPlatformImeData::WantVisible, hide IME composition window when not used. (#2589) [@actboy168] - Platform IME: add ImGuiPlatformImeData::InputLineHeight. (#3113) [@liuliu] - Platform IME: [windows] call ImmSetCandidateWindow() to position candidate window. -- Backends: GLFW: fix CTRL+A, CTRL+Z, CTRL+Y shortcuts to match user keyboard layout. We are now converting GLFW - untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and - facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) +- Backends: GLFW: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. + We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every + other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) +- Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. - Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32, Android: Updated to use io.AddKeyEvent() with full key range. (#2625) [@thedmd] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. From 1bfe4a75be3c89a9c7facce29dd0c791e1d76b29 Mon Sep 17 00:00:00 2001 From: thedmd Date: Sun, 12 Dec 2021 11:58:50 +0100 Subject: [PATCH 243/332] Backends: Allegro5: Update to use io.AddEventKey() will full key map (#2625, #4858) --- backends/imgui_impl_allegro5.cpp | 143 ++++++++++++++++++++++++++----- backends/imgui_impl_allegro5.h | 3 +- 2 files changed, 122 insertions(+), 24 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 62deba93..60a104ed 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -3,6 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Clipboard support (from Allegro 5.1.12) // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // Issues: @@ -16,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -274,6 +276,119 @@ static void ImGui_ImplAllegro5_SetClipboardText(void*, const char* text) } #endif +static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code) +{ + switch (key_code) + { + case ALLEGRO_KEY_TAB: return ImGuiKey_Tab; + case ALLEGRO_KEY_LEFT: return ImGuiKey_LeftArrow; + case ALLEGRO_KEY_RIGHT: return ImGuiKey_RightArrow; + case ALLEGRO_KEY_UP: return ImGuiKey_UpArrow; + case ALLEGRO_KEY_DOWN: return ImGuiKey_DownArrow; + case ALLEGRO_KEY_PGUP: return ImGuiKey_PageUp; + case ALLEGRO_KEY_PGDN: return ImGuiKey_PageDown; + case ALLEGRO_KEY_HOME: return ImGuiKey_Home; + case ALLEGRO_KEY_END: return ImGuiKey_End; + case ALLEGRO_KEY_INSERT: return ImGuiKey_Insert; + case ALLEGRO_KEY_DELETE: return ImGuiKey_Delete; + case ALLEGRO_KEY_BACKSPACE: return ImGuiKey_Backspace; + case ALLEGRO_KEY_SPACE: return ImGuiKey_Space; + case ALLEGRO_KEY_ENTER: return ImGuiKey_Enter; + case ALLEGRO_KEY_ESCAPE: return ImGuiKey_Escape; + case ALLEGRO_KEY_QUOTE: return ImGuiKey_Apostrophe; + case ALLEGRO_KEY_COMMA: return ImGuiKey_Comma; + case ALLEGRO_KEY_MINUS: return ImGuiKey_Minus; + case ALLEGRO_KEY_FULLSTOP: return ImGuiKey_Period; + case ALLEGRO_KEY_SLASH: return ImGuiKey_Slash; + case ALLEGRO_KEY_SEMICOLON: return ImGuiKey_Semicolon; + case ALLEGRO_KEY_EQUALS: return ImGuiKey_Equal; + case ALLEGRO_KEY_OPENBRACE: return ImGuiKey_LeftBracket; + case ALLEGRO_KEY_BACKSLASH: return ImGuiKey_Backslash; + case ALLEGRO_KEY_CLOSEBRACE: return ImGuiKey_RightBracket; + case ALLEGRO_KEY_TILDE: return ImGuiKey_GraveAccent; + case ALLEGRO_KEY_CAPSLOCK: return ImGuiKey_CapsLock; + case ALLEGRO_KEY_SCROLLLOCK: return ImGuiKey_ScrollLock; + case ALLEGRO_KEY_NUMLOCK: return ImGuiKey_NumLock; + case ALLEGRO_KEY_PRINTSCREEN: return ImGuiKey_PrintScreen; + case ALLEGRO_KEY_PAUSE: return ImGuiKey_Pause; + case ALLEGRO_KEY_PAD_0: return ImGuiKey_Keypad0; + case ALLEGRO_KEY_PAD_1: return ImGuiKey_Keypad1; + case ALLEGRO_KEY_PAD_2: return ImGuiKey_Keypad2; + case ALLEGRO_KEY_PAD_3: return ImGuiKey_Keypad3; + case ALLEGRO_KEY_PAD_4: return ImGuiKey_Keypad4; + case ALLEGRO_KEY_PAD_5: return ImGuiKey_Keypad5; + case ALLEGRO_KEY_PAD_6: return ImGuiKey_Keypad6; + case ALLEGRO_KEY_PAD_7: return ImGuiKey_Keypad7; + case ALLEGRO_KEY_PAD_8: return ImGuiKey_Keypad8; + case ALLEGRO_KEY_PAD_9: return ImGuiKey_Keypad9; + case ALLEGRO_KEY_PAD_DELETE: return ImGuiKey_KeypadDecimal; + case ALLEGRO_KEY_PAD_SLASH: return ImGuiKey_KeypadDivide; + case ALLEGRO_KEY_PAD_ASTERISK: return ImGuiKey_KeypadMultiply; + case ALLEGRO_KEY_PAD_MINUS: return ImGuiKey_KeypadSubtract; + case ALLEGRO_KEY_PAD_PLUS: return ImGuiKey_KeypadAdd; + case ALLEGRO_KEY_PAD_ENTER: return ImGuiKey_KeypadEnter; + case ALLEGRO_KEY_PAD_EQUALS: return ImGuiKey_KeypadEqual; + case ALLEGRO_KEY_LSHIFT: return ImGuiKey_LeftShift; + case ALLEGRO_KEY_LCTRL: return ImGuiKey_LeftControl; + case ALLEGRO_KEY_ALT: return ImGuiKey_LeftAlt; + case ALLEGRO_KEY_LWIN: return ImGuiKey_LeftSuper; + case ALLEGRO_KEY_RSHIFT: return ImGuiKey_RightShift; + case ALLEGRO_KEY_RCTRL: return ImGuiKey_RightControl; + case ALLEGRO_KEY_ALTGR: return ImGuiKey_RightAlt; + case ALLEGRO_KEY_RWIN: return ImGuiKey_RightSuper; + case ALLEGRO_KEY_MENU: return ImGuiKey_Menu; + case ALLEGRO_KEY_0: return ImGuiKey_0; + case ALLEGRO_KEY_1: return ImGuiKey_1; + case ALLEGRO_KEY_2: return ImGuiKey_2; + case ALLEGRO_KEY_3: return ImGuiKey_3; + case ALLEGRO_KEY_4: return ImGuiKey_4; + case ALLEGRO_KEY_5: return ImGuiKey_5; + case ALLEGRO_KEY_6: return ImGuiKey_6; + case ALLEGRO_KEY_7: return ImGuiKey_7; + case ALLEGRO_KEY_8: return ImGuiKey_8; + case ALLEGRO_KEY_9: return ImGuiKey_9; + case ALLEGRO_KEY_A: return ImGuiKey_A; + case ALLEGRO_KEY_B: return ImGuiKey_B; + case ALLEGRO_KEY_C: return ImGuiKey_C; + case ALLEGRO_KEY_D: return ImGuiKey_D; + case ALLEGRO_KEY_E: return ImGuiKey_E; + case ALLEGRO_KEY_F: return ImGuiKey_F; + case ALLEGRO_KEY_G: return ImGuiKey_G; + case ALLEGRO_KEY_H: return ImGuiKey_H; + case ALLEGRO_KEY_I: return ImGuiKey_I; + case ALLEGRO_KEY_J: return ImGuiKey_J; + case ALLEGRO_KEY_K: return ImGuiKey_K; + case ALLEGRO_KEY_L: return ImGuiKey_L; + case ALLEGRO_KEY_M: return ImGuiKey_M; + case ALLEGRO_KEY_N: return ImGuiKey_N; + case ALLEGRO_KEY_O: return ImGuiKey_O; + case ALLEGRO_KEY_P: return ImGuiKey_P; + case ALLEGRO_KEY_Q: return ImGuiKey_Q; + case ALLEGRO_KEY_R: return ImGuiKey_R; + case ALLEGRO_KEY_S: return ImGuiKey_S; + case ALLEGRO_KEY_T: return ImGuiKey_T; + case ALLEGRO_KEY_U: return ImGuiKey_U; + case ALLEGRO_KEY_V: return ImGuiKey_V; + case ALLEGRO_KEY_W: return ImGuiKey_W; + case ALLEGRO_KEY_X: return ImGuiKey_X; + case ALLEGRO_KEY_Y: return ImGuiKey_Y; + case ALLEGRO_KEY_Z: return ImGuiKey_Z; + case ALLEGRO_KEY_F1: return ImGuiKey_F1; + case ALLEGRO_KEY_F2: return ImGuiKey_F2; + case ALLEGRO_KEY_F3: return ImGuiKey_F3; + case ALLEGRO_KEY_F4: return ImGuiKey_F4; + case ALLEGRO_KEY_F5: return ImGuiKey_F5; + case ALLEGRO_KEY_F6: return ImGuiKey_F6; + case ALLEGRO_KEY_F7: return ImGuiKey_F7; + case ALLEGRO_KEY_F8: return ImGuiKey_F8; + case ALLEGRO_KEY_F9: return ImGuiKey_F9; + case ALLEGRO_KEY_F10: return ImGuiKey_F10; + case ALLEGRO_KEY_F11: return ImGuiKey_F11; + case ALLEGRO_KEY_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} + bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) { ImGuiIO& io = ImGui::GetIO(); @@ -299,28 +414,6 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) }; bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); - io.KeyMap[ImGuiKey_Tab] = ALLEGRO_KEY_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = ALLEGRO_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = ALLEGRO_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = ALLEGRO_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = ALLEGRO_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = ALLEGRO_KEY_PGUP; - io.KeyMap[ImGuiKey_PageDown] = ALLEGRO_KEY_PGDN; - io.KeyMap[ImGuiKey_Home] = ALLEGRO_KEY_HOME; - io.KeyMap[ImGuiKey_End] = ALLEGRO_KEY_END; - io.KeyMap[ImGuiKey_Insert] = ALLEGRO_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = ALLEGRO_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = ALLEGRO_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = ALLEGRO_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = ALLEGRO_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = ALLEGRO_KEY_ESCAPE; - io.KeyMap[ImGuiKey_KeypadEnter] = ALLEGRO_KEY_PAD_ENTER; - io.KeyMap[ImGuiKey_A] = ALLEGRO_KEY_A; - io.KeyMap[ImGuiKey_C] = ALLEGRO_KEY_C; - io.KeyMap[ImGuiKey_V] = ALLEGRO_KEY_V; - io.KeyMap[ImGuiKey_X] = ALLEGRO_KEY_X; - io.KeyMap[ImGuiKey_Y] = ALLEGRO_KEY_Y; - io.KeyMap[ImGuiKey_Z] = ALLEGRO_KEY_Z; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); #if ALLEGRO_HAS_CLIPBOARD @@ -395,7 +488,11 @@ bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev) case ALLEGRO_EVENT_KEY_DOWN: case ALLEGRO_EVENT_KEY_UP: if (ev->keyboard.display == bd->Display) - io.KeysDown[ev->keyboard.keycode] = (ev->type == ALLEGRO_EVENT_KEY_DOWN); + { + ImGuiKey key = ImGui_ImplAllegro5_KeyCodeToImGuiKey(ev->keyboard.keycode); + io.AddKeyEvent(key, (ev->type == ALLEGRO_EVENT_KEY_DOWN)); + io.SetKeyEventNativeData(key, ev->keyboard.keycode, -1); // To support legacy indexing (<1.87 user code) + } return true; case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT: if (ev->display.source == bd->Display) diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h index 06c7120b..7e97969e 100644 --- a/backends/imgui_impl_allegro5.h +++ b/backends/imgui_impl_allegro5.h @@ -3,13 +3,14 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Clipboard support (from Allegro 5.1.12) // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // Issues: // [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. // [ ] Platform: Missing gamepad support. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs From da1864d79ea87951e6e1cbb49b0ca9a7b4b57eaa Mon Sep 17 00:00:00 2001 From: thedmd Date: Sun, 12 Dec 2021 12:02:20 +0100 Subject: [PATCH 244/332] Backends: GLUT: Update to use io.AddEventKey() will full key map (#2625, #4858) --- backends/imgui_impl_glut.cpp | 185 +++++++++++++++++++++++++---------- backends/imgui_impl_glut.h | 4 +- 2 files changed, 137 insertions(+), 52 deletions(-) diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 2964d801..ba399397 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -5,19 +5,22 @@ // !!! If someone or something is teaching you GLUT today, you are being abused. Please show some resistance. !!! // !!! Nowadays, prefer using GLFW or SDL instead! +// Implemented features: +// [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // Issues: // [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I // [ ] Platform: Missing mouse cursor shape/visibility support. // [ ] Platform: Missing clipboard support (not supported by Glut). // [ ] Platform: Missing gamepad support. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h. // 2019-03-25: Misc: Made io.DeltaTime always above zero. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. @@ -37,6 +40,120 @@ static int g_Time = 0; // Current time, in milliseconds +// Glut has 1 function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above. +static ImGuiKey ImGui_ImplGLUT_KeyToImGuiKey(int key) +{ + switch (key) + { + case '\t': return ImGuiKey_Tab; + case 256 + GLUT_KEY_LEFT: return ImGuiKey_LeftArrow; + case 256 + GLUT_KEY_RIGHT: return ImGuiKey_RightArrow; + case 256 + GLUT_KEY_UP: return ImGuiKey_UpArrow; + case 256 + GLUT_KEY_DOWN: return ImGuiKey_DownArrow; + case 256 + GLUT_KEY_PAGE_UP: return ImGuiKey_PageUp; + case 256 + GLUT_KEY_PAGE_DOWN: return ImGuiKey_PageDown; + case 256 + GLUT_KEY_HOME: return ImGuiKey_Home; + case 256 + GLUT_KEY_END: return ImGuiKey_End; + case 256 + GLUT_KEY_INSERT: return ImGuiKey_Insert; + case 127: return ImGuiKey_Delete; + case 8: return ImGuiKey_Backspace; + case ' ': return ImGuiKey_Space; + case 13: return ImGuiKey_Enter; + case 27: return ImGuiKey_Escape; + case 39: return ImGuiKey_Apostrophe; + case 44: return ImGuiKey_Comma; + case 45: return ImGuiKey_Minus; + case 46: return ImGuiKey_Period; + case 47: return ImGuiKey_Slash; + case 59: return ImGuiKey_Semicolon; + case 61: return ImGuiKey_Equal; + case 91: return ImGuiKey_LeftBracket; + case 92: return ImGuiKey_Backslash; + case 93: return ImGuiKey_RightBracket; + case 96: return ImGuiKey_GraveAccent; + //case 0: return ImGuiKey_CapsLock; + //case 0: return ImGuiKey_ScrollLock; + case 256 + 0x006D: return ImGuiKey_NumLock; + //case 0: return ImGuiKey_PrintScreen; + //case 0: return ImGuiKey_Pause; + //case '0': return ImGuiKey_Keypad0; + //case '1': return ImGuiKey_Keypad1; + //case '2': return ImGuiKey_Keypad2; + //case '3': return ImGuiKey_Keypad3; + //case '4': return ImGuiKey_Keypad4; + //case '5': return ImGuiKey_Keypad5; + //case '6': return ImGuiKey_Keypad6; + //case '7': return ImGuiKey_Keypad7; + //case '8': return ImGuiKey_Keypad8; + //case '9': return ImGuiKey_Keypad9; + //case 46: return ImGuiKey_KeypadDecimal; + //case 47: return ImGuiKey_KeypadDivide; + case 42: return ImGuiKey_KeypadMultiply; + //case 45: return ImGuiKey_KeypadSubtract; + case 43: return ImGuiKey_KeypadAdd; + //case 13: return ImGuiKey_KeypadEnter; + //case 0: return ImGuiKey_KeypadEqual; + case 256 + 0x0070: return ImGuiKey_LeftShift; + case 256 + 0x0072: return ImGuiKey_LeftControl; + case 256 + 0x0074: return ImGuiKey_LeftAlt; + //case 0: return ImGuiKey_LeftSuper; + case 256 + 0x0071: return ImGuiKey_RightShift; + case 256 + 0x0073: return ImGuiKey_RightControl; + case 256 + 0x0075: return ImGuiKey_RightAlt; + //case 0: return ImGuiKey_RightSuper; + //case 0: return ImGuiKey_Menu; + case '0': return ImGuiKey_0; + case '1': return ImGuiKey_1; + case '2': return ImGuiKey_2; + case '3': return ImGuiKey_3; + case '4': return ImGuiKey_4; + case '5': return ImGuiKey_5; + case '6': return ImGuiKey_6; + case '7': return ImGuiKey_7; + case '8': return ImGuiKey_8; + case '9': return ImGuiKey_9; + case 'A': case 'a': return ImGuiKey_A; + case 'B': case 'b': return ImGuiKey_B; + case 'C': case 'c': return ImGuiKey_C; + case 'D': case 'd': return ImGuiKey_D; + case 'E': case 'e': return ImGuiKey_E; + case 'F': case 'f': return ImGuiKey_F; + case 'G': case 'g': return ImGuiKey_G; + case 'H': case 'h': return ImGuiKey_H; + case 'I': case 'i': return ImGuiKey_I; + case 'J': case 'j': return ImGuiKey_J; + case 'K': case 'k': return ImGuiKey_K; + case 'L': case 'l': return ImGuiKey_L; + case 'M': case 'm': return ImGuiKey_M; + case 'N': case 'n': return ImGuiKey_N; + case 'O': case 'o': return ImGuiKey_O; + case 'P': case 'p': return ImGuiKey_P; + case 'Q': case 'q': return ImGuiKey_Q; + case 'R': case 'r': return ImGuiKey_R; + case 'S': case 's': return ImGuiKey_S; + case 'T': case 't': return ImGuiKey_T; + case 'U': case 'u': return ImGuiKey_U; + case 'V': case 'v': return ImGuiKey_V; + case 'W': case 'w': return ImGuiKey_W; + case 'X': case 'x': return ImGuiKey_X; + case 'Y': case 'y': return ImGuiKey_Y; + case 'Z': case 'z': return ImGuiKey_Z; + case 256 + GLUT_KEY_F1: return ImGuiKey_F1; + case 256 + GLUT_KEY_F2: return ImGuiKey_F2; + case 256 + GLUT_KEY_F3: return ImGuiKey_F3; + case 256 + GLUT_KEY_F4: return ImGuiKey_F4; + case 256 + GLUT_KEY_F5: return ImGuiKey_F5; + case 256 + GLUT_KEY_F6: return ImGuiKey_F6; + case 256 + GLUT_KEY_F7: return ImGuiKey_F7; + case 256 + GLUT_KEY_F8: return ImGuiKey_F8; + case 256 + GLUT_KEY_F9: return ImGuiKey_F9; + case 256 + GLUT_KEY_F10: return ImGuiKey_F10; + case 256 + GLUT_KEY_F11: return ImGuiKey_F11; + case 256 + GLUT_KEY_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} + bool ImGui_ImplGLUT_Init() { ImGuiIO& io = ImGui::GetIO(); @@ -46,33 +163,8 @@ bool ImGui_ImplGLUT_Init() #else io.BackendPlatformName = "imgui_impl_glut"; #endif - g_Time = 0; - // Glut has 1 function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above. - io.KeyMap[ImGuiKey_Tab] = '\t'; // == 9 == CTRL+I - io.KeyMap[ImGuiKey_LeftArrow] = 256 + GLUT_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = 256 + GLUT_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = 256 + GLUT_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = 256 + GLUT_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = 256 + GLUT_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = 256 + GLUT_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = 256 + GLUT_KEY_HOME; - io.KeyMap[ImGuiKey_End] = 256 + GLUT_KEY_END; - io.KeyMap[ImGuiKey_Insert] = 256 + GLUT_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = 127; - io.KeyMap[ImGuiKey_Backspace] = 8; // == CTRL+H - io.KeyMap[ImGuiKey_Space] = ' '; - io.KeyMap[ImGuiKey_Enter] = 13; // == CTRL+M - io.KeyMap[ImGuiKey_Escape] = 27; - io.KeyMap[ImGuiKey_KeypadEnter] = 13; // == CTRL+M - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - return true; } @@ -117,6 +209,14 @@ static void ImGui_ImplGLUT_UpdateKeyboardMods() io.KeyCtrl = (mods & GLUT_ACTIVE_CTRL) != 0; io.KeyShift = (mods & GLUT_ACTIVE_SHIFT) != 0; io.KeyAlt = (mods & GLUT_ACTIVE_ALT) != 0; + io.KeySuper = false; +} + +static void ImGui_ImplGLUT_AddKeyEvent(ImGuiKey key, bool down, int native_keycode) +{ + ImGuiIO& io = ImGui::GetIO(); + io.AddKeyEvent(key, down); + io.SetKeyEventNativeData(key, native_keycode, -1); // To support legacy indexing (<1.87 user code) } void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y) @@ -127,16 +227,8 @@ void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y) if (c >= 32) io.AddInputCharacter((unsigned int)c); - // Store letters in KeysDown[] array as both uppercase and lowercase + Handle GLUT translating CTRL+A..CTRL+Z as 1..26. - // This is a hacky mess but GLUT is unable to distinguish e.g. a TAB key from CTRL+I so this is probably the best we can do here. - if (c >= 1 && c <= 26) - io.KeysDown[c] = io.KeysDown[c - 1 + 'a'] = io.KeysDown[c - 1 + 'A'] = true; - else if (c >= 'a' && c <= 'z') - io.KeysDown[c] = io.KeysDown[c - 'a' + 'A'] = true; - else if (c >= 'A' && c <= 'Z') - io.KeysDown[c] = io.KeysDown[c - 'A' + 'a'] = true; - else - io.KeysDown[c] = true; + ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c); + ImGui_ImplGLUT_AddKeyEvent(key, true, c); ImGui_ImplGLUT_UpdateKeyboardMods(); (void)x; (void)y; // Unused } @@ -144,15 +236,8 @@ void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y) void ImGui_ImplGLUT_KeyboardUpFunc(unsigned char c, int x, int y) { //printf("char_up_func %d '%c'\n", c, c); - ImGuiIO& io = ImGui::GetIO(); - if (c >= 1 && c <= 26) - io.KeysDown[c] = io.KeysDown[c - 1 + 'a'] = io.KeysDown[c - 1 + 'A'] = false; - else if (c >= 'a' && c <= 'z') - io.KeysDown[c] = io.KeysDown[c - 'a' + 'A'] = false; - else if (c >= 'A' && c <= 'Z') - io.KeysDown[c] = io.KeysDown[c - 'A' + 'a'] = false; - else - io.KeysDown[c] = false; + ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c); + ImGui_ImplGLUT_AddKeyEvent(key, false, c); ImGui_ImplGLUT_UpdateKeyboardMods(); (void)x; (void)y; // Unused } @@ -160,9 +245,8 @@ void ImGui_ImplGLUT_KeyboardUpFunc(unsigned char c, int x, int y) void ImGui_ImplGLUT_SpecialFunc(int key, int x, int y) { //printf("key_down_func %d\n", key); - ImGuiIO& io = ImGui::GetIO(); - if (key + 256 < IM_ARRAYSIZE(io.KeysDown)) - io.KeysDown[key + 256] = true; + ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256); + ImGui_ImplGLUT_AddKeyEvent(imgui_key, true, key + 256); ImGui_ImplGLUT_UpdateKeyboardMods(); (void)x; (void)y; // Unused } @@ -170,9 +254,8 @@ void ImGui_ImplGLUT_SpecialFunc(int key, int x, int y) void ImGui_ImplGLUT_SpecialUpFunc(int key, int x, int y) { //printf("key_up_func %d\n", key); - ImGuiIO& io = ImGui::GetIO(); - if (key + 256 < IM_ARRAYSIZE(io.KeysDown)) - io.KeysDown[key + 256] = false; + ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256); + ImGui_ImplGLUT_AddKeyEvent(imgui_key, false, key + 256); ImGui_ImplGLUT_UpdateKeyboardMods(); (void)x; (void)y; // Unused } diff --git a/backends/imgui_impl_glut.h b/backends/imgui_impl_glut.h index 96c779fd..98d4e598 100644 --- a/backends/imgui_impl_glut.h +++ b/backends/imgui_impl_glut.h @@ -5,13 +5,15 @@ // !!! If someone or something is teaching you GLUT today, you are being abused. Please show some resistance. !!! // !!! Nowadays, prefer using GLFW or SDL instead! +// Implemented features: +// [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // Issues: // [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I // [ ] Platform: Missing mouse cursor shape/visibility support. // [ ] Platform: Missing clipboard support (not supported by Glut). // [ ] Platform: Missing gamepad support. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs From ee436aa8039f2b86bb08a554b737db4f7ab1f8b4 Mon Sep 17 00:00:00 2001 From: thedmd Date: Sun, 12 Dec 2021 12:04:13 +0100 Subject: [PATCH 245/332] Backends: OSX: Update to use io.AddEventKey() will full key map (#2625, #1873, #4858) --- backends/imgui_impl_osx.h | 2 +- backends/imgui_impl_osx.mm | 285 ++++++++++++++++++------- examples/example_apple_metal/main.mm | 15 +- examples/example_apple_opengl2/main.mm | 9 - 4 files changed, 207 insertions(+), 104 deletions(-) diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index 6cfe7eb2..37851a3c 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -4,9 +4,9 @@ // Implemented features: // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space). // 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. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 9acd8baa..64a45efb 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -4,9 +4,9 @@ // Implemented features: // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space). // 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. @@ -22,6 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. @@ -50,6 +51,7 @@ static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; static bool g_MouseCursorHidden = false; static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; +static ImGuiKeyModFlags g_KeyModifiers = ImGuiKeyModFlags_None; static ImFocusObserver* g_FocusObserver = nil; static KeyEventResponder* g_KeyEventResponder = nil; @@ -73,13 +75,6 @@ static double GetMachAbsoluteTimeInSeconds() return (double)mach_absolute_time() * g_HostClockPeriod; } -static void resetKeys() -{ - ImGuiIO& io = ImGui::GetIO(); - memset(io.KeysDown, 0, sizeof(io.KeysDown)); - io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; -} - /** KeyEventResponder implements the NSTextInputClient protocol as is required by the macOS text input manager. @@ -105,10 +100,17 @@ static void resetKeys() - (void)keyDown:(NSEvent*)event { + ImGui_ImplOSX_HandleEvent(event, self); + // Call to the macOS input manager system. [self interpretKeyEvents:@[event]]; } +- (void)keyUp:(NSEvent*)event +{ + ImGui_ImplOSX_HandleEvent(event, self); +} + - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { ImGuiIO& io = ImGui::GetIO(); @@ -195,16 +197,134 @@ static void resetKeys() { ImGuiIO& io = ImGui::GetIO(); io.AddFocusEvent(false); - - // Unfocused applications do not receive input events, therefore we must manually - // release any pressed keys when application loses focus, otherwise they would remain - // stuck in a pressed state. https://github.com/ocornut/imgui/issues/3832 - resetKeys(); } @end // Functions +static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code) +{ + switch (key_code) + { + case kVK_ANSI_A: return ImGuiKey_A; + case kVK_ANSI_S: return ImGuiKey_S; + case kVK_ANSI_D: return ImGuiKey_D; + case kVK_ANSI_F: return ImGuiKey_F; + case kVK_ANSI_H: return ImGuiKey_H; + case kVK_ANSI_G: return ImGuiKey_G; + case kVK_ANSI_Z: return ImGuiKey_Z; + case kVK_ANSI_X: return ImGuiKey_X; + case kVK_ANSI_C: return ImGuiKey_C; + case kVK_ANSI_V: return ImGuiKey_V; + case kVK_ANSI_B: return ImGuiKey_B; + case kVK_ANSI_Q: return ImGuiKey_Q; + case kVK_ANSI_W: return ImGuiKey_W; + case kVK_ANSI_E: return ImGuiKey_E; + case kVK_ANSI_R: return ImGuiKey_R; + case kVK_ANSI_Y: return ImGuiKey_Y; + case kVK_ANSI_T: return ImGuiKey_T; + case kVK_ANSI_1: return ImGuiKey_1; + case kVK_ANSI_2: return ImGuiKey_2; + case kVK_ANSI_3: return ImGuiKey_3; + case kVK_ANSI_4: return ImGuiKey_4; + case kVK_ANSI_6: return ImGuiKey_6; + case kVK_ANSI_5: return ImGuiKey_5; + case kVK_ANSI_Equal: return ImGuiKey_Equal; + case kVK_ANSI_9: return ImGuiKey_9; + case kVK_ANSI_7: return ImGuiKey_7; + case kVK_ANSI_Minus: return ImGuiKey_Minus; + case kVK_ANSI_8: return ImGuiKey_8; + case kVK_ANSI_0: return ImGuiKey_0; + case kVK_ANSI_RightBracket: return ImGuiKey_RightBracket; + case kVK_ANSI_O: return ImGuiKey_O; + case kVK_ANSI_U: return ImGuiKey_U; + case kVK_ANSI_LeftBracket: return ImGuiKey_LeftBracket; + case kVK_ANSI_I: return ImGuiKey_I; + case kVK_ANSI_P: return ImGuiKey_P; + case kVK_ANSI_L: return ImGuiKey_L; + case kVK_ANSI_J: return ImGuiKey_J; + case kVK_ANSI_Quote: return ImGuiKey_Apostrophe; + case kVK_ANSI_K: return ImGuiKey_K; + case kVK_ANSI_Semicolon: return ImGuiKey_Semicolon; + case kVK_ANSI_Backslash: return ImGuiKey_Backslash; + case kVK_ANSI_Comma: return ImGuiKey_Comma; + case kVK_ANSI_Slash: return ImGuiKey_Slash; + case kVK_ANSI_N: return ImGuiKey_N; + case kVK_ANSI_M: return ImGuiKey_M; + case kVK_ANSI_Period: return ImGuiKey_Period; + case kVK_ANSI_Grave: return ImGuiKey_GraveAccent; + case kVK_ANSI_KeypadDecimal: return ImGuiKey_KeypadDecimal; + case kVK_ANSI_KeypadMultiply: return ImGuiKey_KeypadMultiply; + case kVK_ANSI_KeypadPlus: return ImGuiKey_KeypadAdd; + case kVK_ANSI_KeypadClear: return ImGuiKey_NumLock; + case kVK_ANSI_KeypadDivide: return ImGuiKey_KeypadDivide; + case kVK_ANSI_KeypadEnter: return ImGuiKey_KeypadEnter; + case kVK_ANSI_KeypadMinus: return ImGuiKey_KeypadSubtract; + case kVK_ANSI_KeypadEquals: return ImGuiKey_KeypadEqual; + case kVK_ANSI_Keypad0: return ImGuiKey_Keypad0; + case kVK_ANSI_Keypad1: return ImGuiKey_Keypad1; + case kVK_ANSI_Keypad2: return ImGuiKey_Keypad2; + case kVK_ANSI_Keypad3: return ImGuiKey_Keypad3; + case kVK_ANSI_Keypad4: return ImGuiKey_Keypad4; + case kVK_ANSI_Keypad5: return ImGuiKey_Keypad5; + case kVK_ANSI_Keypad6: return ImGuiKey_Keypad6; + case kVK_ANSI_Keypad7: return ImGuiKey_Keypad7; + case kVK_ANSI_Keypad8: return ImGuiKey_Keypad8; + case kVK_ANSI_Keypad9: return ImGuiKey_Keypad9; + case kVK_Return: return ImGuiKey_Enter; + case kVK_Tab: return ImGuiKey_Tab; + case kVK_Space: return ImGuiKey_Space; + case kVK_Delete: return ImGuiKey_Backspace; + case kVK_Escape: return ImGuiKey_Escape; + case kVK_Command: return ImGuiKey_LeftSuper; + case kVK_Shift: return ImGuiKey_LeftShift; + case kVK_CapsLock: return ImGuiKey_CapsLock; + case kVK_Option: return ImGuiKey_LeftAlt; + case kVK_Control: return ImGuiKey_LeftControl; + case kVK_RightCommand: return ImGuiKey_RightSuper; + case kVK_RightShift: return ImGuiKey_RightShift; + case kVK_RightOption: return ImGuiKey_RightAlt; + case kVK_RightControl: return ImGuiKey_RightControl; +// case kVK_Function: return ImGuiKey_; +// case kVK_F17: return ImGuiKey_; +// case kVK_VolumeUp: return ImGuiKey_; +// case kVK_VolumeDown: return ImGuiKey_; +// case kVK_Mute: return ImGuiKey_; +// case kVK_F18: return ImGuiKey_; +// case kVK_F19: return ImGuiKey_; +// case kVK_F20: return ImGuiKey_; + case kVK_F5: return ImGuiKey_F5; + case kVK_F6: return ImGuiKey_F6; + case kVK_F7: return ImGuiKey_F7; + case kVK_F3: return ImGuiKey_F3; + case kVK_F8: return ImGuiKey_F8; + case kVK_F9: return ImGuiKey_F9; + case kVK_F11: return ImGuiKey_F11; + case kVK_F13: return ImGuiKey_PrintScreen; +// case kVK_F16: return ImGuiKey_; +// case kVK_F14: return ImGuiKey_; + case kVK_F10: return ImGuiKey_F10; + case 0x6E: return ImGuiKey_Menu; + case kVK_F12: return ImGuiKey_F12; +// case kVK_F15: return ImGuiKey_; + case kVK_Help: return ImGuiKey_Insert; + case kVK_Home: return ImGuiKey_Home; + case kVK_PageUp: return ImGuiKey_PageUp; + case kVK_ForwardDelete: return ImGuiKey_Delete; + case kVK_F4: return ImGuiKey_F4; + case kVK_End: return ImGuiKey_End; + case kVK_F2: return ImGuiKey_F2; + case kVK_PageDown: return ImGuiKey_PageDown; + case kVK_F1: return ImGuiKey_F1; + case kVK_LeftArrow: return ImGuiKey_LeftArrow; + case kVK_RightArrow: return ImGuiKey_RightArrow; + case kVK_DownArrow: return ImGuiKey_DownArrow; + case kVK_UpArrow: return ImGuiKey_UpArrow; + default: return ImGuiKey_None; + } +} + + bool ImGui_ImplOSX_Init(NSView* view) { ImGuiIO& io = ImGui::GetIO(); @@ -216,30 +336,6 @@ bool ImGui_ImplOSX_Init(NSView* view) //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) io.BackendPlatformName = "imgui_impl_osx"; - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array. - io.KeyMap[ImGuiKey_Tab] = kVK_Tab; - io.KeyMap[ImGuiKey_LeftArrow] = kVK_LeftArrow; - io.KeyMap[ImGuiKey_RightArrow] = kVK_RightArrow; - io.KeyMap[ImGuiKey_UpArrow] = kVK_UpArrow; - io.KeyMap[ImGuiKey_DownArrow] = kVK_DownArrow; - io.KeyMap[ImGuiKey_PageUp] = kVK_PageUp; - io.KeyMap[ImGuiKey_PageDown] = kVK_PageDown; - io.KeyMap[ImGuiKey_Home] = kVK_Home; - io.KeyMap[ImGuiKey_End] = kVK_End; - io.KeyMap[ImGuiKey_Insert] = kVK_F13; - io.KeyMap[ImGuiKey_Delete] = kVK_ForwardDelete; - io.KeyMap[ImGuiKey_Backspace] = kVK_Delete; - io.KeyMap[ImGuiKey_Space] = kVK_Space; - io.KeyMap[ImGuiKey_Enter] = kVK_Return; - io.KeyMap[ImGuiKey_Escape] = kVK_Escape; - io.KeyMap[ImGuiKey_KeypadEnter] = kVK_ANSI_KeypadEnter; - io.KeyMap[ImGuiKey_A] = kVK_ANSI_A; - io.KeyMap[ImGuiKey_C] = kVK_ANSI_C; - io.KeyMap[ImGuiKey_V] = kVK_ANSI_V; - io.KeyMap[ImGuiKey_X] = kVK_ANSI_X; - io.KeyMap[ImGuiKey_Y] = kVK_ANSI_Y; - io.KeyMap[ImGuiKey_Z] = kVK_ANSI_Z; - // Load cursors. Some of them are undocumented. g_MouseCursorHidden = false; g_MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor]; @@ -296,6 +392,15 @@ bool ImGui_ImplOSX_Init(NSView* view) g_KeyEventResponder = [[KeyEventResponder alloc] initWithFrame:NSZeroRect]; [view addSubview:g_KeyEventResponder]; + // Some events do not raise callbacks of AppView in some circumstances (for example when CMD key is held down). + // This monitor taps into global event stream and captures these events. + NSEventMask eventMask = NSEventMaskFlagsChanged; + [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) + { + ImGui_ImplOSX_HandleEvent(event, g_KeyEventResponder); + return event; + }]; + return true; } @@ -344,7 +449,7 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() } } -void ImGui_ImplOSX_UpdateGamepads() +static void ImGui_ImplOSX_UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); memset(io.NavInputs, 0, sizeof(io.NavInputs)); @@ -388,6 +493,15 @@ void ImGui_ImplOSX_UpdateGamepads() io.BackendFlags |= ImGuiBackendFlags_HasGamepad; } +static void ImGui_ImplOSX_UpdateKeyModifiers() +{ + ImGuiIO& io = ImGui::GetIO(); + io.KeyCtrl = (g_KeyModifiers & ImGuiKeyModFlags_Ctrl) != 0; + io.KeyShift = (g_KeyModifiers & ImGuiKeyModFlags_Shift) != 0; + io.KeyAlt = (g_KeyModifiers & ImGuiKeyModFlags_Alt) != 0; + io.KeySuper = (g_KeyModifiers & ImGuiKeyModFlags_Super) != 0; +} + void ImGui_ImplOSX_NewFrame(NSView* view) { // Setup display size @@ -409,28 +523,11 @@ void ImGui_ImplOSX_NewFrame(NSView* view) io.DeltaTime = (float)(current_time - g_Time); g_Time = current_time; + ImGui_ImplOSX_UpdateKeyModifiers(); ImGui_ImplOSX_UpdateMouseCursorAndButtons(); ImGui_ImplOSX_UpdateGamepads(); } -NSString* NSStringFromPhase(NSEventPhase phase) -{ - static NSString* strings[] = - { - @"none", - @"began", - @"stationary", - @"changed", - @"ended", - @"cancelled", - @"mayBegin", - }; - - int pos = phase == NSEventPhaseNone ? 0 : __builtin_ctzl((NSUInteger)phase) + 1; - - return strings[pos]; -} - bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { ImGuiIO& io = ImGui::GetIO(); @@ -497,8 +594,6 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) wheel_dy = [event deltaY]; } - //NSLog(@"dx=%0.3ff, dy=%0.3f, phase=%@", wheel_dx, wheel_dy, NSStringFromPhase(event.phase)); - if (fabs(wheel_dx) > 0.0) io.MouseWheelH += (float)wheel_dx * 0.1f; if (fabs(wheel_dy) > 0.0) @@ -508,35 +603,65 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) { - unsigned short code = event.keyCode; - IM_ASSERT(code >= 0 && code < IM_ARRAYSIZE(io.KeysDown)); - io.KeysDown[code] = event.type == NSEventTypeKeyDown; - NSEventModifierFlags flags = event.modifierFlags; - io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0; - io.KeyShift = (flags & NSEventModifierFlagShift) != 0; - io.KeyAlt = (flags & NSEventModifierFlagOption) != 0; - io.KeySuper = (flags & NSEventModifierFlagCommand) != 0; + if ([event isARepeat]) + return io.WantCaptureKeyboard; + + ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); + io.AddKeyEvent(key, event.type == NSEventTypeKeyDown); + io.SetKeyEventNativeData(key, (int)[event keyCode], -1); // To support legacy indexing (<1.87 user code) + return io.WantCaptureKeyboard; } if (event.type == NSEventTypeFlagsChanged) { - NSEventModifierFlags flags = event.modifierFlags; - switch (event.keyCode) + unsigned short key_code = [event keyCode]; + unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; + + ImGuiKeyModFlags imgui_flags = ImGuiKeyModFlags_None; + if (flags & NSEventModifierFlagShift) + imgui_flags |= ImGuiKeyModFlags_Shift; + if (flags & NSEventModifierFlagControl) + imgui_flags |= ImGuiKeyModFlags_Ctrl; + if (flags & NSEventModifierFlagOption) + imgui_flags |= ImGuiKeyModFlags_Alt; + if (flags & NSEventModifierFlagCommand) + imgui_flags |= ImGuiKeyModFlags_Super; + + g_KeyModifiers = imgui_flags; + + ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); + if (key != ImGuiKey_None) { - case kVK_Control: - io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0; - break; - case kVK_Shift: - io.KeyShift = (flags & NSEventModifierFlagShift) != 0; - break; - case kVK_Option: - io.KeyAlt = (flags & NSEventModifierFlagOption) != 0; - break; - case kVK_Command: - io.KeySuper = (flags & NSEventModifierFlagCommand) != 0; - break; + // macOS does not generate down/up event for modifiers. We're trying + // to use hardware dependent masks to extract that information. + // 'imgui_mask' is left as a fallback. + NSEventModifierFlags mask = 0; + ImGuiKeyModFlags imgui_mask = ImGuiKeyModFlags_None; + switch (key_code) + { + case kVK_Control: mask = 0x0001; imgui_mask = ImGuiKeyModFlags_Ctrl; break; + case kVK_RightControl: mask = 0x2000; imgui_mask = ImGuiKeyModFlags_Ctrl; break; + case kVK_Shift: mask = 0x0002; imgui_mask = ImGuiKeyModFlags_Shift; break; + case kVK_RightShift: mask = 0x0004; imgui_mask = ImGuiKeyModFlags_Shift; break; + case kVK_Command: mask = 0x0008; imgui_mask = ImGuiKeyModFlags_Super; break; + case kVK_RightCommand: mask = 0x0010; imgui_mask = ImGuiKeyModFlags_Super; break; + case kVK_Option: mask = 0x0020; imgui_mask = ImGuiKeyModFlags_Alt; break; + case kVK_RightOption: mask = 0x0040; imgui_mask = ImGuiKeyModFlags_Alt; break; + } + + if (mask) + { + NSEventModifierFlags modifier_flags = [event modifierFlags]; + io.AddKeyEvent(key, (modifier_flags & mask) != 0); + } + else if (imgui_mask) + { + io.AddKeyEvent(key, (imgui_flags & imgui_mask) != 0); + } + io.SetKeyEventNativeData(key, keycode, -1); // To support legacy indexing (<1.87 user code) } + return io.WantCaptureKeyboard; } diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index bbe51d31..5c05fee5 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -107,18 +107,6 @@ userInfo:nil]; [self.view addTrackingArea:trackingArea]; - // If we want to receive key events, we either need to be in the responder chain of the key view, - // or else we can install a local monitor. The consequence of this heavy-handed approach is that - // we receive events for all controls, not just Dear ImGui widgets. If we had native controls in our - // window, we'd want to be much more careful than just ingesting the complete event stream. - // To match the behavior of other backends, we pass every event down to the OS. - NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged; - [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) - { - ImGui_ImplOSX_HandleEvent(event, self.view); - return event; - }]; - ImGui_ImplOSX_Init(self.view); #endif @@ -223,8 +211,7 @@ #if TARGET_OS_OSX -// Forward Mouse/Keyboard events to Dear ImGui OSX backend. -// Other events are registered via addLocalMonitorForEventsMatchingMask() +// Forward Mouse events to Dear ImGui OSX backend. -(void)mouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } -(void)rightMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } -(void)otherMouseDown:(NSEvent *)event { ImGui_ImplOSX_HandleEvent(event, self.view); } diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index f859162e..482a94f5 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -36,15 +36,6 @@ -(void)initialize { - // Some events do not raise callbacks of AppView in some circumstances (for example when CMD key is held down). - // This monitor taps into global event stream and captures these events. - NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged; - [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) - { - ImGui_ImplOSX_HandleEvent(event, self); - return event; - }]; - // Setup Dear ImGui context // FIXME: This example doesn't have proper cleanup... IMGUI_CHECKVERSION(); From 1797135db50825c8b9fe0e97da8b7e51f9cc3cee Mon Sep 17 00:00:00 2001 From: thedmd Date: Thu, 6 Jan 2022 19:57:34 +0100 Subject: [PATCH 246/332] Backends: Android: Update to use io.AddEventKey() will full key map (#2625, #4858) --- backends/imgui_impl_android.cpp | 192 ++++++++++++++++++++++++++------ backends/imgui_impl_android.h | 5 +- 2 files changed, 164 insertions(+), 33 deletions(-) diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 6a454a4e..7a09e017 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -2,12 +2,13 @@ // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) // Implemented features: -// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // Missing features: // [ ] Platform: Clipboard support. // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. // Important: +// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) @@ -18,6 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. #include "imgui.h" @@ -30,11 +32,135 @@ #include #include +struct KeyEvent +{ + ImGuiKey Key; + bool Down; + int NativeKeycode; + int NativeScancode; + + KeyEvent(): Key(ImGuiKey_None), Down(false), NativeKeycode(-1), NativeScancode(-1) {} +}; + // Android data static double g_Time = 0.0; static ANativeWindow* g_Window; static char g_LogTag[] = "ImGuiExample"; -static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. +static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. +static ImGuiKeyModFlags g_KeyModFlags = ImGuiKeyModFlags_None; + +static ImGuiKey ImGui_ImplAndroid_KeyCodeToImGuiKey(int32_t key_code) +{ + switch (key_code) + { + case AKEYCODE_TAB: return ImGuiKey_Tab; + case AKEYCODE_DPAD_LEFT: return ImGuiKey_LeftArrow; + case AKEYCODE_DPAD_RIGHT: return ImGuiKey_RightArrow; + case AKEYCODE_DPAD_UP: return ImGuiKey_UpArrow; + case AKEYCODE_DPAD_DOWN: return ImGuiKey_DownArrow; + case AKEYCODE_PAGE_UP: return ImGuiKey_PageUp; + case AKEYCODE_PAGE_DOWN: return ImGuiKey_PageDown; + case AKEYCODE_MOVE_HOME: return ImGuiKey_Home; + case AKEYCODE_MOVE_END: return ImGuiKey_End; + case AKEYCODE_INSERT: return ImGuiKey_Insert; + case AKEYCODE_FORWARD_DEL: return ImGuiKey_Delete; + case AKEYCODE_DEL: return ImGuiKey_Backspace; + case AKEYCODE_SPACE: return ImGuiKey_Space; + case AKEYCODE_ENTER: return ImGuiKey_Enter; + case AKEYCODE_ESCAPE: return ImGuiKey_Escape; + case AKEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case AKEYCODE_COMMA: return ImGuiKey_Comma; + case AKEYCODE_MINUS: return ImGuiKey_Minus; + case AKEYCODE_PERIOD: return ImGuiKey_Period; + case AKEYCODE_SLASH: return ImGuiKey_Slash; + case AKEYCODE_SEMICOLON: return ImGuiKey_Semicolon; + case AKEYCODE_EQUALS: return ImGuiKey_Equal; + case AKEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case AKEYCODE_BACKSLASH: return ImGuiKey_Backslash; + case AKEYCODE_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case AKEYCODE_GRAVE: return ImGuiKey_GraveAccent; + case AKEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock; + case AKEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case AKEYCODE_NUM_LOCK: return ImGuiKey_NumLock; + case AKEYCODE_SYSRQ: return ImGuiKey_PrintScreen; + case AKEYCODE_BREAK: return ImGuiKey_Pause; + case AKEYCODE_NUMPAD_0: return ImGuiKey_Keypad0; + case AKEYCODE_NUMPAD_1: return ImGuiKey_Keypad1; + case AKEYCODE_NUMPAD_2: return ImGuiKey_Keypad2; + case AKEYCODE_NUMPAD_3: return ImGuiKey_Keypad3; + case AKEYCODE_NUMPAD_4: return ImGuiKey_Keypad4; + case AKEYCODE_NUMPAD_5: return ImGuiKey_Keypad5; + case AKEYCODE_NUMPAD_6: return ImGuiKey_Keypad6; + case AKEYCODE_NUMPAD_7: return ImGuiKey_Keypad7; + case AKEYCODE_NUMPAD_8: return ImGuiKey_Keypad8; + case AKEYCODE_NUMPAD_9: return ImGuiKey_Keypad9; + case AKEYCODE_NUMPAD_DOT: return ImGuiKey_KeypadDecimal; + case AKEYCODE_NUMPAD_DIVIDE: return ImGuiKey_KeypadDivide; + case AKEYCODE_NUMPAD_MULTIPLY: return ImGuiKey_KeypadMultiply; + case AKEYCODE_NUMPAD_SUBTRACT: return ImGuiKey_KeypadSubtract; + case AKEYCODE_NUMPAD_ADD: return ImGuiKey_KeypadAdd; + case AKEYCODE_NUMPAD_ENTER: return ImGuiKey_KeypadEnter; + case AKEYCODE_NUMPAD_EQUALS: return ImGuiKey_KeypadEqual; + case AKEYCODE_SHIFT_LEFT: return ImGuiKey_LeftShift; + case AKEYCODE_CTRL_LEFT: return ImGuiKey_LeftControl; + case AKEYCODE_ALT_LEFT: return ImGuiKey_LeftAlt; + case AKEYCODE_META_LEFT: return ImGuiKey_LeftSuper; + case AKEYCODE_SHIFT_RIGHT: return ImGuiKey_RightShift; + case AKEYCODE_CTRL_RIGHT: return ImGuiKey_RightControl; + case AKEYCODE_ALT_RIGHT: return ImGuiKey_RightAlt; + case AKEYCODE_META_RIGHT: return ImGuiKey_RightSuper; + case AKEYCODE_MENU: return ImGuiKey_Menu; + case AKEYCODE_0: return ImGuiKey_0; + case AKEYCODE_1: return ImGuiKey_1; + case AKEYCODE_2: return ImGuiKey_2; + case AKEYCODE_3: return ImGuiKey_3; + case AKEYCODE_4: return ImGuiKey_4; + case AKEYCODE_5: return ImGuiKey_5; + case AKEYCODE_6: return ImGuiKey_6; + case AKEYCODE_7: return ImGuiKey_7; + case AKEYCODE_8: return ImGuiKey_8; + case AKEYCODE_9: return ImGuiKey_9; + case AKEYCODE_A: return ImGuiKey_A; + case AKEYCODE_B: return ImGuiKey_B; + case AKEYCODE_C: return ImGuiKey_C; + case AKEYCODE_D: return ImGuiKey_D; + case AKEYCODE_E: return ImGuiKey_E; + case AKEYCODE_F: return ImGuiKey_F; + case AKEYCODE_G: return ImGuiKey_G; + case AKEYCODE_H: return ImGuiKey_H; + case AKEYCODE_I: return ImGuiKey_I; + case AKEYCODE_J: return ImGuiKey_J; + case AKEYCODE_K: return ImGuiKey_K; + case AKEYCODE_L: return ImGuiKey_L; + case AKEYCODE_M: return ImGuiKey_M; + case AKEYCODE_N: return ImGuiKey_N; + case AKEYCODE_O: return ImGuiKey_O; + case AKEYCODE_P: return ImGuiKey_P; + case AKEYCODE_Q: return ImGuiKey_Q; + case AKEYCODE_R: return ImGuiKey_R; + case AKEYCODE_S: return ImGuiKey_S; + case AKEYCODE_T: return ImGuiKey_T; + case AKEYCODE_U: return ImGuiKey_U; + case AKEYCODE_V: return ImGuiKey_V; + case AKEYCODE_W: return ImGuiKey_W; + case AKEYCODE_X: return ImGuiKey_X; + case AKEYCODE_Y: return ImGuiKey_Y; + case AKEYCODE_Z: return ImGuiKey_Z; + case AKEYCODE_F1: return ImGuiKey_F1; + case AKEYCODE_F2: return ImGuiKey_F2; + case AKEYCODE_F3: return ImGuiKey_F3; + case AKEYCODE_F4: return ImGuiKey_F4; + case AKEYCODE_F5: return ImGuiKey_F5; + case AKEYCODE_F6: return ImGuiKey_F6; + case AKEYCODE_F7: return ImGuiKey_F7; + case AKEYCODE_F8: return ImGuiKey_F8; + case AKEYCODE_F9: return ImGuiKey_F9; + case AKEYCODE_F10: return ImGuiKey_F10; + case AKEYCODE_F11: return ImGuiKey_F11; + case AKEYCODE_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) { @@ -45,12 +171,19 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) case AINPUT_EVENT_TYPE_KEY: { int32_t event_key_code = AKeyEvent_getKeyCode(input_event); + int32_t event_scan_code = AKeyEvent_getScanCode(input_event); int32_t event_action = AKeyEvent_getAction(input_event); int32_t event_meta_state = AKeyEvent_getMetaState(input_event); - io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0); - io.KeyShift = ((event_meta_state & AMETA_SHIFT_ON) != 0); - io.KeyAlt = ((event_meta_state & AMETA_ALT_ON) != 0); + g_KeyModFlags = ImGuiKeyModFlags_None; + if ((event_meta_state & AMETA_CTRL_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Ctrl; + if ((event_meta_state & AMETA_SHIFT_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Shift; + if ((event_meta_state & AMETA_ALT_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Alt; + if ((event_meta_state & AMETA_META_ON) != 0) + g_KeyModFlags |= ImGuiKeyModFlags_Super; switch (event_action) { @@ -59,8 +192,21 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) // ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787 case AKEY_EVENT_ACTION_DOWN: case AKEY_EVENT_ACTION_UP: - g_KeyEventQueues[event_key_code].push(event_action); + { + ImGuiKey key = ImGui_ImplAndroid_KeyCodeToImGuiKey(event_key_code); + if (key != ImGuiKey_None && (event_action == AKEY_EVENT_ACTION_DOWN || event_action == AKEY_EVENT_ACTION_UP)) + { + KeyEvent io_event; + io_event.Key = key; + io_event.Down = event_action == AKEY_EVENT_ACTION_DOWN; + io_event.NativeKeycode = event_key_code; + io_event.NativeScancode = event_scan_code; + + g_KeyEventQueues[key].push(io_event); + } + break; + } default: break; } @@ -123,30 +269,6 @@ bool ImGui_ImplAndroid_Init(ANativeWindow* window) ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = "imgui_impl_android"; - // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. - io.KeyMap[ImGuiKey_Tab] = AKEYCODE_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_RightArrow] = AKEYCODE_DPAD_RIGHT; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_UpArrow] = AKEYCODE_DPAD_UP; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_DownArrow] = AKEYCODE_DPAD_DOWN; // also covers physical keyboard arrow key - io.KeyMap[ImGuiKey_PageUp] = AKEYCODE_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = AKEYCODE_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = AKEYCODE_MOVE_HOME; - io.KeyMap[ImGuiKey_End] = AKEYCODE_MOVE_END; - io.KeyMap[ImGuiKey_Insert] = AKEYCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = AKEYCODE_FORWARD_DEL; - io.KeyMap[ImGuiKey_Backspace] = AKEYCODE_DEL; - io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE; - io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER; - io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE; - io.KeyMap[ImGuiKey_KeypadEnter] = AKEYCODE_NUMPAD_ENTER; - io.KeyMap[ImGuiKey_A] = AKEYCODE_A; - io.KeyMap[ImGuiKey_C] = AKEYCODE_C; - io.KeyMap[ImGuiKey_V] = AKEYCODE_V; - io.KeyMap[ImGuiKey_X] = AKEYCODE_X; - io.KeyMap[ImGuiKey_Y] = AKEYCODE_Y; - io.KeyMap[ImGuiKey_Z] = AKEYCODE_Z; - return true; } @@ -164,10 +286,18 @@ void ImGui_ImplAndroid_NewFrame() { if (key_queue.second.empty()) continue; - io.KeysDown[key_queue.first] = (key_queue.second.front() == AKEY_EVENT_ACTION_DOWN); + + auto& key_event = key_queue.second.front(); + io.AddKeyEvent(key_event.Key, key_event.Down); + io.SetKeyEventNativeData(key_event.Key, key_event.NativeKeycode, key_event.NativeScancode); // To support legacy indexing (<1.87 user code) key_queue.second.pop(); } + io.KeyCtrl = ((g_KeyModFlags & ImGuiKeyModFlags_Ctrl) != 0); + io.KeyShift = ((g_KeyModFlags & ImGuiKeyModFlags_Shift) != 0); + io.KeyAlt = ((g_KeyModFlags & ImGuiKeyModFlags_Alt) != 0); + io.KeySuper = ((g_KeyModFlags & ImGuiKeyModFlags_Super) != 0); + // Setup display size (every frame to accommodate for window resizing) int32_t window_width = ANativeWindow_getWidth(g_Window); int32_t window_height = ANativeWindow_getHeight(g_Window); diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h index 92b466b6..8bfa1860 100644 --- a/backends/imgui_impl_android.h +++ b/backends/imgui_impl_android.h @@ -2,16 +2,17 @@ // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) // Implemented features: -// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). +// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // Missing features: // [ ] Platform: Clipboard support. // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. // Important: +// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs From 790132a672df39fbafcb48487f0704ee07032277 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 10 Jan 2022 12:39:01 +0100 Subject: [PATCH 247/332] Added io.AddKeyModEvent() and updated backends accordingly. (#2625, #4858) --- backends/imgui_impl_allegro5.cpp | 12 +++++++----- backends/imgui_impl_android.cpp | 8 ++------ backends/imgui_impl_glfw.cpp | 12 +++++++----- backends/imgui_impl_glut.cpp | 13 +++++++------ backends/imgui_impl_osx.mm | 7 ++----- backends/imgui_impl_sdl.cpp | 16 +++++++++------- backends/imgui_impl_win32.cpp | 12 +++++++----- imgui.cpp | 10 ++++++++++ imgui.h | 1 + 9 files changed, 52 insertions(+), 39 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 60a104ed..ae50485f 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -17,7 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -565,10 +565,12 @@ void ImGui_ImplAllegro5_NewFrame() // Setup inputs ALLEGRO_KEYBOARD_STATE keys; al_get_keyboard_state(&keys); - io.KeyCtrl = al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL); - io.KeyShift = al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT); - io.KeyAlt = al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR); - io.KeySuper = al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN); + ImGuiKeyModFlags key_mods = + ((al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL)) ? ImGuiKeyModFlags_Ctrl : 0) | + ((al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | + ((al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)) ? ImGuiKeyModFlags_Alt : 0) | + ((al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)) ? ImGuiKeyModFlags_Super : 0); + io.AddKeyModEvent(key_mods); ImGui_ImplAllegro5_UpdateMouseCursor(); } diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 7a09e017..1eb7a585 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -19,7 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. #include "imgui.h" @@ -292,11 +292,7 @@ void ImGui_ImplAndroid_NewFrame() io.SetKeyEventNativeData(key_event.Key, key_event.NativeKeycode, key_event.NativeScancode); // To support legacy indexing (<1.87 user code) key_queue.second.pop(); } - - io.KeyCtrl = ((g_KeyModFlags & ImGuiKeyModFlags_Ctrl) != 0); - io.KeyShift = ((g_KeyModFlags & ImGuiKeyModFlags_Shift) != 0); - io.KeyAlt = ((g_KeyModFlags & ImGuiKeyModFlags_Alt) != 0); - io.KeySuper = ((g_KeyModFlags & ImGuiKeyModFlags_Super) != 0); + io.AddKeyModEvent(g_KeyModFlags); // Setup display size (every frame to accommodate for window resizing) int32_t window_width = ANativeWindow_getWidth(g_Window); diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 90295d6c..d3276724 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,7 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). @@ -544,10 +544,12 @@ static void ImGui_ImplGlfw_UpdateKeyModifiers() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); - io.KeyShift = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); - io.KeyCtrl = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); - io.KeyAlt = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); - io.KeySuper = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); + ImGuiKeyModFlags key_mods = + (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)) ? ImGuiKeyModFlags_Ctrl : 0) | + (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Shift : 0) | + (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Alt : 0) | + (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)) ? ImGuiKeyModFlags_Super : 0); + io.AddKeyModEvent(key_mods); } void ImGui_ImplGlfw_NewFrame() diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index ba399397..68e417f2 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -20,7 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h. // 2019-03-25: Misc: Made io.DeltaTime always above zero. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. @@ -205,11 +205,12 @@ void ImGui_ImplGLUT_NewFrame() static void ImGui_ImplGLUT_UpdateKeyboardMods() { ImGuiIO& io = ImGui::GetIO(); - int mods = glutGetModifiers(); - io.KeyCtrl = (mods & GLUT_ACTIVE_CTRL) != 0; - io.KeyShift = (mods & GLUT_ACTIVE_SHIFT) != 0; - io.KeyAlt = (mods & GLUT_ACTIVE_ALT) != 0; - io.KeySuper = false; + int glut_key_mods = glutGetModifiers(); + ImGuiKeyModFlags key_mods = + ((glut_key_mods & GLUT_ACTIVE_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | + ((glut_key_mods & GLUT_ACTIVE_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | + ((glut_key_mods & GLUT_ACTIVE_ALT) ? ImGuiKeyModFlags_Alt : 0); + io.AddKeyModEvent(key_mods); } static void ImGui_ImplGLUT_AddKeyEvent(ImGuiKey key, bool down, int native_keycode) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 64a45efb..64984bd3 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -22,7 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. @@ -496,10 +496,7 @@ static void ImGui_ImplOSX_UpdateGamepads() static void ImGui_ImplOSX_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); - io.KeyCtrl = (g_KeyModifiers & ImGuiKeyModFlags_Ctrl) != 0; - io.KeyShift = (g_KeyModifiers & ImGuiKeyModFlags_Shift) != 0; - io.KeyAlt = (g_KeyModifiers & ImGuiKeyModFlags_Alt) != 0; - io.KeySuper = (g_KeyModifiers & ImGuiKeyModFlags_Super) != 0; + io.AddKeyModEvent(g_KeyModifiers); } void ImGui_ImplOSX_NewFrame(NSView* view) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index c50caab0..0d4d7ef0 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,7 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+) // 2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary. @@ -503,13 +503,15 @@ static void ImGui_ImplSDL2_UpdateGamepads() static void ImGui_ImplSDL2_UpdateKeyModifiers() { - SDL_Keymod keymod = SDL_GetModState(); - ImGuiIO& io = ImGui::GetIO(); - io.KeyShift = (keymod & KMOD_SHIFT) != 0; - io.KeyCtrl = (keymod & KMOD_CTRL) != 0; - io.KeyAlt = (keymod & KMOD_ALT) != 0; - io.KeySuper = (keymod & KMOD_GUI) != 0; + SDL_Keymod sdl_key_mods = SDL_GetModState(); + ImGuiKeyModFlags key_mods = + ((sdl_key_mods & KMOD_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | + ((sdl_key_mods & KMOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | + ((sdl_key_mods & KMOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | + ((sdl_key_mods & KMOD_GUI) ? ImGuiKeyModFlags_Super : 0); + io.AddKeyModEvent(key_mods); + } void ImGui_ImplSDL2_NewFrame() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index ebd8a313..5eb6d2bc 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -33,7 +33,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. // 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. @@ -230,10 +230,12 @@ static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds() static void ImGui_ImplWin32_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); - io.KeyShift = IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT); - io.KeyCtrl = IsVkDown(VK_LCONTROL) || IsVkDown(VK_RCONTROL); - io.KeyAlt = IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU); - io.KeySuper = IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN); + ImGuiKeyModFlags key_mods = + ((IsVkDown(VK_LCONTROL) || IsVkDown(VK_RCONTROL)) ? ImGuiKeyModFlags_Ctrl : 0) | + ((IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | + ((IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU)) ? ImGuiKeyModFlags_Alt : 0) | + ((IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN)) ? ImGuiKeyModFlags_Super : 0); + io.AddKeyModEvent(key_mods); } static void ImGui_ImplWin32_UpdateMousePos() diff --git a/imgui.cpp b/imgui.cpp index 591bca0a..ee47182f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -390,6 +390,7 @@ CODE - 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() + - inputs: added io.AddKeyModEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) @@ -1281,6 +1282,15 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native #endif } +void ImGuiIO::AddKeyModEvent(ImGuiKeyModFlags modifiers) +{ + KeyMods = modifiers; + KeyCtrl = (modifiers & ImGuiKeyModFlags_Ctrl) != 0; + KeyShift = (modifiers & ImGuiKeyModFlags_Shift) != 0; + KeyAlt = (modifiers & ImGuiKeyModFlags_Alt) != 0; + KeySuper = (modifiers & ImGuiKeyModFlags_Super) != 0; +} + void ImGuiIO::AddFocusEvent(bool focused) { // We intentionally overwrite this and process in NewFrame(), in order to give a chance diff --git a/imgui.h b/imgui.h index 77a9eb32..444d0288 100644 --- a/imgui.h +++ b/imgui.h @@ -2007,6 +2007,7 @@ struct ImGuiIO // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) + IMGUI_API void AddKeyModEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate From b2e2cc44c2a77e94bbf9d401449cf221dc5b8443 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 10 Jan 2022 15:47:37 +0100 Subject: [PATCH 248/332] Backends: OSX: Fixed typo. --- backends/imgui_impl_osx.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 64984bd3..df94b95c 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -656,7 +656,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { io.AddKeyEvent(key, (imgui_flags & imgui_mask) != 0); } - io.SetKeyEventNativeData(key, keycode, -1); // To support legacy indexing (<1.87 user code) + io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) } return io.WantCaptureKeyboard; From 9ce0f35ef36d4fe39052f8107d8017c8833ce771 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 10 Jan 2022 15:47:37 +0100 Subject: [PATCH 249/332] Backends: OSX: Fixed typo. --- backends/imgui_impl_osx.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index e9c44649..0fe6a039 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -658,7 +658,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { io.AddKeyEvent(key, (imgui_flags & imgui_mask) != 0); } - io.SetKeyEventNativeData(key, keycode, -1); // To support legacy indexing (<1.87 user code) + io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) } return io.WantCaptureKeyboard; From e8172fdfbc003ba28b93905db980f384092faf86 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 10 Jan 2022 16:59:31 +0100 Subject: [PATCH 250/332] Rename io.AddKeyModEvent() -> io.AddKeyModsEvent() and updated backends accordingly. (#2625, #4858) Amend 790132a (breaking) --- backends/imgui_impl_allegro5.cpp | 4 ++-- backends/imgui_impl_android.cpp | 4 ++-- backends/imgui_impl_glfw.cpp | 4 ++-- backends/imgui_impl_glut.cpp | 4 ++-- backends/imgui_impl_osx.mm | 4 ++-- backends/imgui_impl_sdl.cpp | 5 ++--- backends/imgui_impl_win32.cpp | 4 ++-- docs/CHANGELOG.txt | 2 +- imgui.cpp | 4 ++-- imgui.h | 2 +- 10 files changed, 18 insertions(+), 19 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index ae50485f..68615160 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -17,7 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -570,7 +570,7 @@ void ImGui_ImplAllegro5_NewFrame() ((al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | ((al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)) ? ImGuiKeyModFlags_Alt : 0) | ((al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); ImGui_ImplAllegro5_UpdateMouseCursor(); } diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 1eb7a585..fdf482fc 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -19,7 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. #include "imgui.h" @@ -292,7 +292,7 @@ void ImGui_ImplAndroid_NewFrame() io.SetKeyEventNativeData(key_event.Key, key_event.NativeKeycode, key_event.NativeScancode); // To support legacy indexing (<1.87 user code) key_queue.second.pop(); } - io.AddKeyModEvent(g_KeyModFlags); + io.AddKeyModsEvent(g_KeyModFlags); // Setup display size (every frame to accommodate for window resizing) int32_t window_width = ANativeWindow_getWidth(g_Window); diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index d3276724..8485a9b0 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,7 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). @@ -549,7 +549,7 @@ static void ImGui_ImplGlfw_UpdateKeyModifiers() (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Shift : 0) | (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Alt : 0) | (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } void ImGui_ImplGlfw_NewFrame() diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 68e417f2..6eec280e 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -20,7 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h. // 2019-03-25: Misc: Made io.DeltaTime always above zero. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. @@ -210,7 +210,7 @@ static void ImGui_ImplGLUT_UpdateKeyboardMods() ((glut_key_mods & GLUT_ACTIVE_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | ((glut_key_mods & GLUT_ACTIVE_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | ((glut_key_mods & GLUT_ACTIVE_ALT) ? ImGuiKeyModFlags_Alt : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } static void ImGui_ImplGLUT_AddKeyEvent(ImGuiKey key, bool down, int native_keycode) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index df94b95c..4ef44c5f 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -22,7 +22,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. @@ -496,7 +496,7 @@ static void ImGui_ImplOSX_UpdateGamepads() static void ImGui_ImplOSX_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); - io.AddKeyModEvent(g_KeyModifiers); + io.AddKeyModsEvent(g_KeyModifiers); } void ImGui_ImplOSX_NewFrame(NSView* view) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 0d4d7ef0..78451346 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,7 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+) // 2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary. @@ -510,8 +510,7 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers() ((sdl_key_mods & KMOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | ((sdl_key_mods & KMOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | ((sdl_key_mods & KMOD_GUI) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); - + io.AddKeyModsEvent(key_mods); } void ImGui_ImplSDL2_NewFrame() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 5eb6d2bc..039558d6 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -33,7 +33,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. // 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. @@ -235,7 +235,7 @@ static void ImGui_ImplWin32_UpdateKeyModifiers() ((IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | ((IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU)) ? ImGuiKeyModFlags_Alt : 0) | ((IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } static void ImGui_ImplWin32_UpdateMousePos() diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3153a670..a780c9e6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,7 +39,7 @@ Breaking Changes: - Reworked IO keyboard input system. (#2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - - Added io.AddKeyModEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. + - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. - Added full range of key enums in ImGuiKey (e.g. ImGuiKey_F1). - Added GetKeyName() helper function. diff --git a/imgui.cpp b/imgui.cpp index ee47182f..dc51a320 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -390,7 +390,7 @@ CODE - 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() - - inputs: added io.AddKeyModEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. + - inputs: added io.AddKeyModsEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) @@ -1282,7 +1282,7 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native #endif } -void ImGuiIO::AddKeyModEvent(ImGuiKeyModFlags modifiers) +void ImGuiIO::AddKeyModsEvent(ImGuiKeyModFlags modifiers) { KeyMods = modifiers; KeyCtrl = (modifiers & ImGuiKeyModFlags_Ctrl) != 0; diff --git a/imgui.h b/imgui.h index 444d0288..064ff3b8 100644 --- a/imgui.h +++ b/imgui.h @@ -2007,7 +2007,7 @@ struct ImGuiIO // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) - IMGUI_API void AddKeyModEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers + IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers);// Queue a change of Ctrl/Shift/Alt/Super modifiers IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate From acfc7798fdbab692b8fa855c7dd4ec43bc0bd828 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 10 Jan 2022 16:59:31 +0100 Subject: [PATCH 251/332] Rename io.AddKeyModEvent() -> io.AddKeyModsEvent() and updated backends accordingly. (#2625, #4858) Amend 790132a (breaking) # Conflicts: # backends/imgui_impl_glfw.cpp # backends/imgui_impl_sdl.cpp # backends/imgui_impl_win32.cpp --- backends/imgui_impl_allegro5.cpp | 4 ++-- backends/imgui_impl_android.cpp | 4 ++-- backends/imgui_impl_glfw.cpp | 4 ++-- backends/imgui_impl_glut.cpp | 4 ++-- backends/imgui_impl_osx.mm | 4 ++-- backends/imgui_impl_sdl.cpp | 4 ++-- backends/imgui_impl_win32.cpp | 4 ++-- docs/CHANGELOG.txt | 2 +- imgui.cpp | 4 ++-- imgui.h | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index ae50485f..68615160 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -17,7 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -570,7 +570,7 @@ void ImGui_ImplAllegro5_NewFrame() ((al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | ((al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)) ? ImGuiKeyModFlags_Alt : 0) | ((al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); ImGui_ImplAllegro5_UpdateMouseCursor(); } diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 1eb7a585..fdf482fc 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -19,7 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. #include "imgui.h" @@ -292,7 +292,7 @@ void ImGui_ImplAndroid_NewFrame() io.SetKeyEventNativeData(key_event.Key, key_event.NativeKeycode, key_event.NativeScancode); // To support legacy indexing (<1.87 user code) key_queue.second.pop(); } - io.AddKeyModEvent(g_KeyModFlags); + io.AddKeyModsEvent(g_KeyModFlags); // Setup display size (every frame to accommodate for window resizing) int32_t window_width = ANativeWindow_getWidth(g_Window); diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 82c46f8b..2a87b62c 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -21,7 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). @@ -676,7 +676,7 @@ static void ImGui_ImplGlfw_UpdateKeyModifiers() (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Shift : 0) | (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Alt : 0) | (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } void ImGui_ImplGlfw_NewFrame() diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 68e417f2..6eec280e 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -20,7 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h. // 2019-03-25: Misc: Made io.DeltaTime always above zero. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. @@ -210,7 +210,7 @@ static void ImGui_ImplGLUT_UpdateKeyboardMods() ((glut_key_mods & GLUT_ACTIVE_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | ((glut_key_mods & GLUT_ACTIVE_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | ((glut_key_mods & GLUT_ACTIVE_ALT) ? ImGuiKeyModFlags_Alt : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } static void ImGui_ImplGLUT_AddKeyEvent(ImGuiKey key, bool down, int native_keycode) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 0fe6a039..a3f3ec5e 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -24,7 +24,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. @@ -498,7 +498,7 @@ static void ImGui_ImplOSX_UpdateGamepads() static void ImGui_ImplOSX_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); - io.AddKeyModEvent(g_KeyModifiers); + io.AddKeyModsEvent(g_KeyModifiers); } void ImGui_ImplOSX_NewFrame(NSView* view) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 9cb52824..6711aa5f 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -21,7 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+) // 2021-06:29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary. @@ -599,7 +599,7 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers() ((sdl_key_mods & KMOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | ((sdl_key_mods & KMOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | ((sdl_key_mods & KMOD_GUI) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } void ImGui_ImplSDL2_NewFrame() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 873c20ac..6348e8a4 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -35,7 +35,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. // 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. @@ -251,7 +251,7 @@ static void ImGui_ImplWin32_UpdateKeyModifiers() ((IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | ((IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU)) ? ImGuiKeyModFlags_Alt : 0) | ((IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 19558ae4..7912640d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -106,7 +106,7 @@ Breaking Changes: - Reworked IO keyboard input system. (#2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - - Added io.AddKeyModEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. + - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. - Added full range of key enums in ImGuiKey (e.g. ImGuiKey_F1). - Added GetKeyName() helper function. diff --git a/imgui.cpp b/imgui.cpp index 193583fe..8f6d13ea 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -398,7 +398,7 @@ CODE - 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() - - inputs: added io.AddKeyModEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. + - inputs: added io.AddKeyModsEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) @@ -1318,7 +1318,7 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native #endif } -void ImGuiIO::AddKeyModEvent(ImGuiKeyModFlags modifiers) +void ImGuiIO::AddKeyModsEvent(ImGuiKeyModFlags modifiers) { KeyMods = modifiers; KeyCtrl = (modifiers & ImGuiKeyModFlags_Ctrl) != 0; diff --git a/imgui.h b/imgui.h index 709241ed..9529b5ea 100644 --- a/imgui.h +++ b/imgui.h @@ -2098,7 +2098,7 @@ struct ImGuiIO // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) - IMGUI_API void AddKeyModEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers + IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers);// Queue a change of Ctrl/Shift/Alt/Super modifiers IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate From 956e03009a56efad0681db7a5450a37105afc89d Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 11 Jan 2022 16:13:38 +0100 Subject: [PATCH 252/332] Backends: OSX: Build fIx. Made GetKeyName() input tolerant. Internals: added GetNavInputName(). --- backends/imgui_impl_osx.mm | 3 ++- imgui.cpp | 14 ++++++++++++++ imgui.h | 5 ++--- imgui_internal.h | 1 + 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 4ef44c5f..394cd0de 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -603,9 +603,10 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if ([event isARepeat]) return io.WantCaptureKeyboard; + int key_code = (int)[event keyCode]; ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); io.AddKeyEvent(key, event.type == NSEventTypeKeyDown); - io.SetKeyEventNativeData(key, (int)[event keyCode], -1); // To support legacy indexing (<1.87 user code) + io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) return io.WantCaptureKeyboard; } diff --git a/imgui.cpp b/imgui.cpp index dc51a320..aa24c118 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7435,6 +7435,8 @@ const char* ImGui::GetKeyName(ImGuiKey key) #endif if (key == ImGuiKey_None) return "None"; + if (!IsNamedKey(key)) + return "Unknown"; return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; } @@ -9717,6 +9719,18 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() } } +const char* ImGui::GetNavInputName(ImGuiNavInput n) +{ + static const char* names[] = + { + "Activate", "Cancel", "Input", "Menu", "DpadLeft", "DpadRight", "DpadUp", "DpadDown", "LStickLeft", "LStickRight", "LStickUp", "LStickDown", + "FocusPrev", "FocusNext", "TweakSlow", "TweakFast", "KeyLeft", "KeyRight", "KeyUp", "KeyDown" + }; + IM_ASSERT(IM_ARRAYSIZE(names) == ImGuiNavInput_COUNT); + IM_ASSERT(n >= 0 && n < ImGuiNavInput_COUNT); + return names[n]; +} + float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 064ff3b8..2775a0f3 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18604 +#define IMGUI_VERSION_NUM 18605 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1523,8 +1523,7 @@ enum ImGuiNavInput_ ImGuiNavInput_KeyRight_, // Move right ImGuiNavInput_KeyUp_, // Move up ImGuiNavInput_KeyDown_, // Move down - ImGuiNavInput_COUNT, - ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyLeft_ + ImGuiNavInput_COUNT }; // Configuration flags stored in io.ConfigFlags. Set by user/application. diff --git a/imgui_internal.h b/imgui_internal.h index d725c90f..80fd75d5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2577,6 +2577,7 @@ namespace ImGui IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); + IMGUI_API const char* GetNavInputName(ImGuiNavInput n); IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); From 96186a93b9233b04717f63668cb0c406e260a151 Mon Sep 17 00:00:00 2001 From: Bertie Wheen Date: Wed, 12 Jan 2022 13:12:25 +0000 Subject: [PATCH 253/332] Typo correction in imgui_demo.cpp (#4892) --- imgui_demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 589a92a9..7e96811e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5997,7 +5997,7 @@ void ImGui::ShowAboutWindow(bool* p_open) namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); } // Demo helper function to select among loaded fonts. -// Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one. +// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one. void ImGui::ShowFontSelector(const char* label) { ImGuiIO& io = ImGui::GetIO(); From 0818a423962e4fa85200227c0558a0a5034d1114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Monnom?= Date: Tue, 11 Jan 2022 22:56:35 +0100 Subject: [PATCH 254/332] Backend: WebGPU: Fixed incorrect size parameters in WGPU backend (#4891) + squash SDL alignment bits. --- backends/imgui_impl_sdl.cpp | 12 ++++++------ backends/imgui_impl_wgpu.cpp | 4 ++-- docs/CHANGELOG.txt | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 78451346..bb09a7e7 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -231,7 +231,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) switch (event->type) { - case SDL_MOUSEWHEEL: + case SDL_MOUSEWHEEL: { if (event->wheel.x > 0) io.MouseWheelH += 1; if (event->wheel.x < 0) io.MouseWheelH -= 1; @@ -239,27 +239,27 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) if (event->wheel.y < 0) io.MouseWheel -= 1; return true; } - case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONDOWN: { if (event->button.button == SDL_BUTTON_LEFT) { bd->MousePressed[0] = true; } if (event->button.button == SDL_BUTTON_RIGHT) { bd->MousePressed[1] = true; } if (event->button.button == SDL_BUTTON_MIDDLE) { bd->MousePressed[2] = true; } return true; } - case SDL_TEXTINPUT: + case SDL_TEXTINPUT: { io.AddInputCharactersUTF8(event->text.text); return true; } - case SDL_KEYDOWN: - case SDL_KEYUP: + case SDL_KEYDOWN: + case SDL_KEYUP: { ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym); io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; } - case SDL_WINDOWEVENT: + case SDL_WINDOWEVENT: { if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 072f2549..6e12c6f4 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -314,8 +314,8 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->FramebufferScale.x * draw_data->DisplaySize.x, draw_data->FramebufferScale.y * draw_data->DisplaySize.y, 0, 1); // Bind shader and vertex buffers - wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize); - wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize); + wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize * sizeof(ImDrawVert)); + wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize * sizeof(ImDrawIdx)); wgpuRenderPassEncoderSetPipeline(ctx, g_pipelineState); wgpuRenderPassEncoderSetBindGroup(ctx, 0, g_resources.CommonBindGroup, 0, NULL); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a780c9e6..f39c69de 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -104,6 +104,8 @@ Other Changes: Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. - Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but can theorically be created by user code manipulating a ImDrawList. (#4857) +- Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and + wgpuRenderPassEncoderSetVertexBuffer() calls. (#4891) [@FeepsDev] ----------------------------------------------------------------------- From 290c51735ecf1d470116c7aeeebe3cef33be2583 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jan 2022 14:18:34 +0100 Subject: [PATCH 255/332] Fixed warning in ImFloorSigned(). (#4889) --- imgui_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 80fd75d5..88e09851 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -441,7 +441,7 @@ static inline float ImLengthSqr(const ImVec2& lhs) 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 || (int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() +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 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; } From 457d4b7b72606663f588fe28103bf9e725f9ecfa Mon Sep 17 00:00:00 2001 From: Liu Liu Date: Wed, 12 Jan 2022 14:30:04 +0100 Subject: [PATCH 256/332] Backends: OSX: Adedd basic IME support for macOS. (#3108, #2598) --- backends/imgui_impl_osx.h | 3 ++- backends/imgui_impl_osx.mm | 52 +++++++++++++++++++++++++++++++++++++- docs/CHANGELOG.txt | 1 + 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index 37851a3c..85906192 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -7,7 +7,8 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. - +// [X] Platform: IME support. +// // 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. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 394cd0de..55f269d1 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -7,6 +7,7 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: IME support. // 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. @@ -22,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-12: Inputs: Added basic Platform IME support, hooking the io.SetPlatformImeDataFn() function. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. @@ -54,6 +56,7 @@ static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; static ImGuiKeyModFlags g_KeyModifiers = ImGuiKeyModFlags_None; static ImFocusObserver* g_FocusObserver = nil; static KeyEventResponder* g_KeyEventResponder = nil; +static NSTextInputContext* g_InputContext = nil; // Undocumented methods for creating cursors. @interface NSCursor() @@ -91,6 +94,29 @@ static double GetMachAbsoluteTimeInSeconds() @end @implementation KeyEventResponder +{ + float _posX; + float _posY; + NSRect _imeRect; +} + +#pragma mark - Public + +- (void)setImePosX:(float)posX imePosY:(float)posY +{ + _posX = posX; + _posY = posY; +} + +- (void)updateImePosWithView:(NSView *)view +{ + NSWindow *window = view.window; + if (!window) + return; + NSRect contentRect = [window contentRectForFrameRect:window.frame]; + NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0); + _imeRect = [window convertRectToScreen:rect]; +} - (void)viewDidMoveToWindow { @@ -145,7 +171,7 @@ static double GetMachAbsoluteTimeInSeconds() - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { - return NSZeroRect; + return _imeRect; } - (BOOL)hasMarkedText @@ -390,6 +416,7 @@ bool ImGui_ImplOSX_Init(NSView* view) // Add the NSTextInputClient to the view hierarchy, // to receive keyboard events and translate them to input text. g_KeyEventResponder = [[KeyEventResponder alloc] initWithFrame:NSZeroRect]; + g_InputContext = [[NSTextInputContext alloc] initWithClient:g_KeyEventResponder]; [view addSubview:g_KeyEventResponder]; // Some events do not raise callbacks of AppView in some circumstances (for example when CMD key is held down). @@ -401,6 +428,21 @@ bool ImGui_ImplOSX_Init(NSView* view) return event; }]; + io.SetPlatformImeDataFn = [](ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void + { + if (data->WantVisible) + { + [g_InputContext activate]; + } + else + { + [g_InputContext discardMarkedText]; + [g_InputContext invalidateCharacterCoordinates]; + [g_InputContext deactivate]; + } + [g_KeyEventResponder setImePosX:data->InputPos.x imePosY:data->InputPos.y + data->InputLineHeight]; + }; + return true; } @@ -499,6 +541,13 @@ static void ImGui_ImplOSX_UpdateKeyModifiers() io.AddKeyModsEvent(g_KeyModifiers); } +static void ImGui_ImplOSX_UpdateImePosWithView(NSView* view) +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.WantTextInput) + [g_KeyEventResponder updateImePosWithView:view]; +} + void ImGui_ImplOSX_NewFrame(NSView* view) { // Setup display size @@ -523,6 +572,7 @@ void ImGui_ImplOSX_NewFrame(NSView* view) ImGui_ImplOSX_UpdateKeyModifiers(); ImGui_ImplOSX_UpdateMouseCursorAndButtons(); ImGui_ImplOSX_UpdateGamepads(); + ImGui_ImplOSX_UpdateImePosWithView(view); } bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f39c69de..5fbf9a11 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -104,6 +104,7 @@ Other Changes: Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. - Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but can theorically be created by user code manipulating a ImDrawList. (#4857) +- Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] - Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and wgpuRenderPassEncoderSetVertexBuffer() calls. (#4891) [@FeepsDev] From 7f8a89c25c5a2d42b4246173f4a288202fd75741 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jan 2022 16:11:10 +0100 Subject: [PATCH 257/332] Backends: Win32, SDL: maintain MouseButtonsDown instead of using IsAnyMouseDown(). Internals: added GetInputSourceName(). --- backends/imgui_impl_sdl.cpp | 17 +++++++++++++---- backends/imgui_impl_win32.cpp | 8 ++++++-- docs/CHANGELOG.txt | 1 + imgui.cpp | 14 ++++++++++---- imgui.h | 2 +- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index bb09a7e7..8e61e861 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+) @@ -74,6 +75,7 @@ struct ImGui_ImplSDL2_Data { SDL_Window* Window; Uint64 Time; + int MouseButtonsDown; bool MousePressed[3]; SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; char* ClipboardTextData; @@ -240,10 +242,17 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) return true; } case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: { - if (event->button.button == SDL_BUTTON_LEFT) { bd->MousePressed[0] = true; } - if (event->button.button == SDL_BUTTON_RIGHT) { bd->MousePressed[1] = true; } - if (event->button.button == SDL_BUTTON_MIDDLE) { bd->MousePressed[2] = true; } + int mouse_button = -1; + if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } + if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } + if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; } + if (mouse_button == -1) + break; + if (event->type == SDL_MOUSEBUTTONDOWN) + bd->MousePressed[mouse_button] = true; + bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button)); return true; } case SDL_TEXTINPUT: @@ -409,7 +418,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() mouse_window = focused_window; // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside - SDL_CaptureMouse(ImGui::IsAnyMouseDown() ? SDL_TRUE : SDL_FALSE); + SDL_CaptureMouse(bd->MouseButtonsDown != 0 ? SDL_TRUE : SDL_FALSE); #else // SDL 2.0.3 and non-windowed systems: single-viewport only SDL_Window* mouse_window = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) ? bd->Window : NULL; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 039558d6..12f602f8 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -33,6 +33,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. @@ -74,6 +75,7 @@ struct ImGui_ImplWin32_Data HWND hWnd; HWND MouseHwnd; bool MouseTracked; + int MouseButtonsDown; INT64 Time; INT64 TicksPerSecond; ImGuiMouseCursor LastMouseCursor; @@ -531,9 +533,10 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) + if (bd->MouseButtonsDown == 0 && ::GetCapture() == NULL) ::SetCapture(hwnd); io.MouseDown[button] = true; + bd->MouseButtonsDown |= 1 << button; return 0; } case WM_LBUTTONUP: @@ -547,7 +550,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (msg == WM_MBUTTONUP) { button = 2; } if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) + bd->MouseButtonsDown &= ~(1 << button); + if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd) ::ReleaseCapture(); return 0; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5fbf9a11..4582e489 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -97,6 +97,7 @@ Other Changes: We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. +- Backends: Win32, SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32, Android: Updated to use io.AddKeyEvent() with full key range. (#2625) [@thedmd] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. diff --git a/imgui.cpp b/imgui.cpp index aa24c118..c963db4e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7585,6 +7585,7 @@ bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; } +// [WILL OBSOLETE] 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. bool ImGui::IsAnyMouseDown() { ImGuiContext& g = *GImGui; @@ -7642,6 +7643,13 @@ void ImGui::CaptureMouseFromApp(bool capture) g.WantCaptureMouseNextFrame = capture ? 1 : 0; } +static const char* GetInputSourceName(ImGuiInputSource source) +{ + const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; + IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); + return input_source_names[source]; +} + //----------------------------------------------------------------------------- // [SECTION] ERROR CHECKING @@ -12107,8 +12115,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Misc Details if (TreeNode("Internal state")) { - const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); - Text("WINDOWING"); Indent(); Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); @@ -12119,7 +12125,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("ITEMS"); Indent(); - Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); + Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource)); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); int active_id_using_key_input_count = 0; @@ -12134,7 +12140,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(); Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); - Text("NavInputSource: %s", input_source_names[g.NavInputSource]); + Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource)); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); Text("NavActivateFlags: %04X", g.NavActivateFlags); diff --git a/imgui.h b/imgui.h index 2775a0f3..a1cfb769 100644 --- a/imgui.h +++ b/imgui.h @@ -908,7 +908,7 @@ namespace ImGui IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available - IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? + 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) From bf4de2a46b689f9aafd0211b2e1da29fcc7780d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jan 2022 16:29:20 +0100 Subject: [PATCH 258/332] Backends: Win32: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured + update MousePos before Key Modifiers. --- backends/imgui_impl_win32.cpp | 57 +++++++++++++++++------------------ docs/CHANGELOG.txt | 4 ++- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 12f602f8..bd3d5478 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -18,6 +18,7 @@ #define WIN32_LEAN_AND_MEAN #endif #include +#include // GET_X_LPARAM(), GET_Y_LPARAM() #include #include @@ -33,6 +34,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-12: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. @@ -240,38 +242,31 @@ static void ImGui_ImplWin32_UpdateKeyModifiers() io.AddKeyModsEvent(key_mods); } -static void ImGui_ImplWin32_UpdateMousePos() +static void ImGui_ImplWin32_UpdateMouseData() { ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(bd->hWnd != 0); - const ImVec2 mouse_pos_prev = io.MousePos; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - - // Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing) - HWND focused_window = ::GetForegroundWindow(); - HWND hovered_window = bd->MouseHwnd; - HWND mouse_window = NULL; - if (hovered_window && (hovered_window == bd->hWnd || ::IsChild(hovered_window, bd->hWnd))) - mouse_window = hovered_window; - else if (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd))) - mouse_window = focused_window; - if (mouse_window == NULL) - return; - - // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) + const bool is_app_focused = (::GetForegroundWindow() == bd->hWnd); + if (is_app_focused) { - POINT pos = { (int)mouse_pos_prev.x, (int)mouse_pos_prev.y }; - if (::ClientToScreen(bd->hWnd, &pos)) - ::SetCursorPos(pos.x, pos.y); - } + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + if (::ClientToScreen(bd->hWnd, &pos)) + ::SetCursorPos(pos.x, pos.y); + } - // Set Dear ImGui mouse position from OS position - POINT pos; - if (::GetCursorPos(&pos) && ::ScreenToClient(mouse_window, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); + // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured) + if (!io.WantSetMousePos && !bd->MouseTracked) + { + POINT pos; + if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos)) + io.MousePos = ImVec2((float)pos.x, (float)pos.y); + } + } } // Gamepad navigation mapping @@ -341,15 +336,15 @@ void ImGui_ImplWin32_NewFrame() io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; bd->Time = current_time; + // Update OS mouse position + ImGui_ImplWin32_UpdateMouseData(); + // Process workarounds for known Windows key handling issues ImGui_ImplWin32_ProcessKeyEventsWorkarounds(); // Update key modifiers ImGui_ImplWin32_UpdateKeyModifiers(); - // Update OS mouse position - ImGui_ImplWin32_UpdateMousePos(); - // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); if (bd->LastMouseCursor != mouse_cursor) @@ -517,11 +512,13 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA ::TrackMouseEvent(&tme); bd->MouseTracked = true; } + io.MousePos = ImVec2((float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam)); break; case WM_MOUSELEAVE: if (bd->MouseHwnd == hwnd) bd->MouseHwnd = NULL; bd->MouseTracked = false; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); break; case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: @@ -535,8 +532,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } if (bd->MouseButtonsDown == 0 && ::GetCapture() == NULL) ::SetCapture(hwnd); - io.MouseDown[button] = true; bd->MouseButtonsDown |= 1 << button; + io.MouseDown[button] = true; return 0; } case WM_LBUTTONUP: @@ -549,10 +546,10 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (msg == WM_RBUTTONUP) { button = 1; } if (msg == WM_MBUTTONUP) { button = 2; } if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } - io.MouseDown[button] = false; bd->MouseButtonsDown &= ~(1 << button); if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd) ::ReleaseCapture(); + io.MouseDown[button] = false; return 0; } case WM_MOUSEWHEEL: diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4582e489..e9b8e700 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -96,8 +96,10 @@ Other Changes: - Backends: GLFW: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) +- Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. +- Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. -- Backends: Win32, SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. +- Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32, Android: Updated to use io.AddKeyEvent() with full key range. (#2625) [@thedmd] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. From 98ce013242050add7e891952ba0bd10f26b91a07 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jan 2022 16:44:24 +0100 Subject: [PATCH 259/332] Backends: SDL: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured + update MousePos before MouseButtons. --- backends/imgui_impl_sdl.cpp | 81 ++++++++++++++++--------------------- docs/CHANGELOG.txt | 1 + 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 8e61e861..f0109547 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. @@ -233,6 +234,11 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) switch (event->type) { + case SDL_MOUSEMOTION: + { + io.MousePos = ImVec2((float)event->motion.x, (float)event->motion.y); + return true; + } case SDL_MOUSEWHEEL: { if (event->wheel.x > 0) io.MouseWheelH += 1; @@ -270,6 +276,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_WINDOWEVENT: { + if (event->window.event == SDL_WINDOWEVENT_LEAVE) + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) @@ -391,61 +399,42 @@ void ImGui_ImplSDL2_Shutdown() IM_DELETE(bd); } -static void ImGui_ImplSDL2_UpdateMousePosAndButtons() +static void ImGui_ImplSDL2_UpdateMouseData() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); - ImVec2 mouse_pos_prev = io.MousePos; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - - // Update mouse buttons - int mouse_x_local, mouse_y_local; - Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x_local, &mouse_y_local); - io.MouseDown[0] = bd->MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; - bd->MousePressed[0] = bd->MousePressed[1] = bd->MousePressed[2] = false; - - // Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing) + // We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below) #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE - SDL_Window* focused_window = SDL_GetKeyboardFocus(); - SDL_Window* hovered_window = SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH ? SDL_GetMouseFocus() : NULL; // This is better but is only reliably useful with SDL 2.0.5+ and SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH. - SDL_Window* mouse_window = NULL; - if (hovered_window && bd->Window == hovered_window) - mouse_window = hovered_window; - else if (focused_window && bd->Window == focused_window) - mouse_window = focused_window; - // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside SDL_CaptureMouse(bd->MouseButtonsDown != 0 ? SDL_TRUE : SDL_FALSE); + SDL_Window* focused_window = SDL_GetKeyboardFocus(); + const bool is_app_focused = (bd->Window == focused_window); #else - // SDL 2.0.3 and non-windowed systems: single-viewport only - SDL_Window* mouse_window = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) ? bd->Window : NULL; + const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only #endif - - if (mouse_window == NULL) - return; - - // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - SDL_WarpMouseInWindow(bd->Window, (int)mouse_pos_prev.x, (int)mouse_pos_prev.y); - - // Set Dear ImGui mouse position from OS position + get buttons. (this is the common behavior) - if (bd->MouseCanUseGlobalState) - { - // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) - // Unlike local position obtained earlier this will be valid when straying out of bounds. - int mouse_x_global, mouse_y_global; - SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); - int window_x, window_y; - SDL_GetWindowPosition(mouse_window, &window_x, &window_y); - io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); - } - else + if (is_app_focused) { - io.MousePos = ImVec2((float)mouse_x_local, (float)mouse_y_local); + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured) + if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) + { + int window_x, window_y, mouse_x_global, mouse_y_global; + SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); + } } + + // Update buttons + Uint32 mouse_buttons = SDL_GetMouseState(NULL, NULL); + io.MouseDown[0] = bd->MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; + io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; + bd->MousePressed[0] = bd->MousePressed[1] = bd->MousePressed[2] = false; } static void ImGui_ImplSDL2_UpdateMouseCursor() @@ -545,10 +534,8 @@ void ImGui_ImplSDL2_NewFrame() io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; - // Update key modifiers ImGui_ImplSDL2_UpdateKeyModifiers(); - - ImGui_ImplSDL2_UpdateMousePosAndButtons(); + ImGui_ImplSDL2_UpdateMouseData(); ImGui_ImplSDL2_UpdateMouseCursor(); // Update game controllers (if enabled and available) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e9b8e700..f9eb46b2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,7 @@ Other Changes: - Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. +- Backends: SDL: Update mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. - Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32, Android: Updated to use io.AddKeyEvent() with full key range. (#2625) [@thedmd] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] From 200a8f1ea21010c92f45428dc958d04b803ff5cd Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jan 2022 17:08:37 +0100 Subject: [PATCH 260/332] Backends: GLFW: Update mouse inputs using glfwSetCursorPosCallback() (breaking) + fallback to provide it when focused but not hovered/captured + update MousePos before MouseButtons. + Allegro5 remove unnecessary clear. --- backends/imgui_impl_allegro5.cpp | 2 - backends/imgui_impl_glfw.cpp | 67 +++++++++++++++++++------------- backends/imgui_impl_glfw.h | 5 ++- docs/CHANGELOG.txt | 5 +++ 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 68615160..ac03b3c1 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -414,8 +414,6 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) }; bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - #if ALLEGRO_HAS_CLIPBOARD io.SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText; diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 8485a9b0..de6301dc 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). @@ -90,6 +91,7 @@ struct ImGui_ImplGlfw_Data // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. GLFWwindowfocusfun PrevUserCallbackWindowFocus; + GLFWcursorposfun PrevUserCallbackCursorPos; GLFWcursorenterfun PrevUserCallbackCursorEnter; GLFWmousebuttonfun PrevUserCallbackMousebutton; GLFWscrollfun PrevUserCallbackScroll; @@ -307,16 +309,30 @@ void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) io.AddFocusEvent(focused != 0); } +void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackCursorPos != NULL && window == bd->Window) + bd->PrevUserCallbackCursorPos(window, x, y); + + ImGuiIO& io = ImGui::GetIO(); + io.MousePos = ImVec2((float)x, (float)y); +} + void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackCursorEnter != NULL && window == bd->Window) bd->PrevUserCallbackCursorEnter(window, entered); + ImGuiIO& io = ImGui::GetIO(); if (entered) bd->MouseWindow = window; if (!entered && bd->MouseWindow == window) + { bd->MouseWindow = NULL; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + } } void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) @@ -394,6 +410,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->InstalledCallbacks = true; bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); + bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); @@ -430,6 +447,7 @@ void ImGui_ImplGlfw_Shutdown() { glfwSetWindowFocusCallback(bd->Window, bd->PrevUserCallbackWindowFocus); glfwSetCursorEnterCallback(bd->Window, bd->PrevUserCallbackCursorEnter); + glfwSetCursorPosCallback(bd->Window, bd->PrevUserCallbackCursorPos); glfwSetMouseButtonCallback(bd->Window, bd->PrevUserCallbackMousebutton); glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll); glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey); @@ -445,39 +463,36 @@ void ImGui_ImplGlfw_Shutdown() IM_DELETE(bd); } -static void ImGui_ImplGlfw_UpdateMousePosAndButtons() +static void ImGui_ImplGlfw_UpdateMouseData() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); - const ImVec2 mouse_pos_prev = io.MousePos; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - - // Update mouse buttons - // (if a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame) - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - io.MouseDown[i] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0; - bd->MouseJustPressed[i] = false; - } - #ifdef __EMSCRIPTEN__ - const bool focused = true; + const bool is_app_focused = true; #else - const bool focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0; + const bool is_app_focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0; #endif - GLFWwindow* mouse_window = (bd->MouseWindow == bd->Window || focused) ? bd->Window : NULL; - - // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos && focused) - glfwSetCursorPos(bd->Window, (double)mouse_pos_prev.x, (double)mouse_pos_prev.y); + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + glfwSetCursorPos(bd->Window, (double)io.MousePos.x, (double)io.MousePos.y); + + // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) + if (is_app_focused && bd->MouseWindow == NULL) + { + double mouse_x, mouse_y; + glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + } + } - // Set Dear ImGui mouse position from OS position - if (mouse_window != NULL) + // Update buttons + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { - double mouse_x, mouse_y; - glfwGetCursorPos(mouse_window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + io.MouseDown[i] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0; + bd->MouseJustPressed[i] = false; } } @@ -572,10 +587,8 @@ void ImGui_ImplGlfw_NewFrame() io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; - // Update key modifiers ImGui_ImplGlfw_UpdateKeyModifiers(); - - ImGui_ImplGlfw_UpdateMousePosAndButtons(); + ImGui_ImplGlfw_UpdateMouseData(); ImGui_ImplGlfw_UpdateMouseCursor(); // Update game controllers (if enabled and available) diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 84fa4b8b..46585c0a 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -32,8 +32,9 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); // GLFW callbacks // - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will 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 those function yourself from your own GLFW callbacks. -IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); -IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); +IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 +IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f9eb46b2..58604c9a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -66,6 +66,10 @@ Breaking Changes: - io.AddKeyEvent() will later be turned into a trickling IO queue (for all inputs) to handle very low framerate better. (#2525, #2787, #3383) - io.SetKeyEventNativeData() include native keycode/scancode which will later be exposed. (#3141, #2959) - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) +- Backends: GLFW: backend now uses glfwSetCursorPosCallback(). + - If calling ImGui_ImplGlfw_InitXXX with install_callbacks=true: nothing to do. is already done for you. + - If calling ImGui_ImplGlfw_InitXXX with install_callbacks=false: you WILL NEED to register the GLFW callback + using glfwSetCursorPosCallback() and forward it to the backend function ImGui_ImplGlfw_CursorPosCallback(). - Commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x @@ -96,6 +100,7 @@ Other Changes: - Backends: GLFW: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) +- Backends: GLFW: Update mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. - Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. From 95516356842e2469b59ad590c7918bf9f40683bf Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 13 Jan 2022 18:42:18 +0100 Subject: [PATCH 261/332] IO: fix SetKeyEventNativeData() not handling ImGuiKey_None the same way as AddKeyEvent(). (#4905, #4858) --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index c963db4e..3f77768e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1266,6 +1266,8 @@ void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) // If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) { + if (key == ImGuiKey_None) + return; IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey(native_legacy_index)); // >= 0 && <= 511 IM_UNUSED(native_keycode); // Yet unused From e51a0a80ca89e21eb48c17d5be5185c890233f99 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 13 Jan 2022 18:42:18 +0100 Subject: [PATCH 262/332] IO: fix SetKeyEventNativeData() not handling ImGuiKey_None the same way as AddKeyEvent(). (#4905, #4858) --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 91c97b6c..f319188f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1302,6 +1302,8 @@ void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) // If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) { + if (key == ImGuiKey_None) + return; IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey(native_legacy_index)); // >= 0 && <= 511 IM_UNUSED(native_keycode); // Yet unused From 553b04883ee3e329ab7ce81a4ebbdb2360e92f21 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 13 Jan 2022 18:52:19 +0100 Subject: [PATCH 263/332] Backends: SDL: Fix for Emscriptem. Amend 98ce013. --- backends/imgui_impl_sdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index f0109547..8feef9c2 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -424,7 +424,7 @@ static void ImGui_ImplSDL2_UpdateMouseData() { int window_x, window_y, mouse_x_global, mouse_y_global; SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); - SDL_GetWindowPosition(focused_window, &window_x, &window_y); + SDL_GetWindowPosition(bd->Window, &window_x, &window_y); io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); } } From ec1e57ed4a616ee42f4711b4a955bc68b9c1f463 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 13 Jan 2022 18:52:19 +0100 Subject: [PATCH 264/332] Merge "Backends: SDL: Fix for Emscriptem. Amend 98ce013." + Fix bad merge from master of "is_app_focused" property (Amend 0647ba3) --- backends/imgui_impl_sdl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index c1225ce2..b75fa5e8 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -463,8 +463,9 @@ static void ImGui_ImplSDL2_UpdateMouseData() // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside SDL_CaptureMouse(bd->MouseButtonsDown != 0 ? SDL_TRUE : SDL_FALSE); SDL_Window* focused_window = SDL_GetKeyboardFocus(); - const bool is_app_focused = (bd->Window == focused_window); + const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui::FindViewportByPlatformHandle((void*)focused_window))); #else + SDL_Window* focused_window = bd->Window; const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only #endif From 6188e941086fffd224cf49fc66ec8ed492607736 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 13 Jan 2022 19:18:31 +0100 Subject: [PATCH 265/332] Examples: Vulkan: fill Subpass and MSAASamples to increase discoverability (#4886) + IO docs update (#4858) https://github.com/mosra/magnum-integration/pull/89 --- backends/imgui_impl_vulkan.h | 4 ++-- docs/CHANGELOG.txt | 4 +++- examples/example_glfw_vulkan/main.cpp | 4 +++- examples/example_sdl_vulkan/main.cpp | 4 +++- imgui.cpp | 1 + 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index c770d0c1..abfecc87 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -6,7 +6,7 @@ // Missing features: // [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this backend! See https://github.com/ocornut/imgui/pull/914 -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs @@ -56,7 +56,7 @@ struct ImGui_ImplVulkan_InitInfo uint32_t Subpass; uint32_t MinImageCount; // >= 2 uint32_t ImageCount; // >= MinImageCount - VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT + VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT) const VkAllocationCallbacks* Allocator; void (*CheckVkResultFn)(VkResult err); }; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 58604c9a..832e61f6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -48,11 +48,13 @@ Breaking Changes: - IsKeyDown(), IsKeyPressed(), IsKeyReleased(), GetKeyPressedAmount(). - All backends were updated to use io.AddKeyEvent(). - Backward compatibility: - - Old backends populating those arrays will still work! (for a while) + - Old backends populating those arrays should still work! (for a while) - Calling e.g. IsKeyPressed(MY_NATIVE_KEY_XXX) will still work! (for a while) - Those legacy arrays will only be disabled if '#define IMGUI_DISABLE_OBSOLETE_KEYIO' is set in your imconfig. In a few versions, IMGUI_DISABLE_OBSOLETE_FUNCTIONS will automatically enable IMGUI_DISABLE_OBSOLETE_KEYIO, so this will be moved into the regular obsolescence path. + - BREAKING: If your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") + this is a use case that will now assert and be breaking for your old backend. - Transition guide: - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 615f2ee1..2469be60 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -404,9 +404,11 @@ int main(int, char**) init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; - init_info.Allocator = g_Allocator; + init_info.Subpass = 0; init_info.MinImageCount = g_MinImageCount; init_info.ImageCount = wd->ImageCount; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index 2c01afef..9a26ae4f 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -396,9 +396,11 @@ int main(int, char**) init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; - init_info.Allocator = g_Allocator; + init_info.Subpass = 0; init_info.MinImageCount = g_MinImageCount; init_info.ImageCount = wd->ImageCount; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); diff --git a/imgui.cpp b/imgui.cpp index 3f77768e..d237d1df 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -390,6 +390,7 @@ CODE - 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() + - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert. - inputs: added io.AddKeyModsEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. From 45c4365ec3d500bf4f373d27a4c62baef991ab36 Mon Sep 17 00:00:00 2001 From: RandomErrorMessage <35673979+RandomErrorMessage@users.noreply.github.com> Date: Fri, 14 Jan 2022 04:47:53 +0000 Subject: [PATCH 266/332] Backends: GLFW: glfwGetKeyName is not implemented in emscripten (#4907, #4858) --- backends/imgui_impl_glfw.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index de6301dc..83d4bd62 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -261,7 +261,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { -#if GLFW_HAS_GET_KEY_NAME +#if GLFW_HAS_GET_KEY_NAME && !defined(__EMSCRIPTEN__) // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) // See https://github.com/glfw/glfw/issues/1502 for details. @@ -278,6 +278,8 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; } } // if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name); +#else + IM_UNUSED(scancode); #endif return key; } From f84c93d8347617326952dfb1dbb921c53df071cc Mon Sep 17 00:00:00 2001 From: Adheesh Trivedi <84070263+AdhTri001@users.noreply.github.com> Date: Sun, 16 Jan 2022 22:46:39 +0530 Subject: [PATCH 267/332] Update BACKENDS.md (#4909) --- docs/BACKENDS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 5bd2e836..1efe1dc7 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -9,7 +9,7 @@ your application or engine to easily integrate Dear ImGui.** Each backend is typ e.g. Windows ([imgui_impl_win32.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp)), GLFW ([imgui_impl_glfw.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_glfw.cpp)), SDL2 ([imgui_impl_sdl.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl.cpp)), etc. - The 'Renderer' backends are in charge of: creating atlas texture, rendering imgui draw data.
- e.g. DirectX11 ([imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)), OpenGL/WebGL ([imgui_impl_opengl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp), Vulkan ([imgui_impl_vulkan.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp), etc. + e.g. DirectX11 ([imgui_impl_dx11.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx11.cpp)), OpenGL/WebGL ([imgui_impl_opengl3.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3.cpp)), Vulkan ([imgui_impl_vulkan.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.cpp)), etc. - For some high-level frameworks, a single backend usually handle both 'Platform' and 'Renderer' parts.
e.g. Allegro 5 ([imgui_impl_allegro5.cpp](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_allegro5.cpp)). If you end up creating a custom backend for your engine, you may want to do the same. @@ -88,7 +88,7 @@ The [example_emscripten_opengl3](https://github.com/ocornut/imgui/tree/master/ex ### Backends for third-party frameworks, graphics API or other languages -See https://github.com/ocornut/imgui/wiki/Bindings for the full list (e.g. Adventure Game Studio, Cinder, Cocos2d-x, Game Maker Studio2, Godot, LÖVE+LUA, Magnum, Monogame, Ogre, openFrameworks, OpenSceneGraph, SFML, Sokol, Unity, Unreal Engine and many others). +See https://github.com/ocornut/imgui/wiki/Bindings for the full list (e.g. Adventure Game Studio, Cinder, Cocos2d-x, Game Maker Studio2, Godot, LÖVE+LUA, Magnum, Monogame, Ogre, openFrameworks, OpenSceneGraph, SFML, Sokol, Unity, Unreal Engine and many others). ### Recommended Backends From eb823655afc5d8b4d081526f8e1525fdea49b782 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 17 Jan 2022 11:53:03 +0100 Subject: [PATCH 268/332] Removed support for pre-C++11 compilers. We'll stop supporting VS2010. (#4537) Build all --- docs/CHANGELOG.txt | 1 + imgui.h | 17 ++--------------- imgui_demo.cpp | 1 + imgui_internal.h | 4 ---- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 832e61f6..a4e4a139 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,7 @@ HOW TO UPDATE? Breaking Changes: +- Removed support for pre-C++11 compilers. We'll stop supporting VS2010. (#4537) - Reworked IO keyboard input system. (#2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. diff --git a/imgui.h b/imgui.h index a1cfb769..cd076cec 100644 --- a/imgui.h +++ b/imgui.h @@ -86,11 +86,7 @@ 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. -#if (__cplusplus >= 201100) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201100) #define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11 -#else -#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Old style macro. -#endif // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. #if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) @@ -228,17 +224,8 @@ typedef signed short ImS16; // 16-bit signed integer typedef unsigned short ImU16; // 16-bit unsigned integer typedef signed int ImS32; // 32-bit signed integer == int typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) -#if defined(_MSC_VER) && !defined(__clang__) -typedef signed __int64 ImS64; // 64-bit signed integer (pre and post C++11 with Visual Studio) -typedef unsigned __int64 ImU64; // 64-bit unsigned integer (pre and post C++11 with Visual Studio) -#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100) -#include -typedef int64_t ImS64; // 64-bit signed integer (pre C++11) -typedef uint64_t ImU64; // 64-bit unsigned integer (pre C++11) -#else -typedef signed long long ImS64; // 64-bit signed integer (post C++11) -typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11) -#endif +typedef signed long long ImS64; // 64-bit signed integer (C++11) +typedef unsigned long long ImU64; // 64-bit unsigned integer (C++11) // 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) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7e96811e..5c5bc3a3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -92,6 +92,7 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #endif diff --git a/imgui_internal.h b/imgui_internal.h index 88e09851..bff1a61f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -203,11 +203,7 @@ namespace ImStb #define IMGUI_DEBUG_LOG_NAV(...) ((void)0) // Disable log // Static Asserts -#if (__cplusplus >= 201100) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201100) #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") -#else -#define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] -#endif // "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much. // We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code. From 075576744057573083ea2dff7d8c9b46d12a28bf Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 17 Jan 2022 14:57:55 +0100 Subject: [PATCH 269/332] Backends: move io.AddKeyModsEvent() next to io.AddKeyEvent() submission, rely on mods from platform/source. (#4858) + fix #2622 again broken by 746c9f7 Amended to submit modifiers before. --- backends/imgui_impl_allegro5.cpp | 27 +++++++++++++++++---------- backends/imgui_impl_glfw.cpp | 28 +++++++++++++++------------- backends/imgui_impl_glut.cpp | 10 +++++----- backends/imgui_impl_osx.mm | 23 +++++++---------------- backends/imgui_impl_sdl.cpp | 26 +++++++++++++------------- backends/imgui_impl_win32.cpp | 20 ++++++++++---------- docs/CHANGELOG.txt | 6 +++++- 7 files changed, 72 insertions(+), 68 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index ac03b3c1..25e2d148 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events. @@ -440,6 +441,20 @@ void ImGui_ImplAllegro5_Shutdown() IM_DELETE(bd); } +// ev->keyboard.modifiers seems always zero so using that... +static void ImGui_ImplAllegro5_UpdateKeyModifiers() +{ + ImGuiIO& io = ImGui::GetIO(); + ALLEGRO_KEYBOARD_STATE keys; + al_get_keyboard_state(&keys); + ImGuiKeyModFlags key_mods = + ((al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL)) ? ImGuiKeyModFlags_Ctrl : 0) | + ((al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | + ((al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)) ? ImGuiKeyModFlags_Alt : 0) | + ((al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)) ? ImGuiKeyModFlags_Super : 0); + io.AddKeyModsEvent(key_mods); +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -487,6 +502,7 @@ bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev) case ALLEGRO_EVENT_KEY_UP: if (ev->keyboard.display == bd->Display) { + ImGui_ImplAllegro5_UpdateKeyModifiers(); ImGuiKey key = ImGui_ImplAllegro5_KeyCodeToImGuiKey(ev->keyboard.keycode); io.AddKeyEvent(key, (ev->type == ALLEGRO_EVENT_KEY_DOWN)); io.SetKeyEventNativeData(key, ev->keyboard.keycode, -1); // To support legacy indexing (<1.87 user code) @@ -560,15 +576,6 @@ void ImGui_ImplAllegro5_NewFrame() io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; - // Setup inputs - ALLEGRO_KEYBOARD_STATE keys; - al_get_keyboard_state(&keys); - ImGuiKeyModFlags key_mods = - ((al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL)) ? ImGuiKeyModFlags_Ctrl : 0) | - ((al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | - ((al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)) ? ImGuiKeyModFlags_Alt : 0) | - ((al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModsEvent(key_mods); - + // Setup mouse cursor shape ImGui_ImplAllegro5_UpdateMouseCursor(); } diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 83d4bd62..b10bab89 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. @@ -238,12 +239,24 @@ static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) } } +static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods) +{ + ImGuiIO& io = ImGui::GetIO(); + ImGuiKeyModFlags key_mods = + ((mods & GLFW_MOD_CONTROL) ? ImGuiKeyModFlags_Ctrl : 0) | + ((mods & GLFW_MOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | + ((mods & GLFW_MOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | + ((mods & GLFW_MOD_SUPER) ? ImGuiKeyModFlags_Super : 0); + io.AddKeyModsEvent(key_mods); +} + void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window) bd->PrevUserCallbackMousebutton(window, button, action, mods); + ImGui_ImplGlfw_UpdateKeyModifiers(mods); if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(bd->MouseJustPressed)) bd->MouseJustPressed[button] = true; } @@ -293,6 +306,8 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i if (action != GLFW_PRESS && action != GLFW_RELEASE) return; + ImGui_ImplGlfw_UpdateKeyModifiers(mods); + keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); ImGuiIO& io = ImGui::GetIO(); @@ -557,18 +572,6 @@ static void ImGui_ImplGlfw_UpdateGamepads() io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; } -static void ImGui_ImplGlfw_UpdateKeyModifiers() -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - ImGuiKeyModFlags key_mods = - (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)) ? ImGuiKeyModFlags_Ctrl : 0) | - (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Shift : 0) | - (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Alt : 0) | - (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModsEvent(key_mods); -} - void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); @@ -589,7 +592,6 @@ void ImGui_ImplGlfw_NewFrame() io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; - ImGui_ImplGlfw_UpdateKeyModifiers(); ImGui_ImplGlfw_UpdateMouseData(); ImGui_ImplGlfw_UpdateMouseCursor(); diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 6eec280e..91185372 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -202,7 +202,7 @@ void ImGui_ImplGLUT_NewFrame() ImGui::NewFrame(); } -static void ImGui_ImplGLUT_UpdateKeyboardMods() +static void ImGui_ImplGLUT_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); int glut_key_mods = glutGetModifiers(); @@ -230,7 +230,7 @@ void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y) ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c); ImGui_ImplGLUT_AddKeyEvent(key, true, c); - ImGui_ImplGLUT_UpdateKeyboardMods(); + ImGui_ImplGLUT_UpdateKeyModifiers(); (void)x; (void)y; // Unused } @@ -239,7 +239,7 @@ void ImGui_ImplGLUT_KeyboardUpFunc(unsigned char c, int x, int y) //printf("char_up_func %d '%c'\n", c, c); ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c); ImGui_ImplGLUT_AddKeyEvent(key, false, c); - ImGui_ImplGLUT_UpdateKeyboardMods(); + ImGui_ImplGLUT_UpdateKeyModifiers(); (void)x; (void)y; // Unused } @@ -248,7 +248,7 @@ void ImGui_ImplGLUT_SpecialFunc(int key, int x, int y) //printf("key_down_func %d\n", key); ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256); ImGui_ImplGLUT_AddKeyEvent(imgui_key, true, key + 256); - ImGui_ImplGLUT_UpdateKeyboardMods(); + ImGui_ImplGLUT_UpdateKeyModifiers(); (void)x; (void)y; // Unused } @@ -257,7 +257,7 @@ void ImGui_ImplGLUT_SpecialUpFunc(int key, int x, int y) //printf("key_up_func %d\n", key); ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256); ImGui_ImplGLUT_AddKeyEvent(imgui_key, false, key + 256); - ImGui_ImplGLUT_UpdateKeyboardMods(); + ImGui_ImplGLUT_UpdateKeyModifiers(); (void)x; (void)y; // Unused } diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 55f269d1..648840dd 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -53,7 +53,6 @@ static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; static bool g_MouseCursorHidden = false; static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; -static ImGuiKeyModFlags g_KeyModifiers = ImGuiKeyModFlags_None; static ImFocusObserver* g_FocusObserver = nil; static KeyEventResponder* g_KeyEventResponder = nil; static NSTextInputContext* g_InputContext = nil; @@ -535,12 +534,6 @@ static void ImGui_ImplOSX_UpdateGamepads() io.BackendFlags |= ImGuiBackendFlags_HasGamepad; } -static void ImGui_ImplOSX_UpdateKeyModifiers() -{ - ImGuiIO& io = ImGui::GetIO(); - io.AddKeyModsEvent(g_KeyModifiers); -} - static void ImGui_ImplOSX_UpdateImePosWithView(NSView* view) { ImGuiIO& io = ImGui::GetIO(); @@ -569,7 +562,6 @@ void ImGui_ImplOSX_NewFrame(NSView* view) io.DeltaTime = (float)(current_time - g_Time); g_Time = current_time; - ImGui_ImplOSX_UpdateKeyModifiers(); ImGui_ImplOSX_UpdateMouseCursorAndButtons(); ImGui_ImplOSX_UpdateGamepads(); ImGui_ImplOSX_UpdateImePosWithView(view); @@ -666,17 +658,16 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) unsigned short key_code = [event keyCode]; unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; - ImGuiKeyModFlags imgui_flags = ImGuiKeyModFlags_None; + ImGuiKeyModFlags imgui_key_mods = ImGuiKeyModFlags_None; if (flags & NSEventModifierFlagShift) - imgui_flags |= ImGuiKeyModFlags_Shift; + imgui_key_mods |= ImGuiKeyModFlags_Shift; if (flags & NSEventModifierFlagControl) - imgui_flags |= ImGuiKeyModFlags_Ctrl; + imgui_key_mods |= ImGuiKeyModFlags_Ctrl; if (flags & NSEventModifierFlagOption) - imgui_flags |= ImGuiKeyModFlags_Alt; + imgui_key_mods |= ImGuiKeyModFlags_Alt; if (flags & NSEventModifierFlagCommand) - imgui_flags |= ImGuiKeyModFlags_Super; - - g_KeyModifiers = imgui_flags; + imgui_key_mods |= ImGuiKeyModFlags_Super; + io.AddKeyModsEvent(imgui_key_mods); ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); if (key != ImGuiKey_None) @@ -705,7 +696,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) } else if (imgui_mask) { - io.AddKeyEvent(key, (imgui_flags & imgui_mask) != 0); + io.AddKeyEvent(key, (imgui_key_mods & imgui_mask) != 0); } io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) } diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 8feef9c2..941acaec 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. @@ -222,6 +223,17 @@ static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) return ImGuiKey_None; } +static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) +{ + ImGuiIO& io = ImGui::GetIO(); + ImGuiKeyModFlags key_mods = + ((sdl_key_mods & KMOD_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | + ((sdl_key_mods & KMOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | + ((sdl_key_mods & KMOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | + ((sdl_key_mods & KMOD_GUI) ? ImGuiKeyModFlags_Super : 0); + io.AddKeyModsEvent(key_mods); +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -269,6 +281,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_KEYDOWN: case SDL_KEYUP: { + ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod); ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym); io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. @@ -499,18 +512,6 @@ static void ImGui_ImplSDL2_UpdateGamepads() #undef MAP_ANALOG } -static void ImGui_ImplSDL2_UpdateKeyModifiers() -{ - ImGuiIO& io = ImGui::GetIO(); - SDL_Keymod sdl_key_mods = SDL_GetModState(); - ImGuiKeyModFlags key_mods = - ((sdl_key_mods & KMOD_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | - ((sdl_key_mods & KMOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | - ((sdl_key_mods & KMOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | - ((sdl_key_mods & KMOD_GUI) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModsEvent(key_mods); -} - void ImGui_ImplSDL2_NewFrame() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -534,7 +535,6 @@ void ImGui_ImplSDL2_NewFrame() io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; - ImGui_ImplSDL2_UpdateKeyModifiers(); ImGui_ImplSDL2_UpdateMouseData(); ImGui_ImplSDL2_UpdateMouseCursor(); diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index bd3d5478..37943465 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -34,8 +34,9 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-12: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. -// 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. +// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before a key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. +// 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. @@ -235,10 +236,10 @@ static void ImGui_ImplWin32_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); ImGuiKeyModFlags key_mods = - ((IsVkDown(VK_LCONTROL) || IsVkDown(VK_RCONTROL)) ? ImGuiKeyModFlags_Ctrl : 0) | - ((IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | - ((IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU)) ? ImGuiKeyModFlags_Alt : 0) | - ((IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN)) ? ImGuiKeyModFlags_Super : 0); + ((IsVkDown(VK_CONTROL)) ? ImGuiKeyModFlags_Ctrl : 0) | + ((IsVkDown(VK_SHIFT) ) ? ImGuiKeyModFlags_Shift : 0) | + ((IsVkDown(VK_MENU)) ? ImGuiKeyModFlags_Alt : 0) | + ((IsVkDown(VK_APPS)) ? ImGuiKeyModFlags_Super : 0); io.AddKeyModsEvent(key_mods); } @@ -342,9 +343,6 @@ void ImGui_ImplWin32_NewFrame() // Process workarounds for known Windows key handling issues ImGui_ImplWin32_ProcessKeyEventsWorkarounds(); - // Update key modifiers - ImGui_ImplWin32_UpdateKeyModifiers(); - // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); if (bd->LastMouseCursor != mouse_cursor) @@ -564,9 +562,11 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_SYSKEYUP: { const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); - if (wParam < 256) { + // Submit modifiers + ImGui_ImplWin32_UpdateKeyModifiers(); + // Obtain virtual key code // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.) int vk = (int)wParam; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a4e4a139..d60c8365 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -103,13 +103,17 @@ Other Changes: - Backends: GLFW: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) +- Backends: GLFW: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: GLFW: Update mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. +- Backends: Win32: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. +- Backends: SDL: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: SDL: Update mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. - Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. -- Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32, Android: Updated to use io.AddKeyEvent() with full key range. (#2625) [@thedmd] +- Backends: Allegro5: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: Android, GLUT, OSX: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] From 92c4ff1c2f332f90da7bd87e5d92a25607aa0b03 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 17 Jan 2022 15:41:04 +0100 Subject: [PATCH 270/332] Rename ImGuiKey_LeftControl to ImGuiKey_LeftCtrl, ImGuiKey_RightControl to ImGuiKey_RightCtrl + made key order match mods order. (#4858) --- backends/imgui_impl_allegro5.cpp | 4 ++-- backends/imgui_impl_android.cpp | 4 ++-- backends/imgui_impl_glfw.cpp | 4 ++-- backends/imgui_impl_glut.cpp | 4 ++-- backends/imgui_impl_osx.mm | 10 +++++----- backends/imgui_impl_sdl.cpp | 4 ++-- backends/imgui_impl_win32.cpp | 8 ++++---- imgui.cpp | 4 ++-- imgui.h | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 25e2d148..2a5868d8 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -329,12 +329,12 @@ static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code) case ALLEGRO_KEY_PAD_PLUS: return ImGuiKey_KeypadAdd; case ALLEGRO_KEY_PAD_ENTER: return ImGuiKey_KeypadEnter; case ALLEGRO_KEY_PAD_EQUALS: return ImGuiKey_KeypadEqual; + case ALLEGRO_KEY_LCTRL: return ImGuiKey_LeftCtrl; case ALLEGRO_KEY_LSHIFT: return ImGuiKey_LeftShift; - case ALLEGRO_KEY_LCTRL: return ImGuiKey_LeftControl; case ALLEGRO_KEY_ALT: return ImGuiKey_LeftAlt; case ALLEGRO_KEY_LWIN: return ImGuiKey_LeftSuper; + case ALLEGRO_KEY_RCTRL: return ImGuiKey_RightCtrl; case ALLEGRO_KEY_RSHIFT: return ImGuiKey_RightShift; - case ALLEGRO_KEY_RCTRL: return ImGuiKey_RightControl; case ALLEGRO_KEY_ALTGR: return ImGuiKey_RightAlt; case ALLEGRO_KEY_RWIN: return ImGuiKey_RightSuper; case ALLEGRO_KEY_MENU: return ImGuiKey_Menu; diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index fdf482fc..380e4f9d 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -101,12 +101,12 @@ static ImGuiKey ImGui_ImplAndroid_KeyCodeToImGuiKey(int32_t key_code) case AKEYCODE_NUMPAD_ADD: return ImGuiKey_KeypadAdd; case AKEYCODE_NUMPAD_ENTER: return ImGuiKey_KeypadEnter; case AKEYCODE_NUMPAD_EQUALS: return ImGuiKey_KeypadEqual; + case AKEYCODE_CTRL_LEFT: return ImGuiKey_LeftCtrl; case AKEYCODE_SHIFT_LEFT: return ImGuiKey_LeftShift; - case AKEYCODE_CTRL_LEFT: return ImGuiKey_LeftControl; case AKEYCODE_ALT_LEFT: return ImGuiKey_LeftAlt; case AKEYCODE_META_LEFT: return ImGuiKey_LeftSuper; + case AKEYCODE_CTRL_RIGHT: return ImGuiKey_RightCtrl; case AKEYCODE_SHIFT_RIGHT: return ImGuiKey_RightShift; - case AKEYCODE_CTRL_RIGHT: return ImGuiKey_RightControl; case AKEYCODE_ALT_RIGHT: return ImGuiKey_RightAlt; case AKEYCODE_META_RIGHT: return ImGuiKey_RightSuper; case AKEYCODE_MENU: return ImGuiKey_Menu; diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index b10bab89..ae98581f 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -179,11 +179,11 @@ static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter; case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual; case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift; - case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftControl; + case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl; case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt; case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper; case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift; - case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightControl; + case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl; case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt; case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper; case GLFW_KEY_MENU: return ImGuiKey_Menu; diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 91185372..a316c895 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -93,12 +93,12 @@ static ImGuiKey ImGui_ImplGLUT_KeyToImGuiKey(int key) case 43: return ImGuiKey_KeypadAdd; //case 13: return ImGuiKey_KeypadEnter; //case 0: return ImGuiKey_KeypadEqual; + case 256 + 0x0072: return ImGuiKey_LeftCtrl; case 256 + 0x0070: return ImGuiKey_LeftShift; - case 256 + 0x0072: return ImGuiKey_LeftControl; case 256 + 0x0074: return ImGuiKey_LeftAlt; //case 0: return ImGuiKey_LeftSuper; + case 256 + 0x0073: return ImGuiKey_RightCtrl; case 256 + 0x0071: return ImGuiKey_RightShift; - case 256 + 0x0073: return ImGuiKey_RightControl; case 256 + 0x0075: return ImGuiKey_RightAlt; //case 0: return ImGuiKey_RightSuper; //case 0: return ImGuiKey_Menu; diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 648840dd..48a50f1b 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -301,15 +301,15 @@ static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code) case kVK_Space: return ImGuiKey_Space; case kVK_Delete: return ImGuiKey_Backspace; case kVK_Escape: return ImGuiKey_Escape; - case kVK_Command: return ImGuiKey_LeftSuper; - case kVK_Shift: return ImGuiKey_LeftShift; case kVK_CapsLock: return ImGuiKey_CapsLock; + case kVK_Control: return ImGuiKey_LeftCtrl; + case kVK_Shift: return ImGuiKey_LeftShift; case kVK_Option: return ImGuiKey_LeftAlt; - case kVK_Control: return ImGuiKey_LeftControl; - case kVK_RightCommand: return ImGuiKey_RightSuper; + case kVK_Command: return ImGuiKey_LeftSuper; + case kVK_RightControl: return ImGuiKey_RightCtrl; case kVK_RightShift: return ImGuiKey_RightShift; case kVK_RightOption: return ImGuiKey_RightAlt; - case kVK_RightControl: return ImGuiKey_RightControl; + case kVK_RightCommand: return ImGuiKey_RightSuper; // case kVK_Function: return ImGuiKey_; // case kVK_F17: return ImGuiKey_; // case kVK_VolumeUp: return ImGuiKey_; diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 941acaec..480846b9 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -162,12 +162,12 @@ static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd; case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter; case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual; + case SDLK_LCTRL: return ImGuiKey_LeftCtrl; case SDLK_LSHIFT: return ImGuiKey_LeftShift; - case SDLK_LCTRL: return ImGuiKey_LeftControl; case SDLK_LALT: return ImGuiKey_LeftAlt; case SDLK_LGUI: return ImGuiKey_LeftSuper; + case SDLK_RCTRL: return ImGuiKey_RightCtrl; case SDLK_RSHIFT: return ImGuiKey_RightShift; - case SDLK_RCTRL: return ImGuiKey_RightControl; case SDLK_RALT: return ImGuiKey_RightAlt; case SDLK_RGUI: return ImGuiKey_RightSuper; case SDLK_MENU: return ImGuiKey_Menu; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 37943465..2b140b54 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -411,11 +411,11 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) case VK_ADD: return ImGuiKey_KeypadAdd; case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter; case VK_LSHIFT: return ImGuiKey_LeftShift; - case VK_LCONTROL: return ImGuiKey_LeftControl; + case VK_LCONTROL: return ImGuiKey_LeftCtrl; case VK_LMENU: return ImGuiKey_LeftAlt; case VK_LWIN: return ImGuiKey_LeftSuper; case VK_RSHIFT: return ImGuiKey_RightShift; - case VK_RCONTROL: return ImGuiKey_RightControl; + case VK_RCONTROL: return ImGuiKey_RightCtrl; case VK_RMENU: return ImGuiKey_RightAlt; case VK_RWIN: return ImGuiKey_RightSuper; case VK_APPS: return ImGuiKey_Menu; @@ -588,8 +588,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA } else if (vk == VK_CONTROL) { - if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftControl, is_key_down, VK_LCONTROL, scancode); } - if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightControl, is_key_down, VK_RCONTROL, scancode); } + if (IsVkDown(VK_LCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_LeftCtrl, is_key_down, VK_LCONTROL, scancode); } + if (IsVkDown(VK_RCONTROL) == is_key_down) { ImGui_ImplWin32_AddKeyEvent(ImGuiKey_RightCtrl, is_key_down, VK_RCONTROL, scancode); } } else if (vk == VK_MENU) { diff --git a/imgui.cpp b/imgui.cpp index d237d1df..b84b2b0f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7414,8 +7414,8 @@ static const char* const GKeyNames[] = "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", "LeftShift", "LeftControl", - "LeftAlt", "LeftSuper", "RightShift", "RightControl", "RightAlt", "RightSuper", "Menu", + "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", "LeftCtrl", "LeftShift", + "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu", "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" diff --git a/imgui.h b/imgui.h index cd076cec..1f5df0cc 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18605 +#define IMGUI_VERSION_NUM 18606 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1391,12 +1391,12 @@ enum ImGuiKey_ ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, + ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, - ImGuiKey_LeftControl, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper, + ImGuiKey_RightCtrl, ImGuiKey_RightShift, - ImGuiKey_RightControl, ImGuiKey_RightAlt, ImGuiKey_RightSuper, ImGuiKey_Menu, From b6582a471ae7c13eeecb8e39c72c87a99a59657e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 17 Jan 2022 16:22:32 +0100 Subject: [PATCH 271/332] Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) --- backends/imgui_impl_osx.mm | 4 ++-- docs/CHANGELOG.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 48a50f1b..146fff1e 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -522,8 +522,8 @@ static void ImGui_ImplOSX_UpdateGamepads() MAP_BUTTON(ImGuiNavInput_DpadDown, dpad.down); MAP_BUTTON(ImGuiNavInput_FocusPrev, leftShoulder); MAP_BUTTON(ImGuiNavInput_FocusNext, rightShoulder); - MAP_BUTTON(ImGuiNavInput_TweakSlow, leftTrigger); - MAP_BUTTON(ImGuiNavInput_TweakFast, rightTrigger); + MAP_BUTTON(ImGuiNavInput_TweakSlow, leftShoulder); + MAP_BUTTON(ImGuiNavInput_TweakFast, rightShoulder); #undef MAP_BUTTON io.NavInputs[ImGuiNavInput_LStickLeft] = gp.leftThumbstick.left.value; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d60c8365..69f7b4fd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -121,6 +121,7 @@ Other Changes: - Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but can theorically be created by user code manipulating a ImDrawList. (#4857) - Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] +- Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) - Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and wgpuRenderPassEncoderSetVertexBuffer() calls. (#4891) [@FeepsDev] From b8e56dce83985a49e2e236900d80980510b173f3 Mon Sep 17 00:00:00 2001 From: thedmd Date: Mon, 17 Jan 2022 17:22:37 +0100 Subject: [PATCH 272/332] IO: Added AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() api + updated all Backends. (#4858) (input queue code will be next commit) Details: note that SDL, OSX and GLFW backends removed recording of MouseJustPressed[] which will be unnecessary with input queue (which is the NEXT commit). (#2787, #1992, #3383, #2525, #1320) --- backends/imgui_impl_allegro5.cpp | 16 ++++++++-------- backends/imgui_impl_android.cpp | 16 ++++++++-------- backends/imgui_impl_glfw.cpp | 24 +++++++++--------------- backends/imgui_impl_glut.cpp | 19 ++++++++----------- backends/imgui_impl_osx.mm | 31 ++++++++++--------------------- backends/imgui_impl_sdl.cpp | 25 ++++++++----------------- backends/imgui_impl_win32.cpp | 15 ++++++++------- docs/CHANGELOG.txt | 15 ++++++++++++--- imgui.cpp | 28 +++++++++++++++++++++++++++- imgui.h | 6 +++++- 10 files changed, 103 insertions(+), 92 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 2a5868d8..cef70e63 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. @@ -469,29 +470,28 @@ bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev) case ALLEGRO_EVENT_MOUSE_AXES: if (ev->mouse.display == bd->Display) { - io.MouseWheel += ev->mouse.dz; - io.MouseWheelH -= ev->mouse.dw; - io.MousePos = ImVec2(ev->mouse.x, ev->mouse.y); + io.AddMousePosEvent(ev->mouse.x, ev->mouse.y); + io.AddMouseWheelEvent(-ev->mouse.dw, ev->mouse.dz); } return true; case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: case ALLEGRO_EVENT_MOUSE_BUTTON_UP: - if (ev->mouse.display == bd->Display && ev->mouse.button <= 5) - io.MouseDown[ev->mouse.button - 1] = (ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN); + if (ev->mouse.display == bd->Display && ev->mouse.button > 0 && ev->mouse.button <= 5) + io.AddMouseButtonEvent(ev->mouse.button - 1, ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN); return true; case ALLEGRO_EVENT_TOUCH_MOVE: if (ev->touch.display == bd->Display) - io.MousePos = ImVec2(ev->touch.x, ev->touch.y); + io.AddMousePosEvent(ev->touch.x, ev->touch.y); return true; case ALLEGRO_EVENT_TOUCH_BEGIN: case ALLEGRO_EVENT_TOUCH_END: case ALLEGRO_EVENT_TOUCH_CANCEL: if (ev->touch.display == bd->Display && ev->touch.primary) - io.MouseDown[0] = (ev->type == ALLEGRO_EVENT_TOUCH_BEGIN); + io.AddMouseButtonEvent(0, ev->type == ALLEGRO_EVENT_TOUCH_BEGIN); return true; case ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY: if (ev->mouse.display == bd->Display) - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); return true; case ALLEGRO_EVENT_KEY_CHAR: if (ev->keyboard.display == bd->Display) diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 380e4f9d..82f388a5 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -19,6 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. @@ -227,26 +228,25 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER) || (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) { - io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN); - io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); + io.AddMousePosEvent(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); + io.AddMouseButtonEvent(0, event_action == AMOTION_EVENT_ACTION_DOWN); } break; case AMOTION_EVENT_ACTION_BUTTON_PRESS: case AMOTION_EVENT_ACTION_BUTTON_RELEASE: { int32_t button_state = AMotionEvent_getButtonState(input_event); - io.MouseDown[0] = ((button_state & AMOTION_EVENT_BUTTON_PRIMARY) != 0); - io.MouseDown[1] = ((button_state & AMOTION_EVENT_BUTTON_SECONDARY) != 0); - io.MouseDown[2] = ((button_state & AMOTION_EVENT_BUTTON_TERTIARY) != 0); + io.AddMouseButtonEvent(0, (button_state & AMOTION_EVENT_BUTTON_PRIMARY) != 0); + io.AddMouseButtonEvent(1, (button_state & AMOTION_EVENT_BUTTON_SECONDARY) != 0); + io.AddMouseButtonEvent(2, (button_state & AMOTION_EVENT_BUTTON_TERTIARY) != 0); } break; case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse) case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN - io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); + io.AddMousePosEvent(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); break; case AMOTION_EVENT_ACTION_SCROLL: - io.MouseWheel = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index); - io.MouseWheelH = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index); + io.AddMouseWheelEvent(AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index), AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index)); break; default: break; diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index ae98581f..4dea6238 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. @@ -86,7 +87,6 @@ struct ImGui_ImplGlfw_Data GlfwClientApi ClientApi; double Time; GLFWwindow* MouseWindow; - bool MouseJustPressed[ImGuiMouseButton_COUNT]; GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; bool InstalledCallbacks; @@ -257,8 +257,10 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti bd->PrevUserCallbackMousebutton(window, button, action, mods); ImGui_ImplGlfw_UpdateKeyModifiers(mods); - if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(bd->MouseJustPressed)) - bd->MouseJustPressed[button] = true; + + ImGuiIO& io = ImGui::GetIO(); + if (button >= 0 && button < ImGuiMouseButton_COUNT) + io.AddMouseButtonEvent(button, action == GLFW_PRESS); } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) @@ -268,8 +270,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo bd->PrevUserCallbackScroll(window, xoffset, yoffset); ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (float)xoffset; - io.MouseWheel += (float)yoffset; + io.AddMouseWheelEvent((float)xoffset, (float)yoffset); } static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) @@ -333,7 +334,7 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) bd->PrevUserCallbackCursorPos(window, x, y); ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ImVec2((float)x, (float)y); + io.AddMousePosEvent((float)x, (float)y); } void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) @@ -348,7 +349,7 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) if (!entered && bd->MouseWindow == window) { bd->MouseWindow = NULL; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } } @@ -501,16 +502,9 @@ static void ImGui_ImplGlfw_UpdateMouseData() { double mouse_x, mouse_y; glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } - - // Update buttons - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - io.MouseDown[i] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0; - bd->MouseJustPressed[i] = false; - } } static void ImGui_ImplGlfw_UpdateMouseCursor() diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index a316c895..85ee838d 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h. // 2019-03-25: Misc: Made io.DeltaTime always above zero. @@ -264,26 +265,22 @@ void ImGui_ImplGLUT_SpecialUpFunc(int key, int x, int y) void ImGui_ImplGLUT_MouseFunc(int glut_button, int state, int x, int y) { ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ImVec2((float)x, (float)y); + io.AddMousePosEvent((float)x, (float)y); int button = -1; if (glut_button == GLUT_LEFT_BUTTON) button = 0; if (glut_button == GLUT_RIGHT_BUTTON) button = 1; if (glut_button == GLUT_MIDDLE_BUTTON) button = 2; - if (button != -1 && state == GLUT_DOWN) - io.MouseDown[button] = true; - if (button != -1 && state == GLUT_UP) - io.MouseDown[button] = false; + if (button != -1 && state == GLUT_DOWN || state == GLUT_UP) + io.AddMouseButtonEvent(button, state == GLUT_DOWN); } #ifdef __FREEGLUT_EXT_H__ void ImGui_ImplGLUT_MouseWheelFunc(int button, int dir, int x, int y) { ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ImVec2((float)x, (float)y); - if (dir > 0) - io.MouseWheel += 1.0; - else if (dir < 0) - io.MouseWheel -= 1.0; + io.AddMousePosEvent((float)x, (float)y); + if (dir != 0) + io.AddMouseWheelEvent(0.0f, dir > 0 ? 1.0f : -1.0f); (void)button; // Unused } #endif @@ -297,5 +294,5 @@ void ImGui_ImplGLUT_ReshapeFunc(int w, int h) void ImGui_ImplGLUT_MotionFunc(int x, int y) { ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ImVec2((float)x, (float)y); + io.AddMousePosEvent((float)x, (float)y); } diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 146fff1e..58e5a09f 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-12: Inputs: Added basic Platform IME support, hooking the io.SetPlatformImeDataFn() function. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. @@ -51,8 +52,6 @@ static double g_HostClockPeriod = 0.0; static double g_Time = 0.0; static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; static bool g_MouseCursorHidden = false; -static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; -static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; static ImFocusObserver* g_FocusObserver = nil; static KeyEventResponder* g_KeyEventResponder = nil; static NSTextInputContext* g_InputContext = nil; @@ -450,17 +449,9 @@ void ImGui_ImplOSX_Shutdown() g_FocusObserver = NULL; } -static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() +static void ImGui_ImplOSX_UpdateMouseCursor() { - // Update buttons ImGuiIO& io = ImGui::GetIO(); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || g_MouseDown[i]; - g_MouseJustPressed[i] = false; - } - if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return; @@ -562,7 +553,7 @@ void ImGui_ImplOSX_NewFrame(NSView* view) io.DeltaTime = (float)(current_time - g_Time); g_Time = current_time; - ImGui_ImplOSX_UpdateMouseCursorAndButtons(); + ImGui_ImplOSX_UpdateMouseCursor(); ImGui_ImplOSX_UpdateGamepads(); ImGui_ImplOSX_UpdateImePosWithView(view); } @@ -574,16 +565,16 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) { int button = (int)[event buttonNumber]; - if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) - g_MouseDown[button] = g_MouseJustPressed[button] = true; + if (button >= 0 && button < ImGuiMouseButton_COUNT) + io.AddMouseButtonEvent(button, true); return io.WantCaptureMouse; } if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp) { int button = (int)[event buttonNumber]; - if (button >= 0 && button < IM_ARRAYSIZE(g_MouseDown)) - g_MouseDown[button] = false; + if (button >= 0 && button < ImGuiMouseButton_COUNT) + io.AddMouseButtonEvent(button, false); return io.WantCaptureMouse; } @@ -592,7 +583,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) NSPoint mousePoint = event.locationInWindow; mousePoint = [view convertPoint:mousePoint fromView:nil]; mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); - io.MousePos = ImVec2((float)mousePoint.x, (float)mousePoint.y); + io.AddMousePosEvent((float)mousePoint.x, (float)mousePoint.y); } if (event.type == NSEventTypeScrollWheel) @@ -632,11 +623,9 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) wheel_dx = [event deltaX]; wheel_dy = [event deltaY]; } + if (wheel_dx != 0.0 || wheel_dx != 0.0) + io.AddMouseWheelEvent((float)wheel_dx * 0.1f, (float)wheel_dy * 0.1f); - if (fabs(wheel_dx) > 0.0) - io.MouseWheelH += (float)wheel_dx * 0.1f; - if (fabs(wheel_dy) > 0.0) - io.MouseWheel += (float)wheel_dy * 0.1f; return io.WantCaptureMouse; } diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 480846b9..a04233c7 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. @@ -78,7 +79,6 @@ struct ImGui_ImplSDL2_Data SDL_Window* Window; Uint64 Time; int MouseButtonsDown; - bool MousePressed[3]; SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; char* ClipboardTextData; bool MouseCanUseGlobalState; @@ -248,15 +248,14 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { case SDL_MOUSEMOTION: { - io.MousePos = ImVec2((float)event->motion.x, (float)event->motion.y); + io.AddMousePosEvent((float)event->motion.x, (float)event->motion.y); return true; } case SDL_MOUSEWHEEL: { - if (event->wheel.x > 0) io.MouseWheelH += 1; - if (event->wheel.x < 0) io.MouseWheelH -= 1; - if (event->wheel.y > 0) io.MouseWheel += 1; - if (event->wheel.y < 0) io.MouseWheel -= 1; + float wheel_x = (event->wheel.x > 0) ? 1.0f : (event->wheel.x < 0) ? -1.0f : 0.0f; + float wheel_y = (event->wheel.y > 0) ? 1.0f : (event->wheel.y < 0) ? -1.0f : 0.0f; + io.AddMouseWheelEvent(wheel_x, wheel_y); return true; } case SDL_MOUSEBUTTONDOWN: @@ -268,8 +267,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; } if (mouse_button == -1) break; - if (event->type == SDL_MOUSEBUTTONDOWN) - bd->MousePressed[mouse_button] = true; + io.AddMouseButtonEvent(mouse_button, (event->type == SDL_MOUSEBUTTONDOWN)); bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button)); return true; } @@ -290,7 +288,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_WINDOWEVENT: { if (event->window.event == SDL_WINDOWEVENT_LEAVE) - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) @@ -438,16 +436,9 @@ static void ImGui_ImplSDL2_UpdateMouseData() int window_x, window_y, mouse_x_global, mouse_y_global; SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); SDL_GetWindowPosition(bd->Window, &window_x, &window_y); - io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); + io.AddMousePosEvent((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); } } - - // Update buttons - Uint32 mouse_buttons = SDL_GetMouseState(NULL, NULL); - io.MouseDown[0] = bd->MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; - bd->MousePressed[0] = bd->MousePressed[1] = bd->MousePressed[2] = false; } static void ImGui_ImplSDL2_UpdateMouseCursor() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 2b140b54..db3462fa 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -34,6 +34,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before a key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. @@ -265,7 +266,7 @@ static void ImGui_ImplWin32_UpdateMouseData() { POINT pos; if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); + io.AddMousePosEvent((float)pos.x, (float)pos.y); } } } @@ -510,13 +511,13 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA ::TrackMouseEvent(&tme); bd->MouseTracked = true; } - io.MousePos = ImVec2((float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam)); + io.AddMousePosEvent((float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam)); break; case WM_MOUSELEAVE: if (bd->MouseHwnd == hwnd) bd->MouseHwnd = NULL; bd->MouseTracked = false; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); break; case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: @@ -531,7 +532,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (bd->MouseButtonsDown == 0 && ::GetCapture() == NULL) ::SetCapture(hwnd); bd->MouseButtonsDown |= 1 << button; - io.MouseDown[button] = true; + io.AddMouseButtonEvent(button, true); return 0; } case WM_LBUTTONUP: @@ -547,14 +548,14 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA bd->MouseButtonsDown &= ~(1 << button); if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd) ::ReleaseCapture(); - io.MouseDown[button] = false; + io.AddMouseButtonEvent(button, false); return 0; } case WM_MOUSEWHEEL: - io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; + io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); return 0; case WM_MOUSEHWHEEL: - io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; + io.AddMouseWheelEvent((float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); return 0; case WM_KEYDOWN: case WM_KEYUP: diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 69f7b4fd..3104582c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,7 +38,10 @@ HOW TO UPDATE? Breaking Changes: - Removed support for pre-C++11 compilers. We'll stop supporting VS2010. (#4537) -- Reworked IO keyboard input system. (#2625, #3724) [@thedmd, @ocornut] +- Reworked IO mouse input API: (#4858) [@thedmd, @ocornut] + - Added io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions, + obsoleting writing directly to io.MousePos, io.MouseDown[], io.MouseWheel, etc. +- Reworked IO keyboard input API: (#2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. @@ -47,9 +50,10 @@ Breaking Changes: - Obsoleted GetKeyIndex(): it is now unnecessary and will now return the same value. - All keyboard related functions taking 'int user_key_index' now take 'ImGuiKey key': - IsKeyDown(), IsKeyPressed(), IsKeyReleased(), GetKeyPressedAmount(). - - All backends were updated to use io.AddKeyEvent(). + - Added io.ConfigInputEventQueue (defaulting to true) to disable input queue trickling. - Backward compatibility: - - Old backends populating those arrays should still work! (for a while) + - All backends updated to use new functions. + - Old backends populating those arrays should still work! - Calling e.g. IsKeyPressed(MY_NATIVE_KEY_XXX) will still work! (for a while) - Those legacy arrays will only be disabled if '#define IMGUI_DISABLE_OBSOLETE_KEYIO' is set in your imconfig. In a few versions, IMGUI_DISABLE_OBSOLETE_FUNCTIONS will automatically enable IMGUI_DISABLE_OBSOLETE_KEYIO, @@ -104,16 +108,21 @@ Other Changes: We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) - Backends: GLFW: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: GLFW: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: GLFW: Update mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. - Backends: Win32: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. +- Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. - Backends: SDL: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: SDL: Update mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. +- Backends: SDL: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: Allegro5: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: Allegro5: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: Android, GLUT, OSX: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: Android, GLUT, OSX: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] diff --git a/imgui.cpp b/imgui.cpp index b84b2b0f..c4a3f902 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1262,7 +1262,7 @@ void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) KeysData[keydata_index].Down = down; } -// [Optional] Call add AddKeyEvent(). +// [Optional] Call after AddKeyEvent(). // Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices. // If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) @@ -1294,6 +1294,32 @@ void ImGuiIO::AddKeyModsEvent(ImGuiKeyModFlags modifiers) KeySuper = (modifiers & ImGuiKeyModFlags_Super) != 0; } +// Queue a mouse move event +void ImGuiIO::AddMousePosEvent(float x, float y) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + g.IO.MousePos = ImVec2(x, y); +} + +void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + if (wheel_x == 0.0f && wheel_y == 0.0f) + return; + g.IO.MouseWheelH += wheel_x; + g.IO.MouseWheel += wheel_y; +} + +void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); + g.IO.MouseDown[mouse_button] = down; +} + void ImGuiIO::AddFocusEvent(bool focused) { // We intentionally overwrite this and process in NewFrame(), in order to give a chance diff --git a/imgui.h b/imgui.h index 1f5df0cc..e7b9091a 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18606 +#define IMGUI_VERSION_NUM 18607 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1994,10 +1994,14 @@ struct ImGuiIO // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers);// Queue a change of Ctrl/Shift/Alt/Super modifiers + IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) + IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change + IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually IMGUI_API void ClearInputKeys(); // [Internal] Release all keys 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. From 7374b96f5cdf9adb1a1da033623ce2a4fd7f4934 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 17 Jan 2022 17:32:25 +0100 Subject: [PATCH 273/332] IO: Added input queue. (#4858, #2787, #1992, #3383, #2525, #1320) --- docs/CHANGELOG.txt | 4 + imgui.cpp | 219 ++++++++++++++++++++++++++++++++++++++------- imgui.h | 3 +- imgui_demo.cpp | 4 +- imgui_internal.h | 46 ++++++++++ 5 files changed, 244 insertions(+), 32 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3104582c..e0b345ac 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,7 @@ Breaking Changes: - Reworked IO mouse input API: (#4858) [@thedmd, @ocornut] - Added io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions, obsoleting writing directly to io.MousePos, io.MouseDown[], io.MouseWheel, etc. + - This enable input queue trickling to support low framerates. (#2787, #1992, #3383, #2525, #1320) - Reworked IO keyboard input API: (#2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. @@ -94,6 +95,9 @@ Breaking Changes: Other Changes: +- IO: Added event based input queue API, which now trickles events to support low framerates. [@thedmd, @ocornut] + Previously the most common issue case (button presses in low framerates) was handled by backend. This is now + handled by core automatically for all kind of inputs. (#4858, #2787, #1992, #3383, #2525, #1320) - Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in diff --git a/imgui.cpp b/imgui.cpp index c4a3f902..40d014bd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1136,6 +1136,7 @@ ImGuiIO::ImGuiIO() #else ConfigMacOSXBehaviors = false; #endif + ConfigInputEventQueue = true; ConfigInputTextCursorBlink = true; ConfigWindowsResizeFromEdges = true; ConfigWindowsMoveFromTitleBarOnly = false; @@ -1162,10 +1163,19 @@ ImGuiIO::ImGuiIO() // Pass in translated ASCII characters for text input. // - with glfw you can get those from the callback set in glfwSetCharCallback() // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message +// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API void ImGuiIO::AddInputCharacter(unsigned int c) { - if (c != 0) - InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + if (c == 0) + return; + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_Char; + e.Source = ImGuiInputSource_Keyboard; + e.Text.Char = c; + g.InputEventsQueue.push_back(e); } // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so @@ -1178,7 +1188,7 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) if ((c & 0xFC00) == 0xD800) // High surrogate, must save { if (InputQueueSurrogate != 0) - InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); + AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); InputQueueSurrogate = c; return; } @@ -1188,7 +1198,7 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) { if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate { - InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); + AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID); } else { @@ -1201,7 +1211,7 @@ void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) InputQueueSurrogate = 0; } - InputQueueCharacters.push_back(cp); + AddInputCharacter((unsigned)cp); } void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) @@ -1211,7 +1221,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) unsigned int c = 0; utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); if (c != 0) - InputQueueCharacters.push_back((ImWchar)c); + AddInputCharacter(c); } } @@ -1240,12 +1250,13 @@ void ImGuiIO::ClearInputKeys() // Queue a new key down/up event. // - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) // - bool down: Is the key down? use false to signify a key release. -// FIXME: In the current version this is setting key data immediately. This will evolve into a trickling queue. void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) { //if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } if (key == ImGuiKey_None) return; + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); IM_ASSERT(ImGui::IsNamedKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. @@ -1254,12 +1265,15 @@ void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) if (BackendUsingLegacyKeyArrays == -1) for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); -#endif BackendUsingLegacyKeyArrays = 0; +#endif - // Write key - const int keydata_index = (key - ImGuiKey_KeysData_OFFSET); - KeysData[keydata_index].Down = down; + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_Key; + e.Source = ImGuiInputSource_Keyboard; + e.Key.Key = key; + e.Key.Down = down; + g.InputEventsQueue.push_back(e); } // [Optional] Call after AddKeyEvent(). @@ -1287,11 +1301,14 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native void ImGuiIO::AddKeyModsEvent(ImGuiKeyModFlags modifiers) { - KeyMods = modifiers; - KeyCtrl = (modifiers & ImGuiKeyModFlags_Ctrl) != 0; - KeyShift = (modifiers & ImGuiKeyModFlags_Shift) != 0; - KeyAlt = (modifiers & ImGuiKeyModFlags_Alt) != 0; - KeySuper = (modifiers & ImGuiKeyModFlags_Super) != 0; + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_KeyMods; + e.Source = ImGuiInputSource_Keyboard; + e.KeyMods.Mods = modifiers; + g.InputEventsQueue.push_back(e); } // Queue a mouse move event @@ -1299,7 +1316,13 @@ void ImGuiIO::AddMousePosEvent(float x, float y) { ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); - g.IO.MousePos = ImVec2(x, y); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MousePos; + e.Source = ImGuiInputSource_Mouse; + e.MousePos.PosX = x; + e.MousePos.PosY = y; + g.InputEventsQueue.push_back(e); } void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) @@ -1308,8 +1331,13 @@ void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) IM_ASSERT(&g.IO == this && "Can only add events to current context."); if (wheel_x == 0.0f && wheel_y == 0.0f) return; - g.IO.MouseWheelH += wheel_x; - g.IO.MouseWheel += wheel_y; + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MouseWheel; + e.Source = ImGuiInputSource_Mouse; + e.MouseWheel.WheelX = wheel_x; + e.MouseWheel.WheelY = wheel_y; + g.InputEventsQueue.push_back(e); } void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) @@ -1317,14 +1345,24 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); - g.IO.MouseDown[mouse_button] = down; + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MouseButton; + e.Source = ImGuiInputSource_Mouse; + e.MouseButton.Button = mouse_button; + e.MouseButton.Down = down; + g.InputEventsQueue.push_back(e); } void ImGuiIO::AddFocusEvent(bool focused) { - // We intentionally overwrite this and process in NewFrame(), in order to give a chance - // to multi-viewports backends to queue AddFocusEvent(false),AddFocusEvent(true) in same frame. - AppFocusLost = !focused; + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_Focus; + e.AppFocused.Focused = focused; + g.InputEventsQueue.push_back(e); } //----------------------------------------------------------------------------- @@ -4261,13 +4299,9 @@ void ImGui::NewFrame() //if (g.IO.AppFocusLost) // ClosePopupsExceptModals(); - // Clear buttons state when focus is lost - // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) - if (g.IO.AppFocusLost) - { - g.IO.ClearInputKeys(); - g.IO.AppFocusLost = false; - } + // Process input queue (trickle as many events as possible) + g.InputEventsTrail.resize(0); + UpdateInputEvents(g.IO.ConfigInputEventQueue); // Update keyboard input state UpdateKeyboardInputs(); @@ -7680,6 +7714,131 @@ static const char* GetInputSourceName(ImGuiInputSource source) } +// Process input queue +// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost) +// - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87) +void ImGui::UpdateInputEvents(bool trickle_fast_inputs) +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + + bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputed = false; + int mouse_button_changed = 0x00, key_mods_changed = 0x00; + ImBitArray key_changed_mask; + + int event_n = 0; + for (; event_n < g.InputEventsQueue.Size; event_n++) + { + const ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; + if (e->Type == ImGuiInputEventType_MousePos) + { + if (io.MousePos.x != e->MousePos.PosX || io.MousePos.y != e->MousePos.PosY) + { + // Trickling Rule: Stop processing queued events if we already handled a mouse button change + if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || key_mods_changed || text_inputed)) + break; + io.MousePos = ImVec2(e->MousePos.PosX, e->MousePos.PosY); + mouse_moved = true; + } + } + else if (e->Type == ImGuiInputEventType_MouseButton) + { + const ImGuiMouseButton button = e->MouseButton.Button; + IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); + if (io.MouseDown[button] != e->MouseButton.Down) + { + // Trickling Rule: Stop processing queued events if we got multiple action on the same button + if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) + break; + io.MouseDown[button] = e->MouseButton.Down; + mouse_button_changed |= (1 << button); + } + } + else if (e->Type == ImGuiInputEventType_MouseWheel) + { + if (e->MouseWheel.WheelX != 0.0f || e->MouseWheel.WheelY != 0.0f) + { + // Trickling Rule: Stop processing queued events if we got multiple action on the event + if (trickle_fast_inputs && (mouse_wheeled || mouse_button_changed != 0)) + break; + io.MouseWheelH += e->MouseWheel.WheelX; + io.MouseWheel += e->MouseWheel.WheelY; + mouse_wheeled = true; + } + } + else if (e->Type == ImGuiInputEventType_Key) + { + IM_ASSERT(e->Key.Key != ImGuiKey_None); + const int keydata_index = (e->Key.Key - ImGuiKey_KeysData_OFFSET); + ImGuiKeyData* keydata = &io.KeysData[keydata_index]; + if (keydata->Down != e->Key.Down) + { + // Trickling Rule: Stop processing queued events if we got multiple action on the same button + if (trickle_fast_inputs && (key_changed_mask.TestBit(keydata_index) || text_inputed || mouse_button_changed != 0)) + break; + keydata->Down = e->Key.Down; + key_changed = true; + key_changed_mask.SetBit(keydata_index); + } + } + else if (e->Type == ImGuiInputEventType_KeyMods) + { + const ImGuiKeyModFlags modifiers = e->KeyMods.Mods; + if (io.KeyMods != modifiers) + { + // Trickling Rule: Stop processing queued events if we got multiple action on the same button + ImGuiKeyModFlags modifiers_that_are_changing = (io.KeyMods ^ modifiers); + if (trickle_fast_inputs && (key_mods_changed & modifiers_that_are_changing) != 0) + break; + io.KeyMods = modifiers; + io.KeyCtrl = (modifiers & ImGuiKeyModFlags_Ctrl) != 0; + io.KeyShift = (modifiers & ImGuiKeyModFlags_Shift) != 0; + io.KeyAlt = (modifiers & ImGuiKeyModFlags_Alt) != 0; + io.KeySuper = (modifiers & ImGuiKeyModFlags_Super) != 0; + key_mods_changed |= modifiers_that_are_changing; + } + } + else if (e->Type == ImGuiInputEventType_Char) + { + // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with + if (trickle_fast_inputs && (key_changed || mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) + break; + unsigned int c = e->Text.Char; + io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); + text_inputed = true; + } + else if (e->Type == ImGuiInputEventType_Focus) + { + // We intentionally overwrite this and process lower, in order to give a chance + // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame. + io.AppFocusLost = !e->AppFocused.Focused; + } + else + { + IM_ASSERT(0 && "Unknown event!"); + } + } + + // Record trail (for domain-specific applications wanting to access a precise trail) + for (int n = 0; n < event_n; n++) + g.InputEventsTrail.push_back(g.InputEventsQueue[n]); + + // Remaining events will be processed on the next frame + if (event_n == g.InputEventsQueue.Size) + g.InputEventsQueue.resize(0); + else + g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n); + + // Clear buttons state when focus is lost + // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + if (g.IO.AppFocusLost) + { + g.IO.ClearInputKeys(); + g.IO.AppFocusLost = false; + } +} + + //----------------------------------------------------------------------------- // [SECTION] ERROR CHECKING //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index e7b9091a..580af603 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18607 +#define IMGUI_VERSION_NUM 18608 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1944,6 +1944,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 ConfigInputEventQueue; // = 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 ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5c5bc3a3..9b894c4b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -460,8 +460,10 @@ void ImGui::ShowDemoWindow(bool* p_open) } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); + ImGui::Checkbox("io.ConfigInputEventQueue", &io.ConfigInputEventQueue); + ImGui::SameLine(); HelpMarker("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."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); - ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)"); + ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)."); ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText); ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving)."); ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); diff --git a/imgui_internal.h b/imgui_internal.h index bff1a61f..5cc7a4d4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -199,8 +199,10 @@ namespace ImStb // Debug Logging for selected systems. Remove the '((void)0) //' to enable. //#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log //#define IMGUI_DEBUG_LOG_NAV IMGUI_DEBUG_LOG // Enable log +//#define IMGUI_DEBUG_LOG_IO IMGUI_DEBUG_LOG // Enable log #define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // Disable log #define IMGUI_DEBUG_LOG_NAV(...) ((void)0) // Disable log +#define IMGUI_DEBUG_LOG_IO(...) ((void)0) // Disable log // Static Asserts #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") @@ -896,6 +898,19 @@ enum ImGuiPlotType ImGuiPlotType_Histogram }; +enum ImGuiInputEventType +{ + ImGuiInputEventType_None = 0, + ImGuiInputEventType_MousePos, + ImGuiInputEventType_MouseWheel, + ImGuiInputEventType_MouseButton, + ImGuiInputEventType_Key, + ImGuiInputEventType_KeyMods, + ImGuiInputEventType_Char, + ImGuiInputEventType_Focus, + ImGuiInputEventType_COUNT +}; + enum ImGuiInputSource { ImGuiInputSource_None = 0, @@ -907,6 +922,34 @@ enum ImGuiInputSource ImGuiInputSource_COUNT }; +// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? +// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' +struct ImGuiInputEventMousePos { float PosX, PosY; }; +struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; +struct ImGuiInputEventMouseButton { int Button; bool Down; }; +struct ImGuiInputEventKey { ImGuiKey Key; bool Down; }; +struct ImGuiInputEventKeyMods { ImGuiKeyModFlags Mods; }; +struct ImGuiInputEventText { unsigned int Char; }; +struct ImGuiInputEventAppFocused { bool Focused; }; + +struct ImGuiInputEvent +{ + ImGuiInputEventType Type; + ImGuiInputSource Source; + union + { + ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos + ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel + ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton + ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key + ImGuiInputEventKeyMods KeyMods; // if Type == ImGuiInputEventType_Modifiers + ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text + ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus + }; + + ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } +}; + // FIXME-NAV: Clarify/expose various repeat delay/rate enum ImGuiInputReadMode { @@ -1498,6 +1541,8 @@ struct ImGuiContext bool Initialized; bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; + ImVector InputEventsQueue; // Input events which will be tricked/written into IO structure. + ImVector InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail. ImGuiStyle Style; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. @@ -2453,6 +2498,7 @@ namespace ImGui IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). // NewFrame + IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); IMGUI_API void UpdateMouseMovingWindowNewFrame(); From 90a6961638e3a7244d89969390427e5d94e856f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 17 Jan 2022 18:10:51 +0100 Subject: [PATCH 274/332] IO: apply same flooring as UpdateMouseInputs() in dupe event processing. (#4858) + provision for test engine. --- imgui.cpp | 9 ++++++--- imgui_internal.h | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 40d014bd..8a58f8ac 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3946,7 +3946,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 = ImFloor(io.MousePos); + io.MousePos = g.MouseLastValidPos = ImFloorSigned(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)) @@ -7732,12 +7732,15 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) const ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; if (e->Type == ImGuiInputEventType_MousePos) { - if (io.MousePos.x != e->MousePos.PosX || io.MousePos.y != e->MousePos.PosY) + ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY); + if (IsMousePosValid(&event_pos)) + event_pos = ImVec2(ImFloorSigned(event_pos.x), ImFloorSigned(event_pos.y)); // Apply same flooring as UpdateMouseInputs() + if (io.MousePos.x != event_pos.x || io.MousePos.y != event_pos.y) { // Trickling Rule: Stop processing queued events if we already handled a mouse button change if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || key_mods_changed || text_inputed)) break; - io.MousePos = ImVec2(e->MousePos.PosX, e->MousePos.PosY); + io.MousePos = event_pos; mouse_moved = true; } } diff --git a/imgui_internal.h b/imgui_internal.h index 5cc7a4d4..62900be6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -441,6 +441,7 @@ static inline float ImInvLength(const ImVec2& lhs, float 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 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); } @@ -936,6 +937,7 @@ struct ImGuiInputEvent { ImGuiInputEventType Type; ImGuiInputSource Source; + bool SubmittedByTestEngine; union { ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos From 007a427e0af93529e483ebc78e50231bb055a8d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 18 Jan 2022 16:04:25 +0100 Subject: [PATCH 275/332] Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1b6fe962..92780a28 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -208,6 +208,7 @@ Docking+Viewports Branch: - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) +- Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) - (Breaking) Removed ImGuiPlatformIO::Platform_SetImeInputPos() in favor of newly standardized io.SetPlatformImeDataFn() function. Should not affect more than default backends. diff --git a/imgui.cpp b/imgui.cpp index ce2c9dd3..1420f67d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12925,7 +12925,8 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) { // Merge into host viewport? // We cannot test window->ViewportOwned as it set lower in the function. - bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0); + // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212) + bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap)); if (try_to_merge_into_host_viewport) UpdateTryMergeWindowIntoHostViewports(window); } From 1338eb31f73a7ee1a3dda8cfc6f1e46970e5288c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 18 Jan 2022 16:59:19 +0100 Subject: [PATCH 276/332] Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport. Backends: SDL: Added support for simplified HasMouseHoveredViewport. (#1542, #4665) --- backends/imgui_impl_glfw.cpp | 16 ++++++++++------ backends/imgui_impl_osx.mm | 2 +- backends/imgui_impl_sdl.cpp | 22 +++++++++++++++++++++- backends/imgui_impl_win32.cpp | 16 ++++++++-------- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 12 +++++------- imgui.h | 4 ++-- 7 files changed, 56 insertions(+), 25 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 34790fbe..e9ca12ed 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -578,12 +578,14 @@ static void ImGui_ImplGlfw_UpdateMouseData() } // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. - // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because - // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows). - // - This is _regardless_ of whether another viewport is focused or being dragged from. - // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, imgui will ignore this field and infer the information by relying on the - // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows. - // [GLFW] FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag. + // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). + // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. #if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0; @@ -592,6 +594,8 @@ static void ImGui_ImplGlfw_UpdateMouseData() #endif if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input) io.MouseHoveredViewport = viewport->ID; +#else + // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. #endif } } diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 3e00b8a3..fb624f6c 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -359,7 +359,7 @@ bool ImGui_ImplOSX_Init(NSView* view) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) io.BackendPlatformName = "imgui_impl_osx"; // Load cursors. Some of them are undocumented. diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 3a488137..3e723773 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -89,6 +89,7 @@ struct ImGui_ImplSDL2_Data { SDL_Window* Window; Uint64 Time; + Uint32 MouseWindowID; int MouseButtonsDown; SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; char* ClipboardTextData; @@ -312,9 +313,16 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_WINDOWEVENT: { + // When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. + // However we won't get a correct LEAVE event for a captured window. Uint8 window_event = event->window.event; + if (window_event == SDL_WINDOWEVENT_ENTER) + bd->MouseWindowID = event->window.windowID; if (window_event == SDL_WINDOWEVENT_LEAVE) + { + bd->MouseWindowID = 0; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); else if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) @@ -359,7 +367,10 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) if (mouse_can_use_global_state) + { io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;//We can set io.MouseHoveredViewport correctly (optional) + } bd->Window = window; bd->MouseCanUseGlobalState = mouse_can_use_global_state; @@ -510,8 +521,17 @@ static void ImGui_ImplSDL2_UpdateMouseData() } } - // We don't support ImGuiBackendFlags_HasMouseHoveredViewport + // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). io.MouseHoveredViewport = 0; + if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) + if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) + io.MouseHoveredViewport = mouse_viewport->ID; } static void ImGui_ImplSDL2_UpdateMouseCursor() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index a4fb035c..7d7ff0fe 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -131,7 +131,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) bd->hWnd = (HWND)hwnd; bd->WantUpdateHasGamepad = true; @@ -300,17 +300,17 @@ static void ImGui_ImplWin32_UpdateMouseData() } // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. - // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because - // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows). - // - This is _regardless_ of whether another viewport is focused or being dragged from. - // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, imgui will ignore this field and infer the information by relying on the - // rectangles and last focused time of every viewports it knows about. It will be unaware of foreign windows that may be sitting between or over your windows. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that) + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). io.MouseHoveredViewport = 0; if (has_mouse_screen_pos) if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos)) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) - if ((viewport->Flags & ImGuiViewportFlags_NoInputs) == 0) // FIXME: We still get our NoInputs window with WM_NCHITTEST/HTTRANSPARENT code when decorated? - io.MouseHoveredViewport = viewport->ID; + io.MouseHoveredViewport = viewport->ID; } // Gamepad navigation mapping diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 92780a28..84676898 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -206,9 +206,18 @@ Other Changes: Docking+Viewports Branch: +- Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ + for the backend to have to ignore viewports with the _NoInputs flag when setting io.MouseHoveredViewport. It is + much better if they can (Win32 and GLFW 3.3+ backends can, SDL and GLFW 3.2 backends cannot, they are lacking data). + A concrete example is: when dragging a viewport for docking, the viewport is marked with _NoInputs to allow us + to pick the target viewports for docking. If the backend reports a viewport with _NoInputs in io.MouseHoveredViewport, + then Dear ImGui will revert to its flawed heuristic to find the viewport under. + By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few + drag and drop situations rather that relying on it everywhere. - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) - Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) +- Backends: SDL: Added support for ImGuiBackendFlags_HasMouseHoveredViewport now that its specs have been lowered. - (Breaking) Removed ImGuiPlatformIO::Platform_SetImeInputPos() in favor of newly standardized io.SetPlatformImeDataFn() function. Should not affect more than default backends. diff --git a/imgui.cpp b/imgui.cpp index 1420f67d..b67202e2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12701,17 +12701,14 @@ static void ImGui::UpdateViewportsNewFrame() { viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - { - // Backend failed at honoring its contract if it returned a viewport with the _NoInputs flag. - IM_ASSERT(0); - viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); - } + viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback. } else { // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. - // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) + // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order. + // C) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); } if (viewport_hovered != NULL) @@ -17663,7 +17660,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } - if (TreeNode("Inferred order (front-to-back)")) + BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); + if (TreeNode("Inferred Z order (front-to-back)")) { static ImVector viewports; viewports.resize(g.Viewports.Size); diff --git a/imgui.h b/imgui.h index 97ee1dcb..5f63e704 100644 --- a/imgui.h +++ b/imgui.h @@ -1611,7 +1611,7 @@ enum ImGuiBackendFlags_ // [BETA] Viewports ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Backend Platform supports multiple viewports. - ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines! Don't set this without studying _carefully_ how the backends handle ImGuiViewportFlags_NoInputs! + ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. ImGuiBackendFlags_RendererHasViewports = 1 << 12 // Backend Renderer supports multiple viewports. }; @@ -2076,7 +2076,7 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). + ImGuiID MouseHoveredViewport; // (Optional) With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). bool KeyCtrl; // Keyboard modifier down: Control bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt From e278277d53c961b6b345388b79644187bd0d0339 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 18 Jan 2022 17:24:59 +0100 Subject: [PATCH 277/332] IO: added AddMouseViewportEvent() + used in backends. --- backends/imgui_impl_glfw.cpp | 14 ++++++++------ backends/imgui_impl_osx.mm | 2 +- backends/imgui_impl_sdl.cpp | 9 +++++---- backends/imgui_impl_win32.cpp | 11 ++++++----- docs/CHANGELOG.txt | 9 +++++---- imgui.cpp | 17 +++++++++++++++++ imgui.h | 5 +++-- imgui_internal.h | 3 +++ 8 files changed, 48 insertions(+), 22 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index e9ca12ed..f6aad1b5 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -418,7 +418,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) #endif bd->Window = window; @@ -539,8 +539,7 @@ static void ImGui_ImplGlfw_UpdateMouseData() ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - io.MouseHoveredViewport = 0; - + ImGuiID mouse_viewport_id = 0; const ImVec2 mouse_pos_prev = io.MousePos; for (int n = 0; n < platform_io.Viewports.Size; n++) { @@ -577,7 +576,7 @@ static void ImGui_ImplGlfw_UpdateMouseData() } } - // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag. // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. @@ -593,11 +592,14 @@ static void ImGui_ImplGlfw_UpdateMouseData() glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input); #endif if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input) - io.MouseHoveredViewport = viewport->ID; + mouse_viewport_id = viewport->ID; #else // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. #endif } + + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + io.AddMouseViewportEvent(mouse_viewport_id); } static void ImGui_ImplGlfw_UpdateMouseCursor() @@ -866,7 +868,7 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR { if (msg == WM_NCHITTEST) { - // Let mouse pass-through the window. This will allow the backend to set io.MouseHoveredViewport properly (which is OPTIONAL). + // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index fb624f6c..9ca9fe5a 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -359,7 +359,7 @@ bool ImGui_ImplOSX_Init(NSView* view) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) + //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) io.BackendPlatformName = "imgui_impl_osx"; // Load cursors. Some of them are undocumented. diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 3e723773..7137d746 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -369,7 +369,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) if (mouse_can_use_global_state) { io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;//We can set io.MouseHoveredViewport correctly (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;// We can call io.AddMouseViewportEvent() with correct data (optional) } bd->Window = window; @@ -521,17 +521,18 @@ static void ImGui_ImplSDL2_UpdateMouseData() } } - // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag. // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). - io.MouseHoveredViewport = 0; + ImGuiID mouse_viewport_id = 0; if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) - io.MouseHoveredViewport = mouse_viewport->ID; + mouse_viewport_id = mouse_viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); } static void ImGui_ImplSDL2_UpdateMouseCursor() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 7d7ff0fe..017baa57 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -131,7 +131,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) bd->hWnd = (HWND)hwnd; bd->WantUpdateHasGamepad = true; @@ -299,18 +299,19 @@ static void ImGui_ImplWin32_UpdateMouseData() } } - // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that) // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). - io.MouseHoveredViewport = 0; + ImGuiID mouse_viewport_id = 0; if (has_mouse_screen_pos) if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos)) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) - io.MouseHoveredViewport = viewport->ID; + mouse_viewport_id = viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); } // Gamepad navigation mapping @@ -1077,7 +1078,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, return MA_NOACTIVATE; break; case WM_NCHITTEST: - // Let mouse pass-through the window. This will allow the backend to set io.MouseHoveredViewport properly (which is OPTIONAL). + // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 84676898..e1fd7b53 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,7 +90,7 @@ Other changes: - Added io.ConfigViewportsNoDecoration option. - Added io.ConfigViewportsNoDefaultParent option. - Added ImGuiBackendFlags_PlatformHasViewports, ImGuiBackendFlags_RendererHasViewports, ImGuiBackendFlags_HasMouseHoveredViewport backend flags. -- Added io.MouseHoveredViewport (optional _even_ for multi-viewport support, tied to ImGuiBackendFlags_HasMouseHoveredViewport flag). +- Added io.AddMouseViewportEvent() (optional _even_ for multi-viewport support, tied to ImGuiBackendFlags_HasMouseHoveredViewport flag). - Expanded ImGuiViewport structure, ImGuiViewportFlags flags. - Added ImGuiWindowClass and SetNextWindowClass() for passing viewport related hints to the OS/platform back-end. - Examples: Renderer: OpenGL2, OpenGL3, DirectX9, DirectX10, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. @@ -206,12 +206,13 @@ Other Changes: Docking+Viewports Branch: +- Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ - for the backend to have to ignore viewports with the _NoInputs flag when setting io.MouseHoveredViewport. It is + for the backend to have to ignore viewports with the _NoInputs flag when call io.AddMouseViewportEvent(). It is much better if they can (Win32 and GLFW 3.3+ backends can, SDL and GLFW 3.2 backends cannot, they are lacking data). A concrete example is: when dragging a viewport for docking, the viewport is marked with _NoInputs to allow us - to pick the target viewports for docking. If the backend reports a viewport with _NoInputs in io.MouseHoveredViewport, - then Dear ImGui will revert to its flawed heuristic to find the viewport under. + to pick the target viewports for docking. If the backend reports a viewport with _NoInputs when calling the + io.AddMouseViewportEvent() function, then Dear ImGui will revert to its flawed heuristic to find the viewport under. By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few drag and drop situations rather that relying on it everywhere. - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in diff --git a/imgui.cpp b/imgui.cpp index b67202e2..644b3d7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1390,6 +1390,19 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) g.InputEventsQueue.push_back(e); } +void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MouseViewport; + e.Source = ImGuiInputSource_Mouse; + e.MouseViewport.HoveredViewportID = viewport_id; + g.InputEventsQueue.push_back(e); +} + void ImGuiIO::AddFocusEvent(bool focused) { ImGuiContext& g = *GImGui; @@ -8354,6 +8367,10 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) mouse_wheeled = true; } } + else if (e->Type == ImGuiInputEventType_MouseViewport) + { + io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID; + } else if (e->Type == ImGuiInputEventType_Key) { IM_ASSERT(e->Key.Key != ImGuiKey_None); diff --git a/imgui.h b/imgui.h index 5f63e704..3d60e3ac 100644 --- a/imgui.h +++ b/imgui.h @@ -1611,7 +1611,7 @@ enum ImGuiBackendFlags_ // [BETA] Viewports ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Backend Platform supports multiple viewports. - ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. + ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports calling io.AddMouseViewportEvent() with the viewport under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. ImGuiBackendFlags_RendererHasViewports = 1 << 12 // Backend Renderer supports multiple viewports. }; @@ -2076,7 +2076,7 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - ImGuiID MouseHoveredViewport; // (Optional) With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). + ImGuiID MouseHoveredViewport; // (Optional) Modify using io.AddMouseViewportEvent(). With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). bool KeyCtrl; // Keyboard modifier down: Control bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt @@ -2089,6 +2089,7 @@ struct ImGuiIO IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update + IMGUI_API void AddMouseViewportEvent(ImGuiID id); // Queue a mouse hovered viewport. Requires backend to set ImGuiBackendFlags_HasMouseHoveredViewport to call this. IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate diff --git a/imgui_internal.h b/imgui_internal.h index 9a5c7d9b..5f07d7f7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -917,6 +917,7 @@ enum ImGuiInputEventType ImGuiInputEventType_MousePos, ImGuiInputEventType_MouseWheel, ImGuiInputEventType_MouseButton, + ImGuiInputEventType_MouseViewport, ImGuiInputEventType_Key, ImGuiInputEventType_KeyMods, ImGuiInputEventType_Char, @@ -940,6 +941,7 @@ enum ImGuiInputSource struct ImGuiInputEventMousePos { float PosX, PosY; }; struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; struct ImGuiInputEventMouseButton { int Button; bool Down; }; +struct ImGuiInputEventMouseViewport { ImGuiID HoveredViewportID; }; struct ImGuiInputEventKey { ImGuiKey Key; bool Down; }; struct ImGuiInputEventKeyMods { ImGuiKeyModFlags Mods; }; struct ImGuiInputEventText { unsigned int Char; }; @@ -955,6 +957,7 @@ struct ImGuiInputEvent ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton + ImGuiInputEventMouseViewport MouseViewport; // if Type == ImGuiInputEventType_MouseViewport ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key ImGuiInputEventKeyMods KeyMods; // if Type == ImGuiInputEventType_Modifiers ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text From 673f5e588de32e5fc2bfd1d94a0d8a687ffdbc9b Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 18 Jan 2022 17:41:35 +0100 Subject: [PATCH 278/332] IO: moved fields to put more focus on the new functions + update misc docs/references + update main.mm in Apple+Metal example (#4858) --- docs/EXAMPLES.md | 2 +- docs/FAQ.md | 2 +- examples/example_apple_metal/main.mm | 4 ++-- imgui.cpp | 34 ++++++++++++++++----------- imgui.h | 35 +++++++++++++++------------- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 8321aa1e..cd849060 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -222,7 +222,7 @@ If you are interested in using Cmake to build and links examples, see: **About mouse cursor latency** Dear ImGui has no particular extra lag for most behaviors, -e.g. the value of 'io.MousePos' provided at the time of NewFrame() will result in windows being moved +e.g. the last value passed to 'io.AddMousePosEvent()' before NewFrame() will result in windows being moved to the right spot at the time of EndFrame()/Render(). At 60 FPS your experience should be pleasant. However, consider that OS mouse cursors are typically drawn through a very specific hardware accelerated diff --git a/docs/FAQ.md b/docs/FAQ.md index f6fc96c4..8c46d268 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -114,7 +114,7 @@ void MyLowLevelMouseButtonHandler(int button, bool down) { // (1) ALWAYS forward mouse data to ImGui! This is automatic with default backends. With your own backend: ImGuiIO& io = ImGui::GetIO(); - io.MouseDown[button] = down; + io.AddMouseButtonEvent(button, down); // (2) ONLY forward mouse data to your underlying app/game. if (!io.WantCaptureMouse) diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index 5c05fee5..d92fc6b3 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -238,7 +238,7 @@ UITouch *anyTouch = event.allTouches.anyObject; CGPoint touchLocation = [anyTouch locationInView:self.view]; ImGuiIO &io = ImGui::GetIO(); - io.MousePos = ImVec2(touchLocation.x, touchLocation.y); + io.AddMousePosEvent(touchLocation.x, touchLocation.y); BOOL hasActiveTouch = NO; for (UITouch *touch in event.allTouches) @@ -249,7 +249,7 @@ break; } } - io.MouseDown[0] = hasActiveTouch; + io.AddMouseButtonEvent(0, hasActiveTouch); } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self updateIOWithTouchEvent:event]; } diff --git a/imgui.cpp b/imgui.cpp index 8a58f8ac..00419b47 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -255,9 +255,9 @@ CODE io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) io.DisplaySize.x = 1920.0f; // set the current display width io.DisplaySize.y = 1280.0f; // set the current display height here - io.MousePos = my_mouse_pos; // set the mouse position - io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states - io.MouseDown[1] = my_mouse_buttons[1]; + io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position + io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states + io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states // Call NewFrame(), after this point you can use ImGui::* functions anytime // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere) @@ -386,6 +386,11 @@ 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. + - 2022/01/17 (1.87) - inputs: reworked mouse IO. + - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent() + - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent() + - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent() + - Backend writing to io.MouseHoveredViewpot -> backend should call io.AddMouseViewportEvent() [Docking branch only] - 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. - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) @@ -1325,32 +1330,33 @@ void ImGuiIO::AddMousePosEvent(float x, float y) g.InputEventsQueue.push_back(e); } -void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) +void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) { ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); - if (wheel_x == 0.0f && wheel_y == 0.0f) - return; + IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); ImGuiInputEvent e; - e.Type = ImGuiInputEventType_MouseWheel; + e.Type = ImGuiInputEventType_MouseButton; e.Source = ImGuiInputSource_Mouse; - e.MouseWheel.WheelX = wheel_x; - e.MouseWheel.WheelY = wheel_y; + e.MouseButton.Button = mouse_button; + e.MouseButton.Down = down; g.InputEventsQueue.push_back(e); } -void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) +// Queue a mouse wheel event (most mouse/API will only have a Y component) +void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) { ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); - IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); + if (wheel_x == 0.0f && wheel_y == 0.0f) + return; ImGuiInputEvent e; - e.Type = ImGuiInputEventType_MouseButton; + e.Type = ImGuiInputEventType_MouseWheel; e.Source = ImGuiInputSource_Mouse; - e.MouseButton.Button = mouse_button; - e.MouseButton.Down = down; + e.MouseWheel.WheelX = wheel_x; + e.MouseWheel.WheelY = wheel_y; g.InputEventsQueue.push_back(e); } diff --git a/imgui.h b/imgui.h index 580af603..e6828fe1 100644 --- a/imgui.h +++ b/imgui.h @@ -1979,29 +1979,19 @@ struct ImGuiIO #endif //------------------------------------------------------------------ - // Input - Fill before calling NewFrame() + // Input - Call before calling NewFrame() //------------------------------------------------------------------ - ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) - bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - bool KeyCtrl; // Keyboard modifier down: Control - bool KeyShift; // Keyboard modifier down: Shift - bool KeyAlt; // Keyboard modifier down: Alt - bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows - float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). - // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers);// Queue a change of Ctrl/Shift/Alt/Super modifiers IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update - IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus - IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate - IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string + IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) + IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input + IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually IMGUI_API void ClearInputKeys(); // [Internal] Release all keys @@ -2039,10 +2029,23 @@ struct ImGuiIO // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ + // Main Input State + // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead) + // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere) + ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) + bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. + float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. + bool KeyCtrl; // Keyboard modifier down: Control + bool KeyShift; // Keyboard modifier down: Shift + bool KeyAlt; // Keyboard modifier down: Alt + bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows + float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). + + // Other state maintained from data above + IO function calls ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() ImGuiKeyModFlags KeyModsPrev; // Key mods flags (from previous frame) 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) ImVec2 MouseClickedPos[5]; // Position at time of clicking From 91ae56af45ab43b3e66bd55a40cd59d325a08ab5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 19 Jan 2022 15:10:42 +0100 Subject: [PATCH 279/332] Removed support for legacy arithmetic operators (+*/) when inputing text into a slider/drag. (#4917, #3184) --- docs/CHANGELOG.txt | 4 +++ docs/TODO.txt | 1 - imgui.cpp | 2 +- imgui.h | 2 +- imgui_demo.cpp | 5 ---- imgui_internal.h | 2 +- imgui_widgets.cpp | 75 +++++----------------------------------------- 7 files changed, 15 insertions(+), 76 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e0b345ac..d2237295 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,9 @@ HOW TO UPDATE? Breaking Changes: - Removed support for pre-C++11 compilers. We'll stop supporting VS2010. (#4537) +- Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) + This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). + (Instead you may implement custom expression evaluators to provide a better version of this). - Reworked IO mouse input API: (#4858) [@thedmd, @ocornut] - Added io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions, obsoleting writing directly to io.MousePos, io.MouseDown[], io.MouseWheel, etc. @@ -102,6 +105,7 @@ Other Changes: which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] +- Sliders, Drags: Fixed text input of values with a leading sign, common when using a format enforcing sign. (#4917) - Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API, now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. - Platform IME: moved io.ImeWindowHandle to GetMainViewport()->PlatformHandleRaw. diff --git a/docs/TODO.txt b/docs/TODO.txt index 9d005ed4..cb80679d 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -113,7 +113,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - input number: optional range min/max for Input*() functions - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) - input number: use mouse wheel to step up/down - - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack. - layout: helper or a way to express ImGui::SameLine(ImGui::GetCursorStartPos().x + ImGui::CalcItemWidth() + ImGui::GetStyle().ItemInnerSpacing.x); in a simpler manner. - layout, font: horizontal tab support, A) text mode: forward only tabs (e.g. every 4 characters/N pixels from pos x1), B) manual mode: explicit tab stops acting as mini columns, no clipping (for menu items, many kind of uses, also vaguely relate to #267, #395) diff --git a/imgui.cpp b/imgui.cpp index 00419b47..a963da81 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -128,7 +128,6 @@ CODE - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ - CTRL+Z,CTRL+Y to undo/redo. - ESCAPE to revert text to its original value. - - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets @@ -386,6 +385,7 @@ 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. + - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). - 2022/01/17 (1.87) - inputs: reworked mouse IO. - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent() - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent() diff --git a/imgui.h b/imgui.h index e6828fe1..81a3df50 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18608 +#define IMGUI_VERSION_NUM 18609 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9b894c4b..e50cf038 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -229,7 +229,6 @@ void ImGui::ShowUserGuide() ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste."); ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo."); ImGui::BulletText("ESCAPE to revert."); - ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract."); ImGui::Unindent(); ImGui::BulletText("With keyboard navigation enabled:"); ImGui::Indent(); @@ -676,10 +675,6 @@ static void ShowDemoWindowWidgets() IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); static int i0 = 123; ImGui::InputInt("input int", &i0); - ImGui::SameLine(); HelpMarker( - "You can apply arithmetic operators +,*,/ on numerical values.\n" - " e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\n" - "Use +- to subtract."); static float f0 = 0.001f; ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f"); diff --git a/imgui_internal.h b/imgui_internal.h index 62900be6..b1b8f7f5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2805,7 +2805,7 @@ namespace ImGui IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format); IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2); - IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format); + IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format); IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ad9d44da..8f52f1fb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1992,24 +1992,10 @@ void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const // User can input math operators (e.g. +100) to edit a numerical values. // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format) +bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format) { while (ImCharIsBlankA(*buf)) buf++; - - // We don't support '-' op because it would conflict with inputing negative value. - // Instead you can use +-100 to subtract from an existing value - char op = buf[0]; - if (op == '+' || op == '*' || op == '/') - { - buf++; - while (ImCharIsBlankA(*buf)) - buf++; - } - else - { - op = 0; - } if (!buf[0]) return false; @@ -2021,54 +2007,11 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b if (format == NULL) format = type_info->ScanFmt; - // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. - int arg1i = 0; - if (data_type == ImGuiDataType_S32) - { - int* v = (int*)p_data; - int arg0i = *v; - float arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0i) < 1) - return false; - // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision - if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract) - else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply - else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide - else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant - } - else if (data_type == ImGuiDataType_Float) - { - // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in - format = "%f"; - float* v = (float*)p_data; - float arg0f = *v, arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - else if (data_type == ImGuiDataType_Double) + if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64 || data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) { - format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis - double* v = (double*)p_data; - double arg0f = *v, arg1f = 0.0; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) - { - // All other types assign constant - // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. + // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf + if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) + format = type_info->ScanFmt; if (sscanf(buf, format, p_data) < 1) return false; } @@ -3387,8 +3330,6 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* // However this may not be ideal for all uses, as some user code may break on out of bound values. bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) { - ImGuiContext& g = *GImGui; - char fmt_buf[32]; char data_buf[32]; format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); @@ -3406,7 +3347,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG memcpy(&data_backup, p_data, data_type_size); // Apply new value (or operations) then clamp - DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL); + DataTypeApplyFromText(data_buf, data_type, p_data, NULL); if (p_clamp_min || p_clamp_max) { if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) @@ -3453,7 +3394,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data PushID(label); SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); // Step buttons const ImVec2 backup_frame_padding = style.FramePadding; @@ -3490,7 +3431,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data else { if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); } if (value_changed) MarkItemEdited(g.LastItemData.ID); From 97b1ffcb3b7be5d8bf9e7202bbe914c21c0638e7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 19 Jan 2022 15:40:17 +0100 Subject: [PATCH 280/332] IO: Reorder enums, compact a little in header. Removed const from internal GetKeyData(). --- imgui.cpp | 12 ++--- imgui.h | 116 ++++++++++++----------------------------------- imgui_demo.cpp | 9 ++-- imgui_internal.h | 4 +- 4 files changed, 43 insertions(+), 98 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a963da81..7e2908c3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7444,7 +7444,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c return true; } -const ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) +ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; int index; @@ -7476,15 +7476,15 @@ static const char* const GKeyNames[] = { "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown", "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape", + "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu", + "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", "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", "LeftCtrl", "LeftShift", - "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu", - "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" + "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual" }; IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); diff --git a/imgui.h b/imgui.h index 81a3df50..06baf9f2 100644 --- a/imgui.h +++ b/imgui.h @@ -1342,8 +1342,8 @@ enum ImGuiSortDirection_ enum ImGuiKey_ { - ImGuiKey_None = 0, - ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN + ImGuiKey_None = 0, + ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, @@ -1358,32 +1358,33 @@ enum ImGuiKey_ ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, - ImGuiKey_Apostrophe, // ' - ImGuiKey_Comma, // , - ImGuiKey_Minus, // - - ImGuiKey_Period, // . - ImGuiKey_Slash, // / - ImGuiKey_Semicolon, // ; - ImGuiKey_Equal, // = - ImGuiKey_LeftBracket, // [ - ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backlash) - ImGuiKey_RightBracket, // ] - ImGuiKey_GraveAccent, // ` + ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper, + ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper, + ImGuiKey_Menu, + ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9, + ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J, + ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T, + 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_Apostrophe, // ' + ImGuiKey_Comma, // , + ImGuiKey_Minus, // - + ImGuiKey_Period, // . + ImGuiKey_Slash, // / + ImGuiKey_Semicolon, // ; + ImGuiKey_Equal, // = + ImGuiKey_LeftBracket, // [ + ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backslash) + ImGuiKey_RightBracket, // ] + ImGuiKey_GraveAccent, // ` ImGuiKey_CapsLock, ImGuiKey_ScrollLock, ImGuiKey_NumLock, ImGuiKey_PrintScreen, ImGuiKey_Pause, - ImGuiKey_Keypad0, - ImGuiKey_Keypad1, - ImGuiKey_Keypad2, - ImGuiKey_Keypad3, - ImGuiKey_Keypad4, - ImGuiKey_Keypad5, - ImGuiKey_Keypad6, - ImGuiKey_Keypad7, - ImGuiKey_Keypad8, - ImGuiKey_Keypad9, + ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4, + ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9, ImGuiKey_KeypadDecimal, ImGuiKey_KeypadDivide, ImGuiKey_KeypadMultiply, @@ -1391,78 +1392,21 @@ enum ImGuiKey_ ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, - ImGuiKey_LeftCtrl, - ImGuiKey_LeftShift, - ImGuiKey_LeftAlt, - ImGuiKey_LeftSuper, - ImGuiKey_RightCtrl, - ImGuiKey_RightShift, - ImGuiKey_RightAlt, - ImGuiKey_RightSuper, - ImGuiKey_Menu, - ImGuiKey_0, - ImGuiKey_1, - ImGuiKey_2, - ImGuiKey_3, - ImGuiKey_4, - ImGuiKey_5, - ImGuiKey_6, - ImGuiKey_7, - ImGuiKey_8, - ImGuiKey_9, - ImGuiKey_A, - ImGuiKey_B, - ImGuiKey_C, - ImGuiKey_D, - ImGuiKey_E, - ImGuiKey_F, - ImGuiKey_G, - ImGuiKey_H, - ImGuiKey_I, - ImGuiKey_J, - ImGuiKey_K, - ImGuiKey_L, - ImGuiKey_M, - ImGuiKey_N, - ImGuiKey_O, - ImGuiKey_P, - ImGuiKey_Q, - ImGuiKey_R, - ImGuiKey_S, - ImGuiKey_T, - 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_COUNT, // No valid ImGuiKey is ever greater than this value + ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value // Legacy range used by legacy io.KeyMap[]. Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index. // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) ImGuiKey_LegacyNativeKey_BEGIN = 0, - ImGuiKey_LegacyNativeKey_END = 512, // First index after valid range + ImGuiKey_LegacyNativeKey_END = 512, ImGuiKey_NamedKey_BEGIN = 512, ImGuiKey_NamedKey_END = ImGuiKey_COUNT, ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0] + ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys + ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0] #else - ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_LegacyNativeKey_BEGIN // First key stored in KeysData[0] + ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys + ImGuiKey_KeysData_OFFSET = ImGuiKey_LegacyNativeKey_BEGIN // First key stored in KeysData[0] #endif #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e50cf038..24dfbdf9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -483,6 +483,7 @@ void ImGui::ShowDemoWindow(bool* p_open) "Here we expose them as read-only fields to avoid breaking interactions with your backend."); // Make a local copy to avoid modifying actual backend flags. + // FIXME: We don't use BeginDisabled() to keep label bright, maybe we need a BeginReadonly() equivalent.. ImGuiBackendFlags backend_flags = io.BackendFlags; ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &backend_flags, ImGuiBackendFlags_HasGamepad); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &backend_flags, ImGuiBackendFlags_HasMouseCursors); @@ -5637,7 +5638,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } -namespace ImGui { extern const ImGuiKeyData* GetKeyData(ImGuiKey key); } +namespace ImGui { extern ImGuiKeyData* GetKeyData(ImGuiKey key); } static void ShowDemoWindowMisc() { @@ -5702,9 +5703,9 @@ static void ShowDemoWindowMisc() #else struct funcs { static bool IsNativeDupe(ImGuiKey key) { return key < ImGuiKey_LegacyNativeKey_END && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array #endif - ImGui::Text("Keys down:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X) (%.02f secs)", ImGui::GetKeyName(key), key, key, ImGui::GetKeyData(key)->DownDuration); } } - ImGui::Text("Keys pressed:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), key, key); } } - ImGui::Text("Keys released:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), key, key); } } + ImGui::Text("Keys down:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } } + ImGui::Text("Keys pressed:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } + ImGui::Text("Keys released:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. diff --git a/imgui_internal.h b/imgui_internal.h index b1b8f7f5..e375e593 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -937,7 +937,6 @@ struct ImGuiInputEvent { ImGuiInputEventType Type; ImGuiInputSource Source; - bool SubmittedByTestEngine; union { ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos @@ -948,6 +947,7 @@ struct ImGuiInputEvent ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus }; + bool AddedByTestEngine; ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } }; @@ -2640,7 +2640,7 @@ namespace ImGui // 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 IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } - IMGUI_API const ImGuiKeyData* GetKeyData(ImGuiKey key); + IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key); IMGUI_API void SetItemUsingMouseWheel(); IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } From 26d04c948fa7294bea54f1485f8c51072d64e7ea Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 19 Jan 2022 18:03:36 +0100 Subject: [PATCH 281/332] Tables: Fixed positioning of Sort icon on right-most column with some settings (not resizable + no borders). (#4918). --- docs/CHANGELOG.txt | 1 + imgui_internal.h | 2 +- imgui_tables.cpp | 17 +++++++++++------ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d2237295..1e24e122 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -105,6 +105,7 @@ Other Changes: which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] +- Tables: Fixed positioning of Sort icon on right-most column with some settings (not resizable + no borders). (#4918). - Sliders, Drags: Fixed text input of values with a leading sign, common when using a format enforcing sign. (#4917) - Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API, now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. diff --git a/imgui_internal.h b/imgui_internal.h index e375e593..c1a02eb9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2331,7 +2331,7 @@ struct IMGUI_API ImGuiTable ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is ImRect WorkRect; ImRect InnerClipRect; - ImRect BgClipRect; // We use this to cpu-clip cell background color fill + ImRect BgClipRect; // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 88717bd5..287c2d47 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1569,18 +1569,21 @@ ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n) // Return the cell rectangle based on currently known height. // - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations. -// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it. +// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height. // - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right // columns report a small offset so their CellBgRect can extend up to the outer border. +// FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling. ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n) { const ImGuiTableColumn* column = &table->Columns[column_n]; float x1 = column->MinX; float x2 = column->MaxX; - if (column->PrevEnabledColumn == -1) - x1 -= table->CellSpacingX1; - if (column->NextEnabledColumn == -1) - x2 += table->CellSpacingX2; + //if (column->PrevEnabledColumn == -1) + // x1 -= table->OuterPaddingX; + //if (column->NextEnabledColumn == -1) + // x2 += table->OuterPaddingX; + x1 = ImMax(x1, table->WorkRect.Min.x); + x2 = ImMin(x2, table->WorkRect.Max.x); return ImRect(x1, table->RowPosY1, x2, table->RowPosY2); } @@ -1797,10 +1800,12 @@ void ImGui::TableEndRow(ImGuiTable* table) ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent]; for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++) { + // As we render the BG here we need to clip things (for layout we would not) + // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling. const ImGuiTableColumn* column = &table->Columns[cell_data->Column]; ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column); 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 + 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); } From 7ad42ff431a6e7b0583f1f4e89fa15974d61b06d Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 19 Jan 2022 18:45:57 +0100 Subject: [PATCH 282/332] IO: renamed io.ConfigInputEventQueue to io.ConfigInputTrickleEventQueue. (#4858) --- docs/CHANGELOG.txt | 2 +- imgui.cpp | 4 ++-- imgui.h | 4 ++-- imgui_demo.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1e24e122..61845426 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,7 +54,7 @@ Breaking Changes: - Obsoleted GetKeyIndex(): it is now unnecessary and will now return the same value. - All keyboard related functions taking 'int user_key_index' now take 'ImGuiKey key': - IsKeyDown(), IsKeyPressed(), IsKeyReleased(), GetKeyPressedAmount(). - - Added io.ConfigInputEventQueue (defaulting to true) to disable input queue trickling. + - Added io.ConfigInputTrickleEventQueue (defaulting to true) to disable input queue trickling. - Backward compatibility: - All backends updated to use new functions. - Old backends populating those arrays should still work! diff --git a/imgui.cpp b/imgui.cpp index 7e2908c3..8940cdab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1141,7 +1141,7 @@ ImGuiIO::ImGuiIO() #else ConfigMacOSXBehaviors = false; #endif - ConfigInputEventQueue = true; + ConfigInputTrickleEventQueue = true; ConfigInputTextCursorBlink = true; ConfigWindowsResizeFromEdges = true; ConfigWindowsMoveFromTitleBarOnly = false; @@ -4307,7 +4307,7 @@ void ImGui::NewFrame() // Process input queue (trickle as many events as possible) g.InputEventsTrail.resize(0); - UpdateInputEvents(g.IO.ConfigInputEventQueue); + UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue); // Update keyboard input state UpdateKeyboardInputs(); diff --git a/imgui.h b/imgui.h index 06baf9f2..10616ab2 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18609 +#define IMGUI_VERSION_NUM 18610 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1888,7 +1888,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 ConfigInputEventQueue; // = 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 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 ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 24dfbdf9..46cdf68e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -459,7 +459,7 @@ void ImGui::ShowDemoWindow(bool* p_open) } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); - ImGui::Checkbox("io.ConfigInputEventQueue", &io.ConfigInputEventQueue); + ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue); ImGui::SameLine(); HelpMarker("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."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)."); From 19471da3fd9a63221d9232d0f719f5ce0788c661 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 20 Jan 2022 13:52:19 +0200 Subject: [PATCH 283/332] Popups: Fix a crash when a new window is created after a modal on the same frame. (#4920) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 +++- imgui.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 61845426..cc1a422b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -103,6 +103,7 @@ Other Changes: handled by core automatically for all kind of inputs. (#4858, #2787, #1992, #3383, #2525, #1320) - Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) +- Popups: Fixed a regression crash when a new window is created after a modal on the same frame. (#4920) [@rokups] - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] - Tables: Fixed positioning of Sort icon on right-most column with some settings (not resizable + no borders). (#4918). diff --git a/imgui.cpp b/imgui.cpp index 8940cdab..5c00b6a3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5890,7 +5890,9 @@ static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) { ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; - if (popup_window == NULL || !popup_window->WasActive || !(popup_window->Flags & ImGuiWindowFlags_Modal)) // Check WasActive, because this code may run before popup renders on current frame. + 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 (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. break; diff --git a/imgui.h b/imgui.h index 10616ab2..ff8f0da8 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18610 +#define IMGUI_VERSION_NUM 18611 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 29f104319e91699fec1487fee70b9a6c7cd49dc8 Mon Sep 17 00:00:00 2001 From: Marcell Kiss Date: Thu, 20 Jan 2022 15:32:49 +0100 Subject: [PATCH 284/332] Backends: Vulkan: Added support for ImTextureID as VkDescriptorSet, add ImGui_ImplVulkan_AddTexture(). (#914) --- backends/imgui_impl_vulkan.cpp | 74 ++++++++++++++++++++-------------- backends/imgui_impl_vulkan.h | 4 ++ 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index aa6a3ddb..a36cda71 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -87,7 +87,6 @@ struct ImGui_ImplVulkan_Data VkPipelineCreateFlags PipelineCreateFlags; VkDescriptorSetLayout DescriptorSetLayout; VkPipelineLayout PipelineLayout; - VkDescriptorSet DescriptorSet; VkPipeline Pipeline; uint32_t Subpass; VkShaderModule ShaderModuleVert; @@ -98,6 +97,7 @@ struct ImGui_ImplVulkan_Data VkDeviceMemory FontMemory; VkImage FontImage; VkImageView FontView; + VkDescriptorSet FontDescriptorSet; VkDeviceMemory UploadBufferMemory; VkBuffer UploadBuffer; @@ -376,11 +376,9 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - // Bind pipeline and descriptor sets: + // Bind pipeline: { vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - VkDescriptorSet desc_set[1] = { bd->DescriptorSet }; - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, NULL); } // Bind Vertex And Index Buffer: @@ -531,6 +529,10 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y); vkCmdSetScissor(command_buffer, 0, 1, &scissor); + // Bind DescriptorSet with font or user texture + VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, NULL); + // Draw vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } @@ -607,20 +609,8 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) check_vk_result(err); } - // Update the Descriptor Set: - { - VkDescriptorImageInfo desc_image[1] = {}; - desc_image[0].sampler = bd->FontSampler; - desc_image[0].imageView = bd->FontView; - desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkWriteDescriptorSet write_desc[1] = {}; - write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_desc[0].dstSet = bd->DescriptorSet; - write_desc[0].descriptorCount = 1; - write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write_desc[0].pImageInfo = desc_image; - vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, NULL); - } + // Create the Descriptor Set: + bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Create the Upload Buffer: { @@ -698,7 +688,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) } // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontImage); + io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontDescriptorSet); return true; } @@ -937,17 +927,6 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - // Create Descriptor Set: - { - VkDescriptorSetAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - alloc_info.descriptorPool = v->DescriptorPool; - alloc_info.descriptorSetCount = 1; - alloc_info.pSetLayouts = &bd->DescriptorSetLayout; - err = vkAllocateDescriptorSets(v->Device, &alloc_info, &bd->DescriptorSet); - check_vk_result(err); - } - if (!bd->PipelineLayout) { // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix @@ -1090,6 +1069,41 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) bd->VulkanInitInfo.MinImageCount = min_image_count; } +// Register a texture +// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. +ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + // Create Descriptor Set: + VkDescriptorSet descriptor_set; + { + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = v->DescriptorPool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &bd->DescriptorSetLayout; + VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); + check_vk_result(err); + } + + // Update the Descriptor Set: + { + VkDescriptorImageInfo desc_image[1] = {}; + desc_image[0].sampler = sampler; + desc_image[0].imageView = image_view; + desc_image[0].imageLayout = image_layout; + VkWriteDescriptorSet write_desc[1] = {}; + write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_desc[0].dstSet = descriptor_set; + write_desc[0].descriptorCount = 1; + write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_desc[0].pImageInfo = desc_image; + vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, NULL); + } + return (ImTextureID)descriptor_set; +} //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index abfecc87..467bf93d 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -70,6 +70,10 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer comm IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) +// Register a texture (VkDescriptorSet == ImTextureID) +// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. +IMGUI_IMPL_API ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); + // Optional: load Vulkan functions with a custom function loader // This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = NULL); From ceb26bac48147f61826f04d4b01de45feb71a4e4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Jan 2022 15:53:28 +0100 Subject: [PATCH 285/332] Backends: Vulkan: Added support for ImTextureID as VkDescriptorSet, amends. (#914) --- backends/imgui_impl_dx12.h | 3 +- backends/imgui_impl_vulkan.cpp | 28 ++++++++++++++++--- backends/imgui_impl_vulkan.h | 24 ++++++++-------- docs/CHANGELOG.txt | 1 + examples/example_glfw_vulkan/CMakeLists.txt | 1 + examples/example_glfw_vulkan/build_win32.bat | 4 +-- examples/example_glfw_vulkan/build_win64.bat | 4 +-- .../example_glfw_vulkan.vcxproj | 4 +++ examples/example_sdl_vulkan/build_win32.bat | 2 +- .../example_sdl_vulkan.vcxproj | 4 +++ 10 files changed, 53 insertions(+), 22 deletions(-) diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index 899c8229..6548f6f5 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -6,8 +6,7 @@ // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. -// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. -// This define is set in the example .vcxproj file and need to be replicated in your app or by adding it to your imconfig.h file. +// See imgui_impl_dx12.cpp file for details. // 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. diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index a36cda71..fef57456 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -3,8 +3,15 @@ // Implemented features: // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// Missing features: -// [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this backend! See https://github.com/ocornut/imgui/pull/914 +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. + +// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. +// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. +// To build this on 32-bit systems and support texture changes: +// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files) +// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. +// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) +// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files) // 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. @@ -57,6 +64,11 @@ #include "imgui_impl_vulkan.h" #include +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#endif + // Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() // [Please zero-clear before use!] struct ImGui_ImplVulkanH_FrameRenderBuffers @@ -531,6 +543,14 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Bind DescriptorSet with font or user texture VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; + #if UINTPTR_MAX == 0xffffffff + if (sizeof(ImTextureID) < sizeof(ImU64)) + { + // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. + IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet); + desc_set[0] = bd->FontDescriptorSet; + } + #endif vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, NULL); // Draw @@ -1071,7 +1091,7 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) // Register a texture // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. -ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) +VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; @@ -1102,7 +1122,7 @@ ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_vie write_desc[0].pImageInfo = desc_image; vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, NULL); } - return (ImTextureID)descriptor_set; + return descriptor_set; } //------------------------------------------------------------------------- diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 467bf93d..d80bfc4e 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -3,8 +3,10 @@ // Implemented features: // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// Missing features: -// [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this backend! See https://github.com/ocornut/imgui/pull/914 +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. + +// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. +// See imgui_impl_vulkan.cpp file for details. // 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. @@ -62,21 +64,21 @@ struct ImGui_ImplVulkan_InitInfo }; // Called by user code -IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); -IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); -IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); -IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); -IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) +IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); +IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); +IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); +IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); +IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) // Register a texture (VkDescriptorSet == ImTextureID) // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. -IMGUI_IMPL_API ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); +IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); // Optional: load Vulkan functions with a custom function loader // This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES -IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = NULL); +IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = NULL); //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cc1a422b..dee06e1f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -141,6 +141,7 @@ Other Changes: can theorically be created by user code manipulating a ImDrawList. (#4857) - Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] - Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) +- Backends: Vulkan: Added support for ImTextureID as VkDescriptorSet, add ImGui_ImplVulkan_AddTexture(). (#914) [@martty] - Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and wgpuRenderPassEncoderSetVertexBuffer() calls. (#4891) [@FeepsDev] diff --git a/examples/example_glfw_vulkan/CMakeLists.txt b/examples/example_glfw_vulkan/CMakeLists.txt index 05eab3bc..55a80402 100644 --- a/examples/example_glfw_vulkan/CMakeLists.txt +++ b/examples/example_glfw_vulkan/CMakeLists.txt @@ -41,3 +41,4 @@ file(GLOB sources *.cpp) add_executable(example_glfw_vulkan ${sources} ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp ${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp ${IMGUI_DIR}/imgui.cpp ${IMGUI_DIR}/imgui_draw.cpp ${IMGUI_DIR}/imgui_demo.cpp ${IMGUI_DIR}/imgui_tables.cpp ${IMGUI_DIR}/imgui_widgets.cpp) target_link_libraries(example_glfw_vulkan ${LIBRARIES}) +target_compile_definitions(example_glfw_vulkan PUBLIC -DImTextureID=ImU64) diff --git a/examples/example_glfw_vulkan/build_win32.bat b/examples/example_glfw_vulkan/build_win32.bat index f49cbd77..82f01112 100644 --- a/examples/example_glfw_vulkan/build_win32.bat +++ b/examples/example_glfw_vulkan/build_win32.bat @@ -7,8 +7,8 @@ @set OUT_DIR=Debug mkdir %OUT_DIR% -cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% +cl /nologo /Zi /MD %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% @set OUT_DIR=Release mkdir %OUT_DIR% -cl /nologo /Zi /MD /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% +cl /nologo /Zi /MD /Ox /Oi %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% diff --git a/examples/example_glfw_vulkan/build_win64.bat b/examples/example_glfw_vulkan/build_win64.bat index 893f296f..0bf7936c 100644 --- a/examples/example_glfw_vulkan/build_win64.bat +++ b/examples/example_glfw_vulkan/build_win64.bat @@ -6,8 +6,8 @@ @set OUT_DIR=Debug mkdir %OUT_DIR% -cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% +cl /nologo /Zi /MD %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% @set OUT_DIR=Release mkdir %OUT_DIR% -cl /nologo /Zi /MD /Ox /Oi %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% +cl /nologo /Zi /MD /Ox /Oi %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% diff --git a/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj b/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj index 34b3f21c..ed8fe966 100644 --- a/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj +++ b/examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj @@ -91,6 +91,7 @@ Level4 Disabled ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories) + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true @@ -105,6 +106,7 @@ Level4 Disabled ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories) + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true @@ -122,6 +124,7 @@ true ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories) false + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true @@ -142,6 +145,7 @@ true ..\..;..\..\backends;%VULKAN_SDK%\include;..\libs\glfw\include;%(AdditionalIncludeDirectories) false + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true diff --git a/examples/example_sdl_vulkan/build_win32.bat b/examples/example_sdl_vulkan/build_win32.bat index 21b5ea31..d4a71884 100644 --- a/examples/example_sdl_vulkan/build_win32.bat +++ b/examples/example_sdl_vulkan/build_win32.bat @@ -7,4 +7,4 @@ @set OUT_DIR=Debug mkdir %OUT_DIR% -cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console +cl /nologo /Zi /MD %INCLUDES% /D ImTextureID=ImU64 %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj b/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj index 5cc3eab2..fc69ca70 100644 --- a/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj +++ b/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj @@ -91,6 +91,7 @@ Level4 Disabled ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories) + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true @@ -105,6 +106,7 @@ Level4 Disabled ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories) + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true @@ -122,6 +124,7 @@ true ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories) false + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true @@ -142,6 +145,7 @@ true ..\..;..\..\backends;%VULKAN_SDK%\include;%SDL2_DIR%\include;$(VcpkgCurrentInstalledDir)include\SDL2;%(AdditionalIncludeDirectories) false + ImTextureID=ImU64;_MBCS;%(PreprocessorDefinitions) true From 6e63fcaca3ddde8519d1cb95d20e0c77ed367bc5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Jan 2022 16:56:29 +0100 Subject: [PATCH 286/332] Backends: Vulkan: Amends, fix miscast on 32-bit systems with the right ImTextureID=ImU64. (#914) --- backends/imgui_impl_vulkan.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index fef57456..dbd8b184 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -543,14 +543,12 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Bind DescriptorSet with font or user texture VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; - #if UINTPTR_MAX == 0xffffffff if (sizeof(ImTextureID) < sizeof(ImU64)) { // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet); desc_set[0] = bd->FontDescriptorSet; } - #endif vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, NULL); // Draw @@ -708,7 +706,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) } // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontDescriptorSet); + io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet); return true; } From f33bb99821ee6f56a770ea56a6e3bb96cca9ed7d Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 19 Jan 2022 13:14:40 +0100 Subject: [PATCH 287/332] IO: added AddKeyAnalogEvent() and support for ImGuiKey_GamepadXXXX. (#4858, #787) --- docs/CHANGELOG.txt | 6 +++ imgui.cpp | 104 ++++++++++++++++++++++++++++++++------------- imgui.h | 67 +++++++++++++++++++++++------ imgui_demo.cpp | 4 +- imgui_internal.h | 16 +++---- 5 files changed, 143 insertions(+), 54 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dee06e1f..a7fb525a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -76,6 +76,10 @@ Breaking Changes: - Access to full key ranges will allow us to develop a proper keyboard shortcut system. (#456) - io.AddKeyEvent() will later be turned into a trickling IO queue (for all inputs) to handle very low framerate better. (#2525, #2787, #3383) - io.SetKeyEventNativeData() include native keycode/scancode which will later be exposed. (#3141, #2959) +- Reworked IO nav/gamepad input API and unifying inputs sources: (#4858, #787) + - Added full range of ImGuiKey_GamepadXXXX enums (e.g. ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadR2) to use with + io.AddKeyEvent(), io.AddKeyAnalogEvent(). + - Added io.AddKeyAnalogEvent() function, obsoleting writing directly to io.NavInputs[] arrays. - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) - Backends: GLFW: backend now uses glfwSetCursorPosCallback(). - If calling ImGui_ImplGlfw_InitXXX with install_callbacks=true: nothing to do. is already done for you. @@ -101,6 +105,8 @@ Other Changes: - IO: Added event based input queue API, which now trickles events to support low framerates. [@thedmd, @ocornut] Previously the most common issue case (button presses in low framerates) was handled by backend. This is now handled by core automatically for all kind of inputs. (#4858, #2787, #1992, #3383, #2525, #1320) + - New IO functions for keyboard/gamepad: AddKeyEvent(), AddKeyAnalogEvent(), AddKeyModsEvent(). + - New IO functions for mouse: AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent(). - Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Popups: Fixed a regression crash when a new window is created after a modal on the same frame. (#4920) [@rokups] diff --git a/imgui.cpp b/imgui.cpp index 5c00b6a3..a2f0d0f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -346,27 +346,26 @@ CODE - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Keyboard: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. - NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. - - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag - will be set. For more advanced uses, you may want to read from: + - Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. + - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() calls. + - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), + the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. Please reach out if you think the game vs navigation input sharing could be improved. - Gamepad: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. - - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). - Note that io.NavInputs[] is cleared by EndFrame(). - - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: - 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - - We use a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. - Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). + - Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. + - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys. + For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly. + Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). + - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() + io.AddKeyAnalogEvent() calls. + - BEFORE 1.87, BACKENDS USED TO WRITE DIRECTLY TO io.NavInputs[]. This is going to be obsoleted in the future. Please call io functions instead! - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets - - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo - to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. + - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing, + with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. - Mouse: - - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. + - PS4/PS5 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. @@ -385,6 +384,8 @@ 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. + - 2022/01/20 (1.87) - inputs: reworded gamepad IO. + - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). - 2022/01/17 (1.87) - inputs: reworked mouse IO. - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent() @@ -1163,6 +1164,7 @@ ImGuiIO::ImGuiIO() for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; BackendUsingLegacyKeyArrays = (ImS8)-1; + BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong } // Pass in translated ASCII characters for text input. @@ -1253,9 +1255,10 @@ void ImGuiIO::ClearInputKeys() } // Queue a new key down/up event. -// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) -// - bool down: Is the key down? use false to signify a key release. -void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) +// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) +// - bool down: Is the key down? use false to signify a key release. +// - float analog_value: 0.0f..1.0f +void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value, ImGuiInputSource input_source) { //if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } if (key == ImGuiKey_None) @@ -1263,6 +1266,7 @@ void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); IM_ASSERT(ImGui::IsNamedKey(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(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad); // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -1272,15 +1276,23 @@ void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); BackendUsingLegacyKeyArrays = 0; #endif + if (ImGui::IsGamepadKey(key)) + BackendUsingLegacyNavInputArray = false; ImGuiInputEvent e; e.Type = ImGuiInputEventType_Key; - e.Source = ImGuiInputSource_Keyboard; + e.Source = input_source; e.Key.Key = key; e.Key.Down = down; + e.Key.AnalogValue = analog_value; g.InputEventsQueue.push_back(e); } +void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down, ImGuiInputSource input_source) +{ + AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f, input_source); +} + // [Optional] Call after AddKeyEvent(). // Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices. // If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. @@ -3355,7 +3367,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? (ImGuiInputSource)ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } // Clear declaration of inputs claimed by the widget @@ -3936,6 +3948,14 @@ static void ImGui::UpdateKeyboardInputs() } #endif + // Clear gamepad data if disabled + if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) + for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++) + { + io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false; + io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f; + } + // Update keys for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) { @@ -7486,14 +7506,19 @@ static const char* const GKeyNames[] = "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" + "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", + "GamepadStart", "GamepadBack", "GamepadFaceUp", "GamepadFaceDown", "GamepadFaceLeft", "GamepadFaceRight", + "GamepadDpadUp", "GamepadDpadDown", "GamepadDpadLeft", "GamepadDpadRight", + "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3", + "GamepadLStickUp", "GamepadLStickDown", "GamepadLStickLeft", "GamepadLStickRight", + "GamepadRStickUp", "GamepadRStickDown", "GamepadRStickLeft", "GamepadRStickRight" }; IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); const char* ImGui::GetKeyName(ImGuiKey key) { #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); + IM_ASSERT((IsNamedKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); #else if (IsLegacyKey(key)) { @@ -7782,12 +7807,13 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) IM_ASSERT(e->Key.Key != ImGuiKey_None); const int keydata_index = (e->Key.Key - ImGuiKey_KeysData_OFFSET); ImGuiKeyData* keydata = &io.KeysData[keydata_index]; - if (keydata->Down != e->Key.Down) + if (keydata->Down != e->Key.Down || keydata->AnalogValue != e->Key.AnalogValue) { // Trickling Rule: Stop processing queued events if we got multiple action on the same button - if (trickle_fast_inputs && (key_changed_mask.TestBit(keydata_index) || text_inputed || mouse_button_changed != 0)) + if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputed || mouse_button_changed != 0)) break; keydata->Down = e->Key.Down; + keydata->AnalogValue = e->Key.AnalogValue; key_changed = true; key_changed_mask.SetBit(keydata_index); } @@ -7831,6 +7857,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) } // Record trail (for domain-specific applications wanting to access a precise trail) + //if (event_n != 0) IMGUI_DEBUG_LOG("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n); for (int n = 0; n < event_n; n++) g.InputEventsTrail.push_back(g.InputEventsQueue[n]); @@ -9986,18 +10013,35 @@ static void ImGui::NavUpdate() io.WantSetMousePos = false; //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); + // Update Gamepad->Nav inputs mapping // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) - // (do it before we map Keyboard input!) - const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad) - { - if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f - || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f) - g.NavInputSource = ImGuiInputSource_Gamepad; + if (nav_gamepad_active && g.IO.BackendUsingLegacyNavInputArray == false) + { + for (int n = 0; n < ImGuiNavInput_COUNT; n++) + IM_ASSERT(io.NavInputs[n] == 0.0f && "Backend needs to either only use io.AddKeyEvent()/io.AddKeyAnalogEvent(), either only fill legacy io.NavInputs[]. Not both!"); + #define NAV_MAP_KEY(_KEY, _NAV_INPUT, _ACTIVATE_NAV) do { io.NavInputs[_NAV_INPUT] = io.KeysData[_KEY - ImGuiKey_KeysData_OFFSET].AnalogValue; if (_ACTIVATE_NAV && io.NavInputs[_NAV_INPUT] > 0.0f) { g.NavInputSource = ImGuiInputSource_Gamepad; } } while (0) + NAV_MAP_KEY(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate, true); + NAV_MAP_KEY(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel, true); + NAV_MAP_KEY(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu, true); + NAV_MAP_KEY(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp, true); + NAV_MAP_KEY(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown, true); + NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, false); + NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, false); + NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_TweakSlow, false); + NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_TweakFast, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp, false); + NAV_MAP_KEY(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown, false); + #undef NAV_MAP_KEY } // Update Keyboard->Nav inputs mapping + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (nav_keyboard_active) { #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(_KEY)) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0) diff --git a/imgui.h b/imgui.h index ff8f0da8..a6e44c36 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18611 +#define IMGUI_VERSION_NUM 18612 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -171,6 +171,7 @@ typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier +typedef int ImGuiInputSource; // -> enum ImGuiInputSource_ // Enum: An input source (mouse/keyboard/gamepad) for io.AddKeyEvent() functions typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier @@ -1392,7 +1393,34 @@ enum ImGuiKey_ ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, - ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value + + // Gamepad (some of those are expected to be analog values from 0.0f to 1.0f) ..............// NAVIGATION action + ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- + ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- + ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input + ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate + ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu + ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel + ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp + ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown + ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft + ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight + ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow + ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast + ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] + ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] + ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) + ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) + ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp + ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown + ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft + ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight + ImGuiKey_GamepadRStickUp, // [Analog] + ImGuiKey_GamepadRStickDown, // [Analog] + ImGuiKey_GamepadRStickLeft, // [Analog] + ImGuiKey_GamepadRStickRight, // [Analog] + + ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value // Legacy range used by legacy io.KeyMap[]. Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index. // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) @@ -1403,11 +1431,13 @@ enum ImGuiKey_ ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0] + ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN, // First key stored in KeysData[0] #else ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_LegacyNativeKey_BEGIN // First key stored in KeysData[0] + ImGuiKey_KeysData_OFFSET = ImGuiKey_LegacyNativeKey_BEGIN, // First key stored in KeysData[0] #endif + ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart, + ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 @@ -1857,6 +1887,15 @@ struct ImGuiKeyData bool Down; // True for if key is down float DownDuration; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held) float DownDurationPrev; // Last frame duration the key has been down + float AnalogValue; // 0.0f..1.0f for gamepad values +}; + +enum ImGuiInputSource_ +{ + ImGuiInputSource_None = 0, + ImGuiInputSource_Mouse, + ImGuiInputSource_Keyboard, + ImGuiInputSource_Gamepad }; struct ImGuiIO @@ -1927,15 +1966,16 @@ struct ImGuiIO //------------------------------------------------------------------ // Input Functions - IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) - IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers);// Queue a change of Ctrl/Shift/Alt/Super modifiers - IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) - IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change - IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update - IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) - IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate - IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string + IMGUI_API void AddKeyEvent(ImGuiKey key, bool down, ImGuiInputSource src = ImGuiInputSource_Keyboard); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) + IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v, ImGuiInputSource src); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. + IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers + IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) + IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change + IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update + IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) + IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input + IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually IMGUI_API void ClearInputKeys(); // [Internal] Release all keys @@ -2009,6 +2049,7 @@ struct ImGuiIO float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. bool AppFocusLost; ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[] + bool BackendUsingLegacyNavInputArray; // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 46cdf68e..06d7db02 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5693,8 +5693,8 @@ static void ShowDemoWindowMisc() } // Display Keyboard/Mouse state - IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State"); - if (ImGui::TreeNode("Keyboard & Navigation State")) + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard, Gamepad & Navigation State"); + if (ImGui::TreeNode("Keyboard, Gamepad & Navigation State")) { // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allow displaying the data for old/new backends. // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes. diff --git a/imgui_internal.h b/imgui_internal.h index c1a02eb9..8424d8b6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -912,14 +912,11 @@ enum ImGuiInputEventType ImGuiInputEventType_COUNT }; -enum ImGuiInputSource -{ - ImGuiInputSource_None = 0, - ImGuiInputSource_Mouse, - ImGuiInputSource_Keyboard, - ImGuiInputSource_Gamepad, - ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only - ImGuiInputSource_Clipboard, // Currently only used by InputText() +// Extend ImGuiInputSource +enum ImGuiInputSourcePrivate_ +{ + ImGuiInputSource_Clipboard = ImGuiInputSource_Gamepad + 1, // Currently only used by InputText() + ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only ImGuiInputSource_COUNT }; @@ -928,7 +925,7 @@ enum ImGuiInputSource struct ImGuiInputEventMousePos { float PosX, PosY; }; struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; struct ImGuiInputEventMouseButton { int Button; bool Down; }; -struct ImGuiInputEventKey { ImGuiKey Key; bool Down; }; +struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; struct ImGuiInputEventKeyMods { ImGuiKeyModFlags Mods; }; struct ImGuiInputEventText { unsigned int Char; }; struct ImGuiInputEventAppFocused { bool Focused; }; @@ -2640,6 +2637,7 @@ namespace ImGui // 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 IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } + inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key); IMGUI_API void SetItemUsingMouseWheel(); IMGUI_API void SetActiveIdUsingNavAndKeys(); From 9f8c599ca70c362833613964359202fe1eb6938a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Jan 2022 17:12:11 +0100 Subject: [PATCH 288/332] Backends: Win32: Add full gamepad support using io.AddKeyEvent(), io.AddKeyAnalogEvent(), stopped writing to io.NavInputs[]. (#4858, #787) --- backends/imgui_impl_win32.cpp | 64 ++++++++++++++++++++--------------- docs/CHANGELOG.txt | 1 + 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index db3462fa..6b0daf2b 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -34,6 +34,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before a key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. @@ -277,7 +278,6 @@ static void ImGui_ImplWin32_UpdateGamepads() #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD ImGuiIO& io = ImGui::GetIO(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; @@ -285,39 +285,47 @@ static void ImGui_ImplWin32_UpdateGamepads() // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. if (bd->WantUpdateHasGamepad) { - XINPUT_CAPABILITIES caps; + XINPUT_CAPABILITIES caps = {}; bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; bd->WantUpdateHasGamepad = false; } io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; XINPUT_STATE xinput_state; - if (bd->HasGamepad && bd->XInputGetState && bd->XInputGetState(0, &xinput_state) == ERROR_SUCCESS) - { - const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - - #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } - #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } - MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); - MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); - #undef MAP_BUTTON - #undef MAP_ANALOG - } + XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; + if (!bd->HasGamepad || bd->XInputGetState == NULL || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS) + return; + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + + #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) + #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0, ImGuiInputSource_Gamepad); } + #define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn), ImGuiInputSource_Gamepad); } + MAP_BUTTON(ImGuiKey_GamepadStart, XINPUT_GAMEPAD_START); + MAP_BUTTON(ImGuiKey_GamepadBack, XINPUT_GAMEPAD_BACK); + MAP_BUTTON(ImGuiKey_GamepadFaceDown, XINPUT_GAMEPAD_A); + MAP_BUTTON(ImGuiKey_GamepadFaceRight, XINPUT_GAMEPAD_B); + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, XINPUT_GAMEPAD_X); + MAP_BUTTON(ImGuiKey_GamepadFaceUp, XINPUT_GAMEPAD_Y); + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, XINPUT_GAMEPAD_DPAD_UP); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, XINPUT_GAMEPAD_DPAD_DOWN); + MAP_BUTTON(ImGuiKey_GamepadL1, XINPUT_GAMEPAD_LEFT_SHOULDER); + MAP_BUTTON(ImGuiKey_GamepadR1, XINPUT_GAMEPAD_RIGHT_SHOULDER); + MAP_ANALOG(ImGuiKey_GamepadL2, gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255); + MAP_ANALOG(ImGuiKey_GamepadR2, gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, 255); + MAP_BUTTON(ImGuiKey_GamepadL3, XINPUT_GAMEPAD_LEFT_THUMB); + MAP_BUTTON(ImGuiKey_GamepadR3, XINPUT_GAMEPAD_RIGHT_THUMB); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, gamepad.sThumbRX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, gamepad.sThumbRX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, gamepad.sThumbRY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, gamepad.sThumbRY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + #undef MAP_BUTTON + #undef MAP_ANALOG #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a7fb525a..bdc009e4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -130,6 +130,7 @@ Other Changes: - Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. +- Backends: Win32: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. - Backends: SDL: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: SDL: Update mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. From 3d85433748adbd1a542c1b2db03ee92a28e2c10b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Jan 2022 17:12:22 +0100 Subject: [PATCH 289/332] Backends: GLFW: Add full gamepad support using io.AddKeyEvent(), io.AddKeyAnalogEvent(), stopped writing to io.NavInputs[]. (#4858, #787) --- backends/imgui_impl_glfw.cpp | 66 +++++++++++++++++++++++------------- docs/CHANGELOG.txt | 1 + 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 4dea6238..e67a5a4d 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). @@ -71,6 +72,7 @@ #else #define GLFW_HAS_NEW_CURSORS (0) #endif +#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api #define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName() // GLFW data @@ -529,41 +531,57 @@ static void ImGui_ImplGlfw_UpdateMouseCursor() } } +// Update gamepad inputs +static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } static void ImGui_ImplGlfw_UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); - memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; - // Update gamepad inputs - #define MAP_BUTTON(NAV_NO, BUTTON_NO) do { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } while (0) - #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } while (0) + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; +#if GLFW_HAS_GAMEPAD_API + GLFWgamepadstate gamepad; + if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) + return; + #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0, ImGuiInputSource_Gamepad); } while (0) + #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v), ImGuiInputSource_Gamepad); } while (0) +#else int axes_count = 0, buttons_count = 0; const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); - MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); - MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); + if (axes_count == 0 || buttons_count == 0) + return; + #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS), ImGuiInputSource_Gamepad); } while (0) + #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v), ImGuiInputSource_Gamepad); } while (0) +#endif + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); + MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); + MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross + MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square + MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); + MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); + MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); + MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); + MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); + MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); #undef MAP_BUTTON #undef MAP_ANALOG - if (axes_count > 0 && buttons_count > 0) - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - else - io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; } void ImGui_ImplGlfw_NewFrame() diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bdc009e4..7b565858 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -126,6 +126,7 @@ Other Changes: - Backends: GLFW: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: GLFW: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: GLFW: Update mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. +- Backends: GLFW: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: Win32: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) From 39c3412f9155117a034fbf08c91bdd1d38f21fad Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Jan 2022 17:12:28 +0100 Subject: [PATCH 290/332] Backends: SDL: Add full gamepad support using io.AddKeyEvent(), io.AddKeyAnalogEvent(), stopped writing to io.NavInputs[]. (#4858, #787) --- backends/imgui_impl_sdl.cpp | 54 ++++++++++++++++++++----------------- docs/CHANGELOG.txt | 1 + imgui_demo.cpp | 9 ++++--- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index a04233c7..bab74110 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. @@ -465,40 +466,45 @@ static void ImGui_ImplSDL2_UpdateMouseCursor() static void ImGui_ImplSDL2_UpdateGamepads() { ImGuiIO& io = ImGui::GetIO(); - memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; // Get gamepad + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; SDL_GameController* game_controller = SDL_GameControllerOpen(0); if (!game_controller) - { - io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; return; - } + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; // Update gamepad inputs - #define MAP_BUTTON(NAV_NO, BUTTON_NO) { io.NavInputs[NAV_NO] = (SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0) ? 1.0f : 0.0f; } - #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } + #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) + #define MAP_BUTTON(KEY_NO, BUTTON_NO) { io.AddKeyEvent(KEY_NO, SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0, ImGuiInputSource_Gamepad); } + #define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn, ImGuiInputSource_Gamepad); } const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value. - MAP_BUTTON(ImGuiNavInput_Activate, SDL_CONTROLLER_BUTTON_A); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, SDL_CONTROLLER_BUTTON_B); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, SDL_CONTROLLER_BUTTON_X); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, SDL_CONTROLLER_BUTTON_Y); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768); - MAP_ANALOG(ImGuiNavInput_LStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767); - MAP_ANALOG(ImGuiNavInput_LStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32767); - MAP_ANALOG(ImGuiNavInput_LStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767); - - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + MAP_BUTTON(ImGuiKey_GamepadStart, SDL_CONTROLLER_BUTTON_START); + MAP_BUTTON(ImGuiKey_GamepadBack, SDL_CONTROLLER_BUTTON_BACK); + MAP_BUTTON(ImGuiKey_GamepadFaceDown, SDL_CONTROLLER_BUTTON_A); // Xbox A, PS Cross + MAP_BUTTON(ImGuiKey_GamepadFaceRight, SDL_CONTROLLER_BUTTON_B); // Xbox B, PS Circle + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, SDL_CONTROLLER_BUTTON_X); // Xbox X, PS Square + MAP_BUTTON(ImGuiKey_GamepadFaceUp, SDL_CONTROLLER_BUTTON_Y); // Xbox Y, PS Triangle + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN); + MAP_BUTTON(ImGuiKey_GamepadL1, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); + MAP_BUTTON(ImGuiKey_GamepadR1, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + MAP_ANALOG(ImGuiKey_GamepadL2, SDL_CONTROLLER_AXIS_TRIGGERLEFT, 0.0f, 32767); + MAP_ANALOG(ImGuiKey_GamepadR2, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0.0f, 32767); + MAP_BUTTON(ImGuiKey_GamepadL3, SDL_CONTROLLER_BUTTON_LEFTSTICK); + MAP_BUTTON(ImGuiKey_GamepadR3, SDL_CONTROLLER_BUTTON_RIGHTSTICK); + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32768); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, SDL_CONTROLLER_AXIS_RIGHTX, -thumb_dead_zone, -32768); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, SDL_CONTROLLER_AXIS_RIGHTX, +thumb_dead_zone, +32767); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, SDL_CONTROLLER_AXIS_RIGHTY, -thumb_dead_zone, -32768); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767); #undef MAP_BUTTON #undef MAP_ANALOG } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7b565858..1e6dace9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -137,6 +137,7 @@ Other Changes: - Backends: SDL: Update mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. - Backends: SDL: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. +- Backends: SDL: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: Allegro5: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Allegro5: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: Android, GLUT, OSX: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 06d7db02..3fefc514 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5700,15 +5700,16 @@ static void ShowDemoWindowMisc() // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes. #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO struct funcs { static bool IsNativeDupe(ImGuiKey) { return false; } }; + const ImGuiKey key_first = ImGuiKey_NamedKey_BEGIN; #else struct funcs { static bool IsNativeDupe(ImGuiKey key) { return key < ImGuiKey_LegacyNativeKey_END && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array + const ImGuiKey key_first = 0; #endif - ImGui::Text("Keys down:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } } - ImGui::Text("Keys pressed:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } - ImGui::Text("Keys released:"); for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } + ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } } + ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } + ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. - ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]); } ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } ImGui::TreePop(); From 5ea47d9560855c82c0f00bca695e5a47ca4e1ad1 Mon Sep 17 00:00:00 2001 From: thedmd Date: Wed, 19 Jan 2022 21:43:48 +0100 Subject: [PATCH 291/332] Backends: OSX: Add full gamepad support using io.AddKeyEvent(), io.AddKeyAnalogEvent(), stopped writing to io.NavInputs[]. (#4858, #787) --- backends/imgui_impl_osx.mm | 57 +++++++++++++++++++++++++------------- docs/CHANGELOG.txt | 1 + 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 58e5a09f..3c516f9a 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-12: Inputs: Added basic Platform IME support, hooking the io.SetPlatformImeDataFn() function. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. @@ -502,25 +503,43 @@ static void ImGui_ImplOSX_UpdateGamepads() GCExtendedGamepad* gp = controller.extendedGamepad; -#define MAP_BUTTON(NAV_NO, NAME) { io.NavInputs[NAV_NO] = gp.NAME.isPressed ? 1.0 : 0.0; } - MAP_BUTTON(ImGuiNavInput_Activate, buttonA); - MAP_BUTTON(ImGuiNavInput_Cancel, buttonB); - MAP_BUTTON(ImGuiNavInput_Menu, buttonX); - MAP_BUTTON(ImGuiNavInput_Input, buttonY); - MAP_BUTTON(ImGuiNavInput_DpadLeft, dpad.left); - MAP_BUTTON(ImGuiNavInput_DpadRight, dpad.right); - MAP_BUTTON(ImGuiNavInput_DpadUp, dpad.up); - MAP_BUTTON(ImGuiNavInput_DpadDown, dpad.down); - MAP_BUTTON(ImGuiNavInput_FocusPrev, leftShoulder); - MAP_BUTTON(ImGuiNavInput_FocusNext, rightShoulder); - MAP_BUTTON(ImGuiNavInput_TweakSlow, leftShoulder); - MAP_BUTTON(ImGuiNavInput_TweakFast, rightShoulder); -#undef MAP_BUTTON - - io.NavInputs[ImGuiNavInput_LStickLeft] = gp.leftThumbstick.left.value; - io.NavInputs[ImGuiNavInput_LStickRight] = gp.leftThumbstick.right.value; - io.NavInputs[ImGuiNavInput_LStickUp] = gp.leftThumbstick.up.value; - io.NavInputs[ImGuiNavInput_LStickDown] = gp.leftThumbstick.down.value; + // Update gamepad inputs + #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) + #define MAP_BUTTON(KEY_NO, BUTTON_NAME) { io.AddKeyEvent(KEY_NO, gp.BUTTON_NAME.isPressed, ImGuiInputSource_Gamepad); } + #define MAP_ANALOG(KEY_NO, AXIS_NAME, V0, V1) { float vn = (float)(gp.AXIS_NAME.value - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn, ImGuiInputSource_Gamepad); } + const float thumb_dead_zone = 0.0f; + if (@available(macOS 10.15, *)) + { + MAP_BUTTON(ImGuiKey_GamepadStart, buttonMenu); + MAP_BUTTON(ImGuiKey_GamepadBack, buttonOptions); + } + MAP_BUTTON(ImGuiKey_GamepadFaceDown, buttonA); // Xbox A, PS Cross + MAP_BUTTON(ImGuiKey_GamepadFaceRight, buttonB); // Xbox B, PS Circle + MAP_BUTTON(ImGuiKey_GamepadFaceLeft, buttonX); // Xbox X, PS Square + MAP_BUTTON(ImGuiKey_GamepadFaceUp, buttonY); // Xbox Y, PS Triangle + MAP_BUTTON(ImGuiKey_GamepadDpadLeft, dpad.left); + MAP_BUTTON(ImGuiKey_GamepadDpadRight, dpad.right); + MAP_BUTTON(ImGuiKey_GamepadDpadUp, dpad.up); + MAP_BUTTON(ImGuiKey_GamepadDpadDown, dpad.down); + MAP_ANALOG(ImGuiKey_GamepadL1, leftShoulder, 0.0f, 1.0f); + MAP_ANALOG(ImGuiKey_GamepadR1, rightShoulder, 0.0f, 1.0f); + MAP_ANALOG(ImGuiKey_GamepadL2, leftTrigger, 0.0f, 1.0f); + MAP_ANALOG(ImGuiKey_GamepadR2, rightTrigger, 0.0f, 1.0f); + if (@available(macOS 10.14.1, *)) + { + MAP_BUTTON(ImGuiKey_GamepadL3, leftThumbstickButton); + MAP_BUTTON(ImGuiKey_GamepadR3, rightThumbstickButton); + } + MAP_ANALOG(ImGuiKey_GamepadLStickLeft, leftThumbstick.xAxis, -thumb_dead_zone, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickRight, leftThumbstick.xAxis, +thumb_dead_zone, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickUp, leftThumbstick.yAxis, +thumb_dead_zone, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadLStickDown, leftThumbstick.yAxis, -thumb_dead_zone, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickLeft, rightThumbstick.xAxis, -thumb_dead_zone, -1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickRight, rightThumbstick.xAxis, +thumb_dead_zone, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickUp, rightThumbstick.yAxis, +thumb_dead_zone, +1.0f); + MAP_ANALOG(ImGuiKey_GamepadRStickDown, rightThumbstick.yAxis, -thumb_dead_zone, -1.0f); + #undef MAP_BUTTON + #undef MAP_ANALOG io.BackendFlags |= ImGuiBackendFlags_HasGamepad; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1e6dace9..5d8692ef 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -140,6 +140,7 @@ Other Changes: - Backends: SDL: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: Allegro5: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Allegro5: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) +- Backends: OSX: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: Android, GLUT, OSX: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Android, GLUT, OSX: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] From 62d6be3747924552ebb66046af6d9ebbc4ab0eae Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Jan 2022 17:39:25 +0100 Subject: [PATCH 292/332] IO: Removed ImGuiInputSource from public api for now, unnecessary. (#4858, #787) --- backends/imgui_impl_glfw.cpp | 8 ++++---- backends/imgui_impl_osx.mm | 4 ++-- backends/imgui_impl_sdl.cpp | 4 ++-- backends/imgui_impl_win32.cpp | 4 ++-- imgui.cpp | 9 ++++----- imgui.h | 35 +++++++++++++---------------------- imgui_internal.h | 9 ++++++--- 7 files changed, 33 insertions(+), 40 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index e67a5a4d..17c210ac 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -544,16 +544,16 @@ static void ImGui_ImplGlfw_UpdateGamepads() GLFWgamepadstate gamepad; if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad)) return; - #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0, ImGuiInputSource_Gamepad); } while (0) - #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v), ImGuiInputSource_Gamepad); } while (0) + #define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0) + #define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) #else int axes_count = 0, buttons_count = 0; const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); if (axes_count == 0 || buttons_count == 0) return; - #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS), ImGuiInputSource_Gamepad); } while (0) - #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v), ImGuiInputSource_Gamepad); } while (0) + #define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0) + #define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0) #endif io.BackendFlags |= ImGuiBackendFlags_HasGamepad; MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 3c516f9a..936617a5 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -505,8 +505,8 @@ static void ImGui_ImplOSX_UpdateGamepads() // Update gamepad inputs #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) - #define MAP_BUTTON(KEY_NO, BUTTON_NAME) { io.AddKeyEvent(KEY_NO, gp.BUTTON_NAME.isPressed, ImGuiInputSource_Gamepad); } - #define MAP_ANALOG(KEY_NO, AXIS_NAME, V0, V1) { float vn = (float)(gp.AXIS_NAME.value - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn, ImGuiInputSource_Gamepad); } + #define MAP_BUTTON(KEY_NO, BUTTON_NAME) { io.AddKeyEvent(KEY_NO, gp.BUTTON_NAME.isPressed); } + #define MAP_ANALOG(KEY_NO, AXIS_NAME, V0, V1) { float vn = (float)(gp.AXIS_NAME.value - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); } const float thumb_dead_zone = 0.0f; if (@available(macOS 10.15, *)) { diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index bab74110..0d01cd49 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -478,8 +478,8 @@ static void ImGui_ImplSDL2_UpdateGamepads() // Update gamepad inputs #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) - #define MAP_BUTTON(KEY_NO, BUTTON_NO) { io.AddKeyEvent(KEY_NO, SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0, ImGuiInputSource_Gamepad); } - #define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn, ImGuiInputSource_Gamepad); } + #define MAP_BUTTON(KEY_NO, BUTTON_NO) { io.AddKeyEvent(KEY_NO, SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0); } + #define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); } const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value. MAP_BUTTON(ImGuiKey_GamepadStart, SDL_CONTROLLER_BUTTON_START); MAP_BUTTON(ImGuiKey_GamepadBack, SDL_CONTROLLER_BUTTON_BACK); diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 6b0daf2b..52b3b5a7 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -298,8 +298,8 @@ static void ImGui_ImplWin32_UpdateGamepads() io.BackendFlags |= ImGuiBackendFlags_HasGamepad; #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) - #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0, ImGuiInputSource_Gamepad); } - #define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn), ImGuiInputSource_Gamepad); } + #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); } + #define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn)); } MAP_BUTTON(ImGuiKey_GamepadStart, XINPUT_GAMEPAD_START); MAP_BUTTON(ImGuiKey_GamepadBack, XINPUT_GAMEPAD_BACK); MAP_BUTTON(ImGuiKey_GamepadFaceDown, XINPUT_GAMEPAD_A); diff --git a/imgui.cpp b/imgui.cpp index a2f0d0f8..ebd1f213 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1258,7 +1258,7 @@ void ImGuiIO::ClearInputKeys() // - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) // - bool down: Is the key down? use false to signify a key release. // - float analog_value: 0.0f..1.0f -void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value, ImGuiInputSource input_source) +void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) { //if (e->Down) { IMGUI_DEBUG_LOG("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } if (key == ImGuiKey_None) @@ -1266,7 +1266,6 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value, ImG ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); IM_ASSERT(ImGui::IsNamedKey(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(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad); // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -1281,16 +1280,16 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value, ImG ImGuiInputEvent e; e.Type = ImGuiInputEventType_Key; - e.Source = input_source; + e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard; e.Key.Key = key; e.Key.Down = down; e.Key.AnalogValue = analog_value; g.InputEventsQueue.push_back(e); } -void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down, ImGuiInputSource input_source) +void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) { - AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f, input_source); + AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f); } // [Optional] Call after AddKeyEvent(). diff --git a/imgui.h b/imgui.h index a6e44c36..21e4b77c 100644 --- a/imgui.h +++ b/imgui.h @@ -171,7 +171,6 @@ typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier -typedef int ImGuiInputSource; // -> enum ImGuiInputSource_ // Enum: An input source (mouse/keyboard/gamepad) for io.AddKeyEvent() functions typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier @@ -1890,14 +1889,6 @@ struct ImGuiKeyData float AnalogValue; // 0.0f..1.0f for gamepad values }; -enum ImGuiInputSource_ -{ - ImGuiInputSource_None = 0, - ImGuiInputSource_Mouse, - ImGuiInputSource_Keyboard, - ImGuiInputSource_Gamepad -}; - struct ImGuiIO { //------------------------------------------------------------------ @@ -1966,19 +1957,19 @@ struct ImGuiIO //------------------------------------------------------------------ // Input Functions - IMGUI_API void AddKeyEvent(ImGuiKey key, bool down, ImGuiInputSource src = ImGuiInputSource_Keyboard); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) - IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v, ImGuiInputSource src); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. - IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers - IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) - IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change - IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update - IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) - IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate - IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string - - IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually - IMGUI_API void ClearInputKeys(); // [Internal] Release all keys + IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) + IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. + IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers + IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) + IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change + IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update + IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) + IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input + IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string + + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys 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. //------------------------------------------------------------------ diff --git a/imgui_internal.h b/imgui_internal.h index 8424d8b6..339a6e2c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -912,10 +912,13 @@ enum ImGuiInputEventType ImGuiInputEventType_COUNT }; -// Extend ImGuiInputSource -enum ImGuiInputSourcePrivate_ +enum ImGuiInputSource { - ImGuiInputSource_Clipboard = ImGuiInputSource_Gamepad + 1, // Currently only used by InputText() + ImGuiInputSource_None = 0, + ImGuiInputSource_Mouse, + ImGuiInputSource_Keyboard, + ImGuiInputSource_Gamepad, + ImGuiInputSource_Clipboard, // Currently only used by InputText() ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only ImGuiInputSource_COUNT }; From 26e59c8742c82ebfa0a696572b21b0bc9f14d5c6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Jan 2022 14:41:34 +0100 Subject: [PATCH 293/332] Nav: Fixed gamepad navigation in wrapping popups not wrapping all the way. (#4365) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 24 +++++++++++++++--------- imgui.h | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5d8692ef..a210a497 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -113,6 +113,7 @@ Other Changes: - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] - Tables: Fixed positioning of Sort icon on right-most column with some settings (not resizable + no borders). (#4918). +- Nav: Fixed gamepad navigation in wrapping popups not wrapping all the way. (#4365) - Sliders, Drags: Fixed text input of values with a leading sign, common when using a format enforcing sign. (#4917) - Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API, now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. diff --git a/imgui.cpp b/imgui.cpp index ebd1f213..a8d069d4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10287,15 +10287,21 @@ void ImGui::NavUpdateCreateMoveRequest() // When using gamepad, we project the reference nav bounding box into window visible area. // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative // (can't focus a visible object like we can with the mouse). - if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) - { - ImRect window_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); - if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) - { - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); - float pad = window->CalcFontSize() * 0.5f; - window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item - window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel); + if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded)) + { + bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0; + bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0; + ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); + if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer])) + { + IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); + float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f); + float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item + inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX; + inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX; + inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX; + inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX; + window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel); g.NavId = g.NavFocusScopeId = 0; } } diff --git a/imgui.h b/imgui.h index 21e4b77c..6581bdab 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18612 +#define IMGUI_VERSION_NUM 18613 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 69b697378b7e73d42c730bf20954c13319f2391c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Jan 2022 14:50:31 +0100 Subject: [PATCH 294/332] Comments, tweaks. --- docs/CHANGELOG.txt | 34 ++++++++++++++++++---------------- imgui.cpp | 2 +- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a210a497..03bfb598 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,14 +38,11 @@ HOW TO UPDATE? Breaking Changes: - Removed support for pre-C++11 compilers. We'll stop supporting VS2010. (#4537) -- Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) - This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). - (Instead you may implement custom expression evaluators to provide a better version of this). -- Reworked IO mouse input API: (#4858) [@thedmd, @ocornut] +- Reworked IO mouse input API: (#4921, #4858) [@thedmd, @ocornut] - Added io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions, obsoleting writing directly to io.MousePos, io.MouseDown[], io.MouseWheel, etc. - This enable input queue trickling to support low framerates. (#2787, #1992, #3383, #2525, #1320) -- Reworked IO keyboard input API: (#2625, #3724) [@thedmd, @ocornut] +- Reworked IO keyboard input API: (#4921, #2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. @@ -71,16 +68,19 @@ Breaking Changes: - Basically the trick we took advantage of is that we previously only supported native keycode from 0 to 511, so ImGuiKey values can still express a legacy native keycode, and new named keys are all >= 512. - This will enable a few things in the future: - - Access to portable keys allows for backed-agnostic keyboard input code. Until now it was difficult to - share code using keyboard accross project because of this gap. (#2625, #3724) + - Access to portable keys allows for backend-agnostic keyboard input code. Until now it was difficult + to share code using keyboard accross project because of this gap. (#2625, #3724) - Access to full key ranges will allow us to develop a proper keyboard shortcut system. (#456) - io.AddKeyEvent() will later be turned into a trickling IO queue (for all inputs) to handle very low framerate better. (#2525, #2787, #3383) - io.SetKeyEventNativeData() include native keycode/scancode which will later be exposed. (#3141, #2959) -- Reworked IO nav/gamepad input API and unifying inputs sources: (#4858, #787) +- Reworked IO nav/gamepad input API and unifying inputs sources: (#4921, #4858, #787) - Added full range of ImGuiKey_GamepadXXXX enums (e.g. ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadR2) to use with io.AddKeyEvent(), io.AddKeyAnalogEvent(). - Added io.AddKeyAnalogEvent() function, obsoleting writing directly to io.NavInputs[] arrays. - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) +- Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) + This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). + (Instead you may implement custom expression evaluators to provide a better version of this). - Backends: GLFW: backend now uses glfwSetCursorPosCallback(). - If calling ImGui_ImplGlfw_InitXXX with install_callbacks=true: nothing to do. is already done for you. - If calling ImGui_ImplGlfw_InitXXX with install_callbacks=false: you WILL NEED to register the GLFW callback @@ -126,32 +126,34 @@ Other Changes: other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) - Backends: GLFW: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: GLFW: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) -- Backends: GLFW: Update mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. +- Backends: GLFW: Retrieve mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. - Backends: GLFW: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: Win32: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) -- Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. +- Backends: Win32: Retrieve mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: Win32: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. -- Backends: SDL: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) -- Backends: SDL: Update mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. +- Backends: SDL: Submit key data using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: SDL: Retrieve mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. - Backends: SDL: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: SDL: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: Allegro5: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Allegro5: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) +- Backends: OSX: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: OSX: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: OSX: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) -- Backends: Android, GLUT, OSX: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) -- Backends: Android, GLUT, OSX: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) +- Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] +- Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) +- Backends: Android, GLUT: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: Android, GLUT: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. - Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but can theorically be created by user code manipulating a ImDrawList. (#4857) -- Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] -- Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) - Backends: Vulkan: Added support for ImTextureID as VkDescriptorSet, add ImGui_ImplVulkan_AddTexture(). (#914) [@martty] - Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and wgpuRenderPassEncoderSetVertexBuffer() calls. (#4891) [@FeepsDev] diff --git a/imgui.cpp b/imgui.cpp index a8d069d4..3df186d0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -391,7 +391,7 @@ CODE - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent() - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent() - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent() - - Backend writing to io.MouseHoveredViewpot -> backend should call io.AddMouseViewportEvent() [Docking branch only] + - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only] - 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. - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) From c2db4c246205820d017bee544f257aa42c9b4723 Mon Sep 17 00:00:00 2001 From: thedmd Date: Fri, 21 Jan 2022 15:24:54 +0100 Subject: [PATCH 295/332] Demo: draw a section of keyboard in "Inputs > Keyboard, Gamepad & Navigation state" to visualize keys. --- docs/CHANGELOG.txt | 1 + imgui_demo.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 03bfb598..577624d6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -115,6 +115,7 @@ Other Changes: - Tables: Fixed positioning of Sort icon on right-most column with some settings (not resizable + no borders). (#4918). - Nav: Fixed gamepad navigation in wrapping popups not wrapping all the way. (#4365) - Sliders, Drags: Fixed text input of values with a leading sign, common when using a format enforcing sign. (#4917) +- Demo: draw a section of keyboard in "Inputs > Keyboard, Gamepad & Navigation state" to visualize keys. [@thedmd] - Platform IME: changed io.ImeSetInputScreenPosFn() to io.SetPlatformImeDataFn() API, now taking a ImGuiPlatformImeData structure which we can more easily extend in the future. - Platform IME: moved io.ImeWindowHandle to GetMainViewport()->PlatformHandleRaw. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3fefc514..d73389fe 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5712,6 +5712,51 @@ static void ShowDemoWindowMisc() ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]); } ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } + + // Draw an arbitrary US keyboard layout to visualize translated keys + { + 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 ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f); + const float key_row_offset = 9.0f; + + ImVec2 board_min = ImGui::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); + ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y); + + struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; }; + const KeyLayoutData keys_to_display[] = + { + { 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R }, + { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F }, + { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V } + }; + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRect(board_min, board_max, true); + for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++) + { + const KeyLayoutData* key_data = &keys_to_display[n]; + ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y); + ImVec2 key_max = ImVec2(key_min.x + key_size.x, key_min.y + key_size.y); + draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding); + draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding); + ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y); + ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y); + draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f); + draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding); + ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y); + draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label); + if (ImGui::IsKeyDown(key_data->Key)) + draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding); + } + draw_list->PopClipRect(); + ImGui::Dummy(ImVec2(board_max.x - board_min.x, board_max.y - board_min.y)); + } ImGui::TreePop(); } From cff034245471e88d2dcf6b72314f2333546bbdf9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Jan 2022 17:49:04 +0100 Subject: [PATCH 296/332] Internals: moved lines into a Inputs section. --- imgui_internal.h | 132 +++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 63 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 339a6e2c..798c84f7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -18,6 +18,7 @@ Index of this file: // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Inputs support // [SECTION] Clipper support // [SECTION] Navigation support // [SECTION] Columns support @@ -899,69 +900,6 @@ enum ImGuiPlotType ImGuiPlotType_Histogram }; -enum ImGuiInputEventType -{ - ImGuiInputEventType_None = 0, - ImGuiInputEventType_MousePos, - ImGuiInputEventType_MouseWheel, - ImGuiInputEventType_MouseButton, - ImGuiInputEventType_Key, - ImGuiInputEventType_KeyMods, - ImGuiInputEventType_Char, - ImGuiInputEventType_Focus, - ImGuiInputEventType_COUNT -}; - -enum ImGuiInputSource -{ - ImGuiInputSource_None = 0, - ImGuiInputSource_Mouse, - ImGuiInputSource_Keyboard, - ImGuiInputSource_Gamepad, - ImGuiInputSource_Clipboard, // Currently only used by InputText() - ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only - ImGuiInputSource_COUNT -}; - -// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? -// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' -struct ImGuiInputEventMousePos { float PosX, PosY; }; -struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; -struct ImGuiInputEventMouseButton { int Button; bool Down; }; -struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; -struct ImGuiInputEventKeyMods { ImGuiKeyModFlags Mods; }; -struct ImGuiInputEventText { unsigned int Char; }; -struct ImGuiInputEventAppFocused { bool Focused; }; - -struct ImGuiInputEvent -{ - ImGuiInputEventType Type; - ImGuiInputSource Source; - union - { - ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos - ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel - ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton - ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key - ImGuiInputEventKeyMods KeyMods; // if Type == ImGuiInputEventType_Modifiers - ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text - ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus - }; - bool AddedByTestEngine; - - ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } -}; - -// FIXME-NAV: Clarify/expose various repeat delay/rate -enum ImGuiInputReadMode -{ - ImGuiInputReadMode_Down, - ImGuiInputReadMode_Pressed, - ImGuiInputReadMode_Released, - ImGuiInputReadMode_Repeat, - ImGuiInputReadMode_RepeatSlow, - ImGuiInputReadMode_RepeatFast -}; enum ImGuiPopupPositionPolicy { @@ -1215,6 +1153,74 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// [SECTION] Inputs support +//----------------------------------------------------------------------------- + +enum ImGuiInputEventType +{ + ImGuiInputEventType_None = 0, + ImGuiInputEventType_MousePos, + ImGuiInputEventType_MouseWheel, + ImGuiInputEventType_MouseButton, + ImGuiInputEventType_Key, + ImGuiInputEventType_KeyMods, + ImGuiInputEventType_Char, + ImGuiInputEventType_Focus, + ImGuiInputEventType_COUNT +}; + +enum ImGuiInputSource +{ + ImGuiInputSource_None = 0, + ImGuiInputSource_Mouse, + ImGuiInputSource_Keyboard, + ImGuiInputSource_Gamepad, + ImGuiInputSource_Clipboard, // Currently only used by InputText() + ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only + ImGuiInputSource_COUNT +}; + +// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? +// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' +struct ImGuiInputEventMousePos { float PosX, PosY; }; +struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; +struct ImGuiInputEventMouseButton { int Button; bool Down; }; +struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; +struct ImGuiInputEventKeyMods { ImGuiKeyModFlags Mods; }; +struct ImGuiInputEventText { unsigned int Char; }; +struct ImGuiInputEventAppFocused { bool Focused; }; + +struct ImGuiInputEvent +{ + ImGuiInputEventType Type; + ImGuiInputSource Source; + union + { + ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos + ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel + ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton + ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key + ImGuiInputEventKeyMods KeyMods; // if Type == ImGuiInputEventType_Modifiers + ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text + ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus + }; + bool AddedByTestEngine; + + ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } +}; + +// FIXME-NAV: Clarify/expose various repeat delay/rate +enum ImGuiInputReadMode +{ + ImGuiInputReadMode_Down, + ImGuiInputReadMode_Pressed, + ImGuiInputReadMode_Released, + ImGuiInputReadMode_Repeat, + ImGuiInputReadMode_RepeatSlow, + ImGuiInputReadMode_RepeatFast +}; + //----------------------------------------------------------------------------- // [SECTION] Clipper support //----------------------------------------------------------------------------- From 4fb0c1f9639679402ea005ec70b807d96a072505 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Fri, 21 Jan 2022 23:03:09 +0000 Subject: [PATCH 297/332] Correct some typos in FAQ.md (#4924) --- docs/FAQ.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 8c46d268..aee42cae 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -139,7 +139,7 @@ void MyLowLevelMouseButtonHandler(int button, bool down) - The gamepad/keyboard navigation is fairly functional and keeps being improved. The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. Gamepad support is particularly useful to use Dear ImGui on a game console (e.g. PS4, Switch, XB1) without a mouse connected! - Keyboard: set `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard` to enable. - Gamepad: set `io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad` to enable (with a supporting backend). -- See [Control Sheets for Gamepads](http://www.dearimgui.org/controls_sheets) (reference PNG/PSD for for PS4, XB1, Switch gamepads). +- See [Control Sheets for Gamepads](http://www.dearimgui.org/controls_sheets) (reference PNG/PSD for PS4, XB1, Switch gamepads). - See `USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS` section of [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp) for more details. ##### [Return to Index](#index) @@ -463,7 +463,7 @@ draw_list->AddCircleFilled(ImVec2(p.x + 50, p.y + 50), 30.0f, IM_COL32(255, 0, 0 // Draw a 3 pixel thick yellow line draw_list->AddLine(ImVec2(p.x, p.y), ImVec2(p.x + 100.0f, p.y + 100.0f), IM_COL32(255, 255, 0, 255), 3.0f); -// Advance the ImGui cursor to claim space in the window (otherwise the window will appears small and needs to be resized) +// Advance the ImGui cursor to claim space in the window (otherwise the window will appear small and needs to be resized) ImGui::Dummy(ImVec2(200, 200)); ImGui::End(); From dd6a44abb14643824e62cd3ed1d818b189bbf530 Mon Sep 17 00:00:00 2001 From: sergeyn Date: Mon, 24 Jan 2022 12:09:02 +0100 Subject: [PATCH 298/332] Fix compiler warning for Intel compiler (#4934) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index 6581bdab..a09566a0 100644 --- a/imgui.h +++ b/imgui.h @@ -101,7 +101,7 @@ Index of this file: #endif // Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions) -#if defined(_MSC_VER) && !defined(__clang__) && !defined(IMGUI_DEBUG_PARANOID) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID) #define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off)) #define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop)) #else From cda3db14493a4891971d7acd4ee2e6c8b9443dbd Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 24 Jan 2022 12:10:40 +0100 Subject: [PATCH 299/332] Backends: SDL: Fixed key mapping for ImGuiKey_Menu (#4921) + misc typos (#4928) --- backends/imgui_impl_sdl.cpp | 2 +- docs/FONTS.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 0d01cd49..2c2ede95 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -171,7 +171,7 @@ static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) case SDLK_RSHIFT: return ImGuiKey_RightShift; case SDLK_RALT: return ImGuiKey_RightAlt; case SDLK_RGUI: return ImGuiKey_RightSuper; - case SDLK_MENU: return ImGuiKey_Menu; + case SDLK_APPLICATION: return ImGuiKey_Menu; case SDLK_0: return ImGuiKey_0; case SDLK_1: return ImGuiKey_1; case SDLK_2: return ImGuiKey_2; diff --git a/docs/FONTS.md b/docs/FONTS.md index 09c1cb9a..023e0490 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -292,8 +292,8 @@ for (int rect_n = 0; rect_n < IM_ARRAYSIZE(rect_ids); rect_n++) ## Using Font Data Embedded In Source Code - Compile and use [binary_to_compressed_c.cpp](https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp) to create a compressed C style array that you can embed in source code. -- See the documentation in [binary_to_compressed_c.cpp](https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp) for instruction on how to use the tool. -- You may find a precompiled version binary_to_compressed_c.exe for Windows instead of demo binaries package (see [README](https://github.com/ocornut/imgui/blob/master/docs/README.md)). +- See the documentation in [binary_to_compressed_c.cpp](https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp) for instructions on how to use the tool. +- You may find a precompiled version binary_to_compressed_c.exe for Windows inside the demo binaries package (see [README](https://github.com/ocornut/imgui/blob/master/docs/README.md)). - The tool can optionally output Base85 encoding to reduce the size of _source code_ but the read-only arrays in the actual binary will be about 20% bigger. Then load the font with: From b17b2fb732f4dda7641461a30c446837776d6667 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 24 Jan 2022 13:13:38 +0100 Subject: [PATCH 300/332] Popups: Fixed an issue when reopening a same popup multiple times would offset them by 1 pixel on the right. (#4936) Passing explicit ImGuiPopupFlags_MouseButtonRight to OpenPopupOnItemClick() calls somehow document the unusual (due to legacy) default value. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 16 +++++----------- imgui.h | 3 ++- imgui_demo.cpp | 4 ++-- imgui_widgets.cpp | 12 ++++++------ 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 577624d6..cbd324b5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -110,6 +110,7 @@ Other Changes: - Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Popups: Fixed a regression crash when a new window is created after a modal on the same frame. (#4920) [@rokups] +- Popups: Fixed an issue when reopening a same popup multiple times would offset them by 1 pixel on the right. (#4936) - Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt] - Tables: Fixed positioning of Sort icon on right-most column with some settings (not resizable + no borders). (#4918). diff --git a/imgui.cpp b/imgui.cpp index 3df186d0..bf2452d8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6235,8 +6235,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) window->Pos = FindBestWindowPosForPopup(window); - else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) - window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = FindBestWindowPosForPopup(window); @@ -9261,7 +9259,7 @@ void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags // - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). // This is essentially the same as: // id = str_id ? GetID(str_id) : GetItemID(); -// OpenPopupOnItemClick(str_id); +// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight); // return BeginPopup(id); // Which is essentially the same as: // id = str_id ? GetID(str_id) : GetItemID(); @@ -9420,11 +9418,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); } - if (window->Flags & ImGuiWindowFlags_Popup) - { - ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); - } if (window->Flags & ImGuiWindowFlags_Tooltip) { // Position tooltip (always follows mouse) @@ -9932,9 +9925,10 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) - if (IsMousePosValid(&g.IO.MousePos)) - return g.IO.MousePos; - return g.MouseLastValidPos; + // 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. + // In theory we could move that +1.0f offset in OpenPopupEx() + ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos; + return ImVec2(p.x + 1.0f, p.y); } else { diff --git a/imgui.h b/imgui.h index a09566a0..20edf839 100644 --- a/imgui.h +++ b/imgui.h @@ -681,6 +681,7 @@ namespace ImGui // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. + // - IMPORTANT: Notice that for OpenPopupOnItemClick() we exceptionally default flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) @@ -690,7 +691,7 @@ namespace ImGui // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. // - They are convenient to easily create context menus, hence the name. // - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future. - // - IMPORTANT: we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight. + // - IMPORTANT: Notice that we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight. IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d73389fe..7952615b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7570,8 +7570,8 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Context menu (under default mouse threshold) ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right); - if (opt_enable_context_menu && ImGui::IsMouseReleased(ImGuiMouseButton_Right) && drag_delta.x == 0.0f && drag_delta.y == 0.0f) - ImGui::OpenPopupOnItemClick("context"); + if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f) + ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); if (ImGui::BeginPopup("context")) { if (adding_line) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8f52f1fb..77d86e54 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4924,7 +4924,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } } else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) @@ -4952,7 +4952,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } ImGuiWindow* picker_active_window = NULL; @@ -4969,11 +4969,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Store current color and open a picker g.ColorPickerRef = col_v4; OpenPopup("picker"); - SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(-1, style.ItemSpacing.y)); + SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y)); } } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); if (BeginPopup("picker")) { @@ -5187,7 +5187,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl } } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); } else if (flags & ImGuiColorEditFlags_PickerHueBar) { @@ -5204,7 +5204,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed = value_changed_sv = true; } if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); + OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); // Hue bar logic SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); From 4caf1e9b597a3a9f2e919c2e4c6576f638d5a00d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 24 Jan 2022 13:45:47 +0100 Subject: [PATCH 301/332] Backends: GLFW: fix ImGui_ImplGlfw_TranslateUntranslatedKey() for grave accents. (#456, #2625) + fix preceeding commit. Amend 100ede57 + amend b17b2fb --- backends/imgui_impl_glfw.cpp | 2 +- imgui.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 17c210ac..b5b11838 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -286,7 +286,7 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) const char* key_name = glfwGetKeyName(key, scancode); if (key_name && key_name[0] != 0 && key_name[1] == 0) { - const char char_names[] = "'-=[]\\,;\'./"; + const char char_names[] = "`-=[]\\,;\'./"; const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 }; IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys)); if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); } diff --git a/imgui.cpp b/imgui.cpp index bf2452d8..c6104dc5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6099,7 +6099,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS // Update contents size from last frame for auto-fitting (or use explicit size) - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); if (window->HiddenFramesCanSkipItems > 0) window->HiddenFramesCanSkipItems--; From 08f3aa89723f8e41a8b10aa0c60c6617da563e18 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 24 Jan 2022 14:14:34 +0100 Subject: [PATCH 302/332] Popups: Fix b17b2fb (#4936) --- imgui.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index c6104dc5..2661c71a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6073,6 +6073,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Initialize const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) + const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); window->Active = true; window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); @@ -6234,6 +6235,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) window->Pos = FindBestWindowPosForPopup(window); + else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) + window->Pos = FindBestWindowPosForPopup(window); else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = FindBestWindowPosForPopup(window); @@ -9417,6 +9420,10 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); } + if (window->Flags & ImGuiWindowFlags_Popup) + { + return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here + } if (window->Flags & ImGuiWindowFlags_Tooltip) { // Position tooltip (always follows mouse) From f1a073186c859facfc75823302b5c5b04f43293f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Jan 2022 17:28:43 +0100 Subject: [PATCH 303/332] Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1ddb6cd6..d20f802f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -232,6 +232,7 @@ Docking+Viewports Branch: io.AddMouseViewportEvent() function, then Dear ImGui will revert to its flawed heuristic to find the viewport under. By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few drag and drop situations rather that relying on it everywhere. +- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) - Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) diff --git a/imgui.cpp b/imgui.cpp index 141306bc..83857031 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4892,6 +4892,8 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 if (window->RootWindow->DockIsActive) { ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; + if (draw_list->CmdBuffer.Size == 0) + draw_list->AddDrawCmd(); draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); draw_list->PopClipRect(); From 8555335935d059b2d260b2a449bdf2716d0c2b0f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Jan 2022 12:23:26 +0100 Subject: [PATCH 304/332] Shallow alignment of ImGuiKey stuff + moving some in internals. Internals: add offset to ImBitArray<>, simpify ActiveIdUsingKeyInputMask. --- imgui.cpp | 7 ++++--- imgui.h | 53 ++++++++++++++++++++++------------------------- imgui_demo.cpp | 10 ++++----- imgui_internal.h | 30 +++++++++++++++++---------- imgui_widgets.cpp | 5 +++-- 5 files changed, 56 insertions(+), 49 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2661c71a..77ce8cad 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9182,7 +9182,8 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) return false; } flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); + ImGuiID id = g.CurrentWindow->GetID(str_id); + return BeginPopupEx(id, flags); } // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. @@ -12377,8 +12378,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); int active_id_using_key_input_count = 0; - for (int n = 0; n < g.ActiveIdUsingKeyInputMask.Size; n++) - active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask.TestBit(n) ? 1 : 0; + for (int n = 0; n < ImGuiKey_NamedKey_COUNT; n++) + active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask[n] ? 1 : 0; Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count); 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("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); diff --git a/imgui.h b/imgui.h index 20edf839..d6671609 100644 --- a/imgui.h +++ b/imgui.h @@ -1343,6 +1343,7 @@ enum ImGuiSortDirection_ enum ImGuiKey_ { + // Keyboard ImGuiKey_None = 0, ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN ImGuiKey_LeftArrow, @@ -1394,27 +1395,27 @@ enum ImGuiKey_ ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, - // Gamepad (some of those are expected to be analog values from 0.0f to 1.0f) ..............// NAVIGATION action - ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- - ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- - ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input - ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate - ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu - ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel - ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp - ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown - ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft - ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight - ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow - ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast - ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] - ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] - ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) - ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) - ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp - ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown - ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft - ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight + // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION action + ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- + ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- + ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input + ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate + ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu + ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel + ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp + ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown + ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft + ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight + ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow + ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast + ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] + ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] + ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) + ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) + ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp + ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown + ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft + ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight ImGuiKey_GamepadRStickUp, // [Analog] ImGuiKey_GamepadRStickDown, // [Analog] ImGuiKey_GamepadRStickLeft, // [Analog] @@ -1422,22 +1423,18 @@ enum ImGuiKey_ ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value - // Legacy range used by legacy io.KeyMap[]. Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index. + // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array. // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) - ImGuiKey_LegacyNativeKey_BEGIN = 0, - ImGuiKey_LegacyNativeKey_END = 512, ImGuiKey_NamedKey_BEGIN = 512, ImGuiKey_NamedKey_END = ImGuiKey_COUNT, ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN, // First key stored in KeysData[0] + ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0] #else ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_LegacyNativeKey_BEGIN, // First key stored in KeysData[0] + ImGuiKey_KeysData_OFFSET = 0 // First key stored in KeysData[0] #endif - ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart, - ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7952615b..b59eb4d3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5699,15 +5699,15 @@ static void ShowDemoWindowMisc() // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allow displaying the data for old/new backends. // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes. #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - struct funcs { static bool IsNativeDupe(ImGuiKey) { return false; } }; + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; const ImGuiKey key_first = ImGuiKey_NamedKey_BEGIN; #else - struct funcs { static bool IsNativeDupe(ImGuiKey key) { return key < ImGuiKey_LegacyNativeKey_END && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array const ImGuiKey key_first = 0; #endif - ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } } - ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } - ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } + ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } } + ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } + ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]); } diff --git a/imgui_internal.h b/imgui_internal.h index 798c84f7..3de0f15a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -540,21 +540,19 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran // Helper: ImBitArray class (wrapper over ImBitArray functions) // Store 1-bit per value. -template +template struct IMGUI_API ImBitArray { - static const int Size = BITCOUNT; ImU32 Storage[(BITCOUNT + 31) >> 5]; ImBitArray() { ClearAllBits(); } void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } - bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } - void SetBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArraySetBit(Storage, n); } - void ClearBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArrayClearBit(Storage, n); } - void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) + bool TestBit(int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); } + void SetBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArraySetBit(Storage, n + OFFSET); } + void ClearBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArrayClearBit(Storage, n + OFFSET); } + void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n + OFFSET, n2 + OFFSET); } // Works on range [n..n2) + bool operator[](int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); } }; -template -const int ImBitArray::Size; // Helper: ImBitVector // Store 1-bit per value. @@ -1157,6 +1155,16 @@ struct ImGuiPtrOrIndex // [SECTION] Inputs support //----------------------------------------------------------------------------- +typedef ImBitArray ImBitArrayForNamedKeys; + +enum ImGuiKeyPrivate_ +{ + ImGuiKey_LegacyNativeKey_BEGIN = 0, + ImGuiKey_LegacyNativeKey_END = 512, + ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart, + ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 +}; + enum ImGuiInputEventType { ImGuiInputEventType_None = 0, @@ -1605,7 +1613,7 @@ struct ImGuiContext bool ActiveIdUsingMouseWheel; // Active widget will want to read mouse wheel. Blocks scrolling the underlying window. ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs. - ImBitArray ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. + ImBitArrayForNamedKeys ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) @@ -2652,8 +2660,8 @@ namespace ImGui IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } - inline bool IsActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(IsNamedKey(key)); ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask.TestBit(key - ImGuiKey_NamedKey_BEGIN); } - inline void SetActiveIdUsingKey(ImGuiKey key) { IM_ASSERT(IsNamedKey(key)); ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key - ImGuiKey_NamedKey_BEGIN); } + inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; } + inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; } inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 77d86e54..f0501979 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6885,6 +6885,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) BeginDisabled(); const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; bool pressed; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar @@ -6895,7 +6896,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) 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, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups, ImVec2(w, 0.0f)); + pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, 0.0f)); 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(). @@ -6911,7 +6912,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) 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); - pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); RenderText(text_pos, label); if (icon_w > 0.0f) RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); From 075f4ac66127cc20a72e65720645922b5817ccf1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Jan 2022 15:53:18 +0100 Subject: [PATCH 305/332] Don't merge ImDrawCmd which have had their IdxOffset changed to not be sequential. Fixed CTRL+Tab into an empty window causing artefacts on the highlight rectangle due to bad reordering on ImDrawCmd. This is bit of a weird edge case adding weight to ImDrawCmd merging, if we could rework the mess in RenderDimmedBackgroundBehindWindow() we may be able to undo some of that. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 ++++- imgui_draw.cpp | 15 +++++++++------ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cbd324b5..37391478 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -107,6 +107,7 @@ Other Changes: handled by core automatically for all kind of inputs. (#4858, #2787, #1992, #3383, #2525, #1320) - New IO functions for keyboard/gamepad: AddKeyEvent(), AddKeyAnalogEvent(), AddKeyModsEvent(). - New IO functions for mouse: AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent(). +- Fixed CTRL+Tab into an empty window causing artefacts on the highlight rectangle due to bad reordering on ImDrawCmd. - Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Popups: Fixed a regression crash when a new window is created after a modal on the same frame. (#4920) [@rokups] diff --git a/imgui.cpp b/imgui.cpp index 77ce8cad..87a3bead 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4678,6 +4678,7 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 { // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. + // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order. ImDrawList* draw_list = window->RootWindow->DrawList; if (draw_list->CmdBuffer.Size == 0) draw_list->AddDrawCmd(); @@ -4688,7 +4689,7 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 draw_list->CmdBuffer.pop_back(); draw_list->CmdBuffer.push_front(cmd); draw_list->PopClipRect(); - draw_list->_PopUnusedDrawCmd(); // Since are past the calls to AddDrawListToDrawData() we don't have a _PopUnusedDrawCmd() running on commands. + draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command. } } @@ -4713,6 +4714,8 @@ static void ImGui::RenderDimmedBackgrounds() { ImGuiContext& g = *GImGui; ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); + if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) + return; const bool dim_bg_for_modal = (modal_window != NULL); const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); if (!dim_bg_for_modal && !dim_bg_for_window_list) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 423f8cd2..d431c61a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -488,9 +488,10 @@ 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_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_HeaderSize (IM_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) // Try to merge two last draw commands void ImDrawList::_TryMergeDrawCmds() @@ -498,7 +499,7 @@ void ImDrawList::_TryMergeDrawCmds() IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; ImDrawCmd* prev_cmd = curr_cmd - 1; - if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) + if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) { prev_cmd->ElemCount += curr_cmd->ElemCount; CmdBuffer.pop_back(); @@ -521,7 +522,7 @@ void ImDrawList::_OnChangedClipRect() // Try to merge with previous command if it matches, else use current command ImDrawCmd* prev_cmd = curr_cmd - 1; - if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL) + if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) { CmdBuffer.pop_back(); return; @@ -544,7 +545,7 @@ void ImDrawList::_OnChangedTextureID() // Try to merge with previous command if it matches, else use current command ImDrawCmd* prev_cmd = curr_cmd - 1; - if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL) + if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL) { CmdBuffer.pop_back(); return; @@ -1738,6 +1739,8 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list) if (ch._CmdBuffer.Size > 0 && last_cmd != NULL) { + // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves. + // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter. ImDrawCmd* next_cmd = &ch._CmdBuffer[0]; if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL) { From 718daa153d9d5f64a277bee5c4eeeeaad77f0ae4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 27 Jan 2022 16:59:24 +0100 Subject: [PATCH 306/332] Rendering: defer responsability of calling _PopUnusedDrawCmd() later so RenderDimmedBackgrounds()/RenderMouseCursor() don't need to deal with the side-effects (#4857, #4317) --- imgui.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 87a3bead..26d503ad 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4551,11 +4551,10 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) { - // Remove trailing command if unused. - // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well. - draw_list->_PopUnusedDrawCmd(); 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. @@ -4641,8 +4640,10 @@ static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVectorFramebufferScale = io.DisplayFramebufferScale; for (int n = 0; n < draw_lists->Size; n++) { - draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; - draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; + ImDrawList* draw_list = draw_lists->Data[n]; + draw_list->_PopUnusedDrawCmd(); + draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; } } @@ -4745,7 +4746,6 @@ static void ImGui::RenderDimmedBackgrounds() window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); window->DrawList->PopClipRect(); - window->DrawList->_PopUnusedDrawCmd(); // Since are past the calls to AddDrawListToDrawData() we don't have a _PopUnusedDrawCmd() running on commands. } } From c906c65cac6860e7e77649100cdf8fbf1bb201a0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Jan 2022 17:32:35 +0100 Subject: [PATCH 307/332] (BREAKING) Replaced AddKeyModsEvent() added by 790132a in favor of unified key enum system. Backends: update most. (#4921, #4858) Sorry this is an unusual breaking but since we are WIP it is a good time to make a correction. --- backends/imgui_impl_allegro5.cpp | 11 +++--- backends/imgui_impl_glfw.cpp | 13 +++---- backends/imgui_impl_glut.cpp | 9 ++--- backends/imgui_impl_sdl.cpp | 13 +++---- backends/imgui_impl_win32.cpp | 13 +++---- docs/CHANGELOG.txt | 7 +++- imgui.cpp | 66 +++++++++++++++----------------- imgui.h | 26 +++++++------ imgui_internal.h | 14 +++++-- 9 files changed, 87 insertions(+), 85 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index cef70e63..dcc806a6 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. @@ -448,12 +449,10 @@ static void ImGui_ImplAllegro5_UpdateKeyModifiers() ImGuiIO& io = ImGui::GetIO(); ALLEGRO_KEYBOARD_STATE keys; al_get_keyboard_state(&keys); - ImGuiKeyModFlags key_mods = - ((al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL)) ? ImGuiKeyModFlags_Ctrl : 0) | - ((al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | - ((al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)) ? ImGuiKeyModFlags_Alt : 0) | - ((al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModsEvent(key_mods); + io.AddKeyEvent(ImGuiKey_ModCtrl, al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL)); + io.AddKeyEvent(ImGuiKey_ModShift, al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)); + io.AddKeyEvent(ImGuiKey_ModAlt, al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)); + io.AddKeyEvent(ImGuiKey_ModSuper, al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index b5b11838..7afb3169 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,9 +16,10 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). -// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback(). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. @@ -244,12 +245,10 @@ static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods) { ImGuiIO& io = ImGui::GetIO(); - ImGuiKeyModFlags key_mods = - ((mods & GLFW_MOD_CONTROL) ? ImGuiKeyModFlags_Ctrl : 0) | - ((mods & GLFW_MOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | - ((mods & GLFW_MOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | - ((mods & GLFW_MOD_SUPER) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModsEvent(key_mods); + io.AddKeyEvent(ImGuiKey_ModCtrl, (mods & GLFW_MOD_CONTROL) != 0); + io.AddKeyEvent(ImGuiKey_ModShift, (mods & GLFW_MOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiKey_ModAlt, (mods & GLFW_MOD_ALT) != 0); + io.AddKeyEvent(ImGuiKey_ModSuper, (mods & GLFW_MOD_SUPER) != 0); } void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 85ee838d..d3e638b5 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h. @@ -207,11 +208,9 @@ static void ImGui_ImplGLUT_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); int glut_key_mods = glutGetModifiers(); - ImGuiKeyModFlags key_mods = - ((glut_key_mods & GLUT_ACTIVE_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | - ((glut_key_mods & GLUT_ACTIVE_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | - ((glut_key_mods & GLUT_ACTIVE_ALT) ? ImGuiKeyModFlags_Alt : 0); - io.AddKeyModsEvent(key_mods); + io.AddKeyEvent(ImGuiKey_ModCtrl, (glut_key_mods & GLUT_ACTIVE_CTRL) != 0); + io.AddKeyEvent(ImGuiKey_ModShift, (glut_key_mods & GLUT_ACTIVE_SHIFT) != 0); + io.AddKeyEvent(ImGuiKey_ModAlt, (glut_key_mods & GLUT_ACTIVE_ALT) != 0); } static void ImGui_ImplGLUT_AddKeyEvent(ImGuiKey key, bool down, int native_keycode) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 2c2ede95..0b9f9c0a 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,9 +18,10 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). -// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. @@ -227,12 +228,10 @@ static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode) static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) { ImGuiIO& io = ImGui::GetIO(); - ImGuiKeyModFlags key_mods = - ((sdl_key_mods & KMOD_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | - ((sdl_key_mods & KMOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | - ((sdl_key_mods & KMOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | - ((sdl_key_mods & KMOD_GUI) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModsEvent(key_mods); + io.AddKeyEvent(ImGuiKey_ModCtrl, (sdl_key_mods & KMOD_CTRL) != 0); + io.AddKeyEvent(ImGuiKey_ModShift, (sdl_key_mods & KMOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiKey_ModAlt, (sdl_key_mods & KMOD_ALT) != 0); + io.AddKeyEvent(ImGuiKey_ModSuper, (sdl_key_mods & KMOD_GUI) != 0); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 52b3b5a7..157b57de 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -34,9 +34,10 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). -// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before a key event (not in NewFrame) to fix input queue with very low framerates. +// 2022-01-17: Inputs: always update key mods next and before a key event (not in NewFrame) to fix input queue with very low framerates. // 2022-01-12: Inputs: Update mouse inputs using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Inputs: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. @@ -237,12 +238,10 @@ static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds() static void ImGui_ImplWin32_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); - ImGuiKeyModFlags key_mods = - ((IsVkDown(VK_CONTROL)) ? ImGuiKeyModFlags_Ctrl : 0) | - ((IsVkDown(VK_SHIFT) ) ? ImGuiKeyModFlags_Shift : 0) | - ((IsVkDown(VK_MENU)) ? ImGuiKeyModFlags_Alt : 0) | - ((IsVkDown(VK_APPS)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModsEvent(key_mods); + io.AddKeyEvent(ImGuiKey_ModCtrl, IsVkDown(VK_CONTROL)); + io.AddKeyEvent(ImGuiKey_ModShift, IsVkDown(VK_SHIFT)); + io.AddKeyEvent(ImGuiKey_ModAlt, IsVkDown(VK_MENU)); + io.AddKeyEvent(ImGuiKey_ModSuper, IsVkDown(VK_APPS)); } static void ImGui_ImplWin32_UpdateMouseData() diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 37391478..f3a5b1c6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -44,7 +44,8 @@ Breaking Changes: - This enable input queue trickling to support low framerates. (#2787, #1992, #3383, #2525, #1320) - Reworked IO keyboard input API: (#4921, #2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. + - For keyboard modifiers, you can call io.AddKeyEvent() with ImGuiKey_ModXXX values, + obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. - Added full range of key enums in ImGuiKey (e.g. ImGuiKey_F1). - Added GetKeyName() helper function. @@ -78,6 +79,8 @@ Breaking Changes: io.AddKeyEvent(), io.AddKeyAnalogEvent(). - Added io.AddKeyAnalogEvent() function, obsoleting writing directly to io.NavInputs[] arrays. - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) +- Moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place + marked "private" in comments. Will still be used internally. - Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). (Instead you may implement custom expression evaluators to provide a better version of this). @@ -105,7 +108,7 @@ Other Changes: - IO: Added event based input queue API, which now trickles events to support low framerates. [@thedmd, @ocornut] Previously the most common issue case (button presses in low framerates) was handled by backend. This is now handled by core automatically for all kind of inputs. (#4858, #2787, #1992, #3383, #2525, #1320) - - New IO functions for keyboard/gamepad: AddKeyEvent(), AddKeyAnalogEvent(), AddKeyModsEvent(). + - New IO functions for keyboard/gamepad: AddKeyEvent(), AddKeyAnalogEvent(). - New IO functions for mouse: AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent(). - Fixed CTRL+Tab into an empty window causing artefacts on the highlight rectangle due to bad reordering on ImDrawCmd. - Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, diff --git a/imgui.cpp b/imgui.cpp index 26d503ad..fd1a10f7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -384,6 +384,7 @@ 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. + - 2022/01/26 (1.87) - inputs: moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place marked "private" in comments. Will still be used internally. - 2022/01/20 (1.87) - inputs: reworded gamepad IO. - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). @@ -396,8 +397,9 @@ CODE - 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() + - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiKey_ModXXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiKey_ModXXX values.* - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert. - - inputs: added io.AddKeyModsEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. + - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) @@ -1315,18 +1317,6 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native #endif } -void ImGuiIO::AddKeyModsEvent(ImGuiKeyModFlags modifiers) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(&g.IO == this && "Can only add events to current context."); - - ImGuiInputEvent e; - e.Type = ImGuiInputEventType_KeyMods; - e.Source = ImGuiInputSource_Keyboard; - e.KeyMods.Mods = modifiers; - g.InputEventsQueue.push_back(e); -} - // Queue a mouse move event void ImGuiIO::AddMousePosEvent(float x, float y) { @@ -3944,6 +3934,13 @@ static void ImGui::UpdateKeyboardInputs() io.KeysData[key].Down = io.KeysDown[n]; io.BackendUsingLegacyKeyArrays = 1; } + if (io.BackendUsingLegacyKeyArrays == 1) + { + io.KeysData[ImGuiKey_ModCtrl].Down = io.KeyCtrl; + io.KeysData[ImGuiKey_ModShift].Down = io.KeyShift; + io.KeysData[ImGuiKey_ModAlt].Down = io.KeyAlt; + io.KeysData[ImGuiKey_ModSuper].Down = io.KeySuper; + } } #endif @@ -4331,6 +4328,11 @@ void ImGui::NewFrame() // Update keyboard input state UpdateKeyboardInputs(); + //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl)); + //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift)); + //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt)); + //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper)); + // Update gamepad/keyboard navigation NavUpdate(); @@ -7513,7 +7515,8 @@ static const char* const GKeyNames[] = "GamepadDpadUp", "GamepadDpadDown", "GamepadDpadLeft", "GamepadDpadRight", "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3", "GamepadLStickUp", "GamepadLStickDown", "GamepadLStickLeft", "GamepadLStickRight", - "GamepadRStickUp", "GamepadRStickDown", "GamepadRStickLeft", "GamepadRStickRight" + "GamepadRStickUp", "GamepadRStickDown", "GamepadRStickLeft", "GamepadRStickRight", + "ModCtrl", "ModShift", "ModAlt", "ModSuper" }; IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); @@ -7758,7 +7761,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) ImGuiIO& io = g.IO; bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputed = false; - int mouse_button_changed = 0x00, key_mods_changed = 0x00; + int mouse_button_changed = 0x00; ImBitArray key_changed_mask; int event_n = 0; @@ -7773,7 +7776,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) if (io.MousePos.x != event_pos.x || io.MousePos.y != event_pos.y) { // Trickling Rule: Stop processing queued events if we already handled a mouse button change - if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || key_mods_changed || text_inputed)) + if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputed)) break; io.MousePos = event_pos; mouse_moved = true; @@ -7806,8 +7809,9 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) } else if (e->Type == ImGuiInputEventType_Key) { - IM_ASSERT(e->Key.Key != ImGuiKey_None); - const int keydata_index = (e->Key.Key - ImGuiKey_KeysData_OFFSET); + ImGuiKey key = e->Key.Key; + IM_ASSERT(key != ImGuiKey_None); + const int keydata_index = (key - ImGuiKey_KeysData_OFFSET); ImGuiKeyData* keydata = &io.KeysData[keydata_index]; if (keydata->Down != e->Key.Down || keydata->AnalogValue != e->Key.AnalogValue) { @@ -7818,23 +7822,15 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) keydata->AnalogValue = e->Key.AnalogValue; key_changed = true; key_changed_mask.SetBit(keydata_index); - } - } - else if (e->Type == ImGuiInputEventType_KeyMods) - { - const ImGuiKeyModFlags modifiers = e->KeyMods.Mods; - if (io.KeyMods != modifiers) - { - // Trickling Rule: Stop processing queued events if we got multiple action on the same button - ImGuiKeyModFlags modifiers_that_are_changing = (io.KeyMods ^ modifiers); - if (trickle_fast_inputs && (key_mods_changed & modifiers_that_are_changing) != 0) - break; - io.KeyMods = modifiers; - io.KeyCtrl = (modifiers & ImGuiKeyModFlags_Ctrl) != 0; - io.KeyShift = (modifiers & ImGuiKeyModFlags_Shift) != 0; - io.KeyAlt = (modifiers & ImGuiKeyModFlags_Alt) != 0; - io.KeySuper = (modifiers & ImGuiKeyModFlags_Super) != 0; - key_mods_changed |= modifiers_that_are_changing; + + if (key == ImGuiKey_ModCtrl || key == ImGuiKey_ModShift || key == ImGuiKey_ModAlt || key == ImGuiKey_ModSuper) + { + if (key == ImGuiKey_ModCtrl) { io.KeyCtrl = keydata->Down; } + if (key == ImGuiKey_ModShift) { io.KeyShift = keydata->Down; } + if (key == ImGuiKey_ModAlt) { io.KeyAlt = keydata->Down; } + if (key == ImGuiKey_ModSuper) { io.KeySuper = keydata->Down; } + io.KeyMods = GetMergedKeyModFlags(); + } } } else if (e->Type == ImGuiInputEventType_Char) diff --git a/imgui.h b/imgui.h index d6671609..8732db4e 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18613 +#define IMGUI_VERSION_NUM 18614 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1421,6 +1421,19 @@ enum ImGuiKey_ ImGuiKey_GamepadRStickLeft, // [Analog] ImGuiKey_GamepadRStickRight, // [Analog] + // Keyboard Modifiers + // - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing + // them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc. + // - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those + // and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl). + // - 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... + ImGuiKey_ModCtrl, + ImGuiKey_ModShift, + ImGuiKey_ModAlt, + ImGuiKey_ModSuper, + ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array. @@ -1441,16 +1454,6 @@ enum ImGuiKey_ #endif }; -// To test io.KeyMods (which is a combination of individual fields io.KeyCtrl, io.KeyShift, io.KeyAlt set by user/backend) -enum ImGuiKeyModFlags_ -{ - ImGuiKeyModFlags_None = 0, - ImGuiKeyModFlags_Ctrl = 1 << 0, - ImGuiKeyModFlags_Shift = 1 << 1, - ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key -}; - // Gamepad/Keyboard navigation // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). @@ -1957,7 +1960,6 @@ struct ImGuiIO // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. - IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update diff --git a/imgui_internal.h b/imgui_internal.h index 3de0f15a..442ab57b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -898,7 +898,6 @@ enum ImGuiPlotType ImGuiPlotType_Histogram }; - enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, @@ -1165,6 +1164,16 @@ enum ImGuiKeyPrivate_ ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 }; +// Helper to store all mods easily. Stored in e.g. io.KeyMods. +enum ImGuiKeyModFlags_ +{ + ImGuiKeyModFlags_None = 0, + ImGuiKeyModFlags_Ctrl = 1 << 0, + ImGuiKeyModFlags_Shift = 1 << 1, + ImGuiKeyModFlags_Alt = 1 << 2, + ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key +}; + enum ImGuiInputEventType { ImGuiInputEventType_None = 0, @@ -1172,7 +1181,6 @@ enum ImGuiInputEventType ImGuiInputEventType_MouseWheel, ImGuiInputEventType_MouseButton, ImGuiInputEventType_Key, - ImGuiInputEventType_KeyMods, ImGuiInputEventType_Char, ImGuiInputEventType_Focus, ImGuiInputEventType_COUNT @@ -1195,7 +1203,6 @@ struct ImGuiInputEventMousePos { float PosX, PosY; }; struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; struct ImGuiInputEventMouseButton { int Button; bool Down; }; struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; -struct ImGuiInputEventKeyMods { ImGuiKeyModFlags Mods; }; struct ImGuiInputEventText { unsigned int Char; }; struct ImGuiInputEventAppFocused { bool Focused; }; @@ -1209,7 +1216,6 @@ struct ImGuiInputEvent ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key - ImGuiInputEventKeyMods KeyMods; // if Type == ImGuiInputEventType_Modifiers ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus }; From 5429f0f6b55c32bd6af150c6436d31e61ac29a77 Mon Sep 17 00:00:00 2001 From: thedmd Date: Wed, 26 Jan 2022 22:00:50 +0100 Subject: [PATCH 308/332] Backends: OSX, Android: Replaced AddKeyModsEvent() added by 790132a in favor of unified key enum system. (#4921, #4858) --- backends/imgui_impl_android.cpp | 51 +++++---------------------------- backends/imgui_impl_osx.mm | 51 +++++++++++++-------------------- 2 files changed, 27 insertions(+), 75 deletions(-) diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 82f388a5..bb8de811 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -19,6 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. @@ -26,29 +27,15 @@ #include "imgui.h" #include "imgui_impl_android.h" #include -#include -#include #include #include #include #include -struct KeyEvent -{ - ImGuiKey Key; - bool Down; - int NativeKeycode; - int NativeScancode; - - KeyEvent(): Key(ImGuiKey_None), Down(false), NativeKeycode(-1), NativeScancode(-1) {} -}; - // Android data static double g_Time = 0.0; static ANativeWindow* g_Window; static char g_LogTag[] = "ImGuiExample"; -static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. -static ImGuiKeyModFlags g_KeyModFlags = ImGuiKeyModFlags_None; static ImGuiKey ImGui_ImplAndroid_KeyCodeToImGuiKey(int32_t key_code) { @@ -176,15 +163,10 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) int32_t event_action = AKeyEvent_getAction(input_event); int32_t event_meta_state = AKeyEvent_getMetaState(input_event); - g_KeyModFlags = ImGuiKeyModFlags_None; - if ((event_meta_state & AMETA_CTRL_ON) != 0) - g_KeyModFlags |= ImGuiKeyModFlags_Ctrl; - if ((event_meta_state & AMETA_SHIFT_ON) != 0) - g_KeyModFlags |= ImGuiKeyModFlags_Shift; - if ((event_meta_state & AMETA_ALT_ON) != 0) - g_KeyModFlags |= ImGuiKeyModFlags_Alt; - if ((event_meta_state & AMETA_META_ON) != 0) - g_KeyModFlags |= ImGuiKeyModFlags_Super; + io.AddKeyEvent(ImGuiKey_ModCtrl, (event_meta_state & AMETA_CTRL_ON) != 0); + io.AddKeyEvent(ImGuiKey_ModShift, (event_meta_state & AMETA_SHIFT_ON) != 0); + io.AddKeyEvent(ImGuiKey_ModAlt, (event_meta_state & AMETA_ALT_ON) != 0); + io.AddKeyEvent(ImGuiKey_ModSuper, (event_meta_state & AMETA_META_ON) != 0); switch (event_action) { @@ -197,13 +179,8 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) ImGuiKey key = ImGui_ImplAndroid_KeyCodeToImGuiKey(event_key_code); if (key != ImGuiKey_None && (event_action == AKEY_EVENT_ACTION_DOWN || event_action == AKEY_EVENT_ACTION_UP)) { - KeyEvent io_event; - io_event.Key = key; - io_event.Down = event_action == AKEY_EVENT_ACTION_DOWN; - io_event.NativeKeycode = event_key_code; - io_event.NativeScancode = event_scan_code; - - g_KeyEventQueues[key].push(io_event); + io.AddKeyEvent(key, event_action == AKEY_EVENT_ACTION_DOWN); + io.SetKeyEventNativeData(key, event_key_code, event_scan_code); } break; @@ -280,20 +257,6 @@ void ImGui_ImplAndroid_NewFrame() { ImGuiIO& io = ImGui::GetIO(); - // Process queued key events - // FIXME: This is a workaround for multiple key event actions occurring at once (see above) and can be removed once we use upcoming input queue. - for (auto& key_queue : g_KeyEventQueues) - { - if (key_queue.second.empty()) - continue; - - auto& key_event = key_queue.second.front(); - io.AddKeyEvent(key_event.Key, key_event.Down); - io.SetKeyEventNativeData(key_event.Key, key_event.NativeKeycode, key_event.NativeScancode); // To support legacy indexing (<1.87 user code) - key_queue.second.pop(); - } - io.AddKeyModsEvent(g_KeyModFlags); - // Setup display size (every frame to accommodate for window resizing) int32_t window_width = ANativeWindow_getWidth(g_Window); int32_t window_height = ANativeWindow_getHeight(g_Window); diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 936617a5..e21a7e66 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). // 2022-01-12: Inputs: Added basic Platform IME support, hooking the io.SetPlatformImeDataFn() function. @@ -664,18 +665,12 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if (event.type == NSEventTypeFlagsChanged) { unsigned short key_code = [event keyCode]; - unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; - - ImGuiKeyModFlags imgui_key_mods = ImGuiKeyModFlags_None; - if (flags & NSEventModifierFlagShift) - imgui_key_mods |= ImGuiKeyModFlags_Shift; - if (flags & NSEventModifierFlagControl) - imgui_key_mods |= ImGuiKeyModFlags_Ctrl; - if (flags & NSEventModifierFlagOption) - imgui_key_mods |= ImGuiKeyModFlags_Alt; - if (flags & NSEventModifierFlagCommand) - imgui_key_mods |= ImGuiKeyModFlags_Super; - io.AddKeyModsEvent(imgui_key_mods); + NSEventModifierFlags modifier_flags = [event modifierFlags]; + + io.AddKeyEvent(ImGuiKey_ModShift, (modifier_flags & NSEventModifierFlagShift) != 0); + io.AddKeyEvent(ImGuiKey_ModCtrl, (modifier_flags & NSEventModifierFlagControl) != 0); + io.AddKeyEvent(ImGuiKey_ModAlt, (modifier_flags & NSEventModifierFlagOption) != 0); + io.AddKeyEvent(ImGuiKey_ModSuper, (modifier_flags & NSEventModifierFlagCommand) != 0); ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code); if (key != ImGuiKey_None) @@ -684,28 +679,22 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) // to use hardware dependent masks to extract that information. // 'imgui_mask' is left as a fallback. NSEventModifierFlags mask = 0; - ImGuiKeyModFlags imgui_mask = ImGuiKeyModFlags_None; - switch (key_code) + switch (key) { - case kVK_Control: mask = 0x0001; imgui_mask = ImGuiKeyModFlags_Ctrl; break; - case kVK_RightControl: mask = 0x2000; imgui_mask = ImGuiKeyModFlags_Ctrl; break; - case kVK_Shift: mask = 0x0002; imgui_mask = ImGuiKeyModFlags_Shift; break; - case kVK_RightShift: mask = 0x0004; imgui_mask = ImGuiKeyModFlags_Shift; break; - case kVK_Command: mask = 0x0008; imgui_mask = ImGuiKeyModFlags_Super; break; - case kVK_RightCommand: mask = 0x0010; imgui_mask = ImGuiKeyModFlags_Super; break; - case kVK_Option: mask = 0x0020; imgui_mask = ImGuiKeyModFlags_Alt; break; - case kVK_RightOption: mask = 0x0040; imgui_mask = ImGuiKeyModFlags_Alt; break; + case ImGuiKey_LeftCtrl: mask = 0x0001; break; + case ImGuiKey_RightCtrl: mask = 0x2000; break; + case ImGuiKey_LeftShift: mask = 0x0002; break; + case ImGuiKey_RightShift: mask = 0x0004; break; + case ImGuiKey_LeftSuper: mask = 0x0008; break; + case ImGuiKey_RightSuper: mask = 0x0010; break; + case ImGuiKey_LeftAlt: mask = 0x0020; break; + case ImGuiKey_RightAlt: mask = 0x0040; break; + default: + return io.WantCaptureKeyboard; } - if (mask) - { - NSEventModifierFlags modifier_flags = [event modifierFlags]; - io.AddKeyEvent(key, (modifier_flags & mask) != 0); - } - else if (imgui_mask) - { - io.AddKeyEvent(key, (imgui_key_mods & imgui_mask) != 0); - } + NSEventModifierFlags modifier_flags = [event modifierFlags]; + io.AddKeyEvent(key, (modifier_flags & mask) != 0); io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) } From 9def2b04d7188cc6e95128c2bcabe2756ce7b395 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 27 Jan 2022 17:18:29 +0100 Subject: [PATCH 309/332] IO: Added simple (incomplete) filter for duplicates to reduce data spam. (#4921, #4858) --- imgui.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index fd1a10f7..e64d973b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1280,6 +1280,19 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) if (ImGui::IsGamepadKey(key)) BackendUsingLegacyNavInputArray = false; + // Partial filter of duplicates (not strictly needed, but makes data neater in particular for key mods and gamepad values which are most commonly spmamed) + ImGuiKeyData* key_data = ImGui::GetKeyData(key); + if (key_data->Down == down && key_data->AnalogValue == analog_value) + { + bool found = false; + for (int n = g.InputEventsQueue.Size - 1; n >= 0 && !found; n--) + if (g.InputEventsQueue[n].Type == ImGuiInputEventType_Key && g.InputEventsQueue[n].Key.Key == key) + found = true; + if (!found) + return; + } + + // Add event ImGuiInputEvent e; e.Type = ImGuiInputEventType_Key; e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard; From 49e38a5b320629d1f982e1838dca2c89069a9aba Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jan 2022 15:53:09 +0100 Subject: [PATCH 310/332] Revert moving ImGuiKeyModFlags to internal.h (amendc906c65) --- docs/CHANGELOG.txt | 2 -- imgui.cpp | 1 - imgui.h | 12 +++++++++++- imgui_internal.h | 10 ---------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f3a5b1c6..e2fa1ccf 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -79,8 +79,6 @@ Breaking Changes: io.AddKeyEvent(), io.AddKeyAnalogEvent(). - Added io.AddKeyAnalogEvent() function, obsoleting writing directly to io.NavInputs[] arrays. - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) -- Moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place - marked "private" in comments. Will still be used internally. - Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). (Instead you may implement custom expression evaluators to provide a better version of this). diff --git a/imgui.cpp b/imgui.cpp index e64d973b..0a8a03c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -384,7 +384,6 @@ 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. - - 2022/01/26 (1.87) - inputs: moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place marked "private" in comments. Will still be used internally. - 2022/01/20 (1.87) - inputs: reworded gamepad IO. - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). diff --git a/imgui.h b/imgui.h index 8732db4e..a4381113 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18614 +#define IMGUI_VERSION_NUM 18615 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -1454,6 +1454,16 @@ enum ImGuiKey_ #endif }; +// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API. +enum ImGuiKeyModFlags_ +{ + ImGuiKeyModFlags_None = 0, + ImGuiKeyModFlags_Ctrl = 1 << 0, + ImGuiKeyModFlags_Shift = 1 << 1, + ImGuiKeyModFlags_Alt = 1 << 2, + ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key +}; + // Gamepad/Keyboard navigation // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). diff --git a/imgui_internal.h b/imgui_internal.h index 442ab57b..33dfc423 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1164,16 +1164,6 @@ enum ImGuiKeyPrivate_ ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 }; -// Helper to store all mods easily. Stored in e.g. io.KeyMods. -enum ImGuiKeyModFlags_ -{ - ImGuiKeyModFlags_None = 0, - ImGuiKeyModFlags_Ctrl = 1 << 0, - ImGuiKeyModFlags_Shift = 1 << 1, - ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key -}; - enum ImGuiInputEventType { ImGuiInputEventType_None = 0, From 27004aca705e33e8a88f368372628c28f1ee4cd1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jan 2022 15:53:09 +0100 Subject: [PATCH 311/332] Revert moving ImGuiKeyModFlags to internal.h (amendc906c65) # Conflicts: # imgui.cpp --- docs/CHANGELOG.txt | 2 -- imgui.cpp | 1 - imgui.h | 12 +++++++++++- imgui_internal.h | 10 ---------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e3a421fd..e7f056d3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -146,8 +146,6 @@ Breaking Changes: io.AddKeyEvent(), io.AddKeyAnalogEvent(). - Added io.AddKeyAnalogEvent() function, obsoleting writing directly to io.NavInputs[] arrays. - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) -- Moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place - marked "private" in comments. Will still be used internally. - Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). (Instead you may implement custom expression evaluators to provide a better version of this). diff --git a/imgui.cpp b/imgui.cpp index 76b27733..e09a1585 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -392,7 +392,6 @@ CODE - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - - 2022/01/26 (1.87) - inputs: moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place marked "private" in comments. Will still be used internally. - 2022/01/20 (1.87) - inputs: reworded gamepad IO. - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). diff --git a/imgui.h b/imgui.h index fffc7cd1..a172aa3d 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18614 +#define IMGUI_VERSION_NUM 18615 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch @@ -1516,6 +1516,16 @@ enum ImGuiKey_ #endif }; +// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API. +enum ImGuiKeyModFlags_ +{ + ImGuiKeyModFlags_None = 0, + ImGuiKeyModFlags_Ctrl = 1 << 0, + ImGuiKeyModFlags_Shift = 1 << 1, + ImGuiKeyModFlags_Alt = 1 << 2, + ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key +}; + // Gamepad/Keyboard navigation // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). diff --git a/imgui_internal.h b/imgui_internal.h index 13f7f738..db0de88a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1184,16 +1184,6 @@ enum ImGuiKeyPrivate_ ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 }; -// Helper to store all mods easily. Stored in e.g. io.KeyMods. -enum ImGuiKeyModFlags_ -{ - ImGuiKeyModFlags_None = 0, - ImGuiKeyModFlags_Ctrl = 1 << 0, - ImGuiKeyModFlags_Shift = 1 << 1, - ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key -}; - enum ImGuiInputEventType { ImGuiInputEventType_None = 0, From 64d910ad37a33652163c372dce8c5c5b24dac22f Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 29 Jan 2022 18:10:36 +0100 Subject: [PATCH 312/332] Removed IMGUI_API from inline templated classes. (#4962) --- imgui_internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 33dfc423..4a5416f3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -541,7 +541,7 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran // Helper: ImBitArray class (wrapper over ImBitArray functions) // Store 1-bit per value. template -struct IMGUI_API ImBitArray +struct ImBitArray { ImU32 Storage[(BITCOUNT + 31) >> 5]; ImBitArray() { ClearAllBits(); } @@ -622,7 +622,7 @@ struct ImSpanAllocator // Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object. typedef int ImPoolIdx; template -struct IMGUI_API ImPool +struct ImPool { ImVector Buf; // Contiguous data ImGuiStorage Map; // ID->Index @@ -659,7 +659,7 @@ struct IMGUI_API ImPool // We store the chunk size first, and align the final size on 4 bytes boundaries. // The tedious/zealous amount of casting is to avoid -Wcast-align warnings. template -struct IMGUI_API ImChunkStream +struct ImChunkStream { ImVector Buf; From 08350e53e79e2dd44ee40e1143ffcf79401af1fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 31 Jan 2022 11:47:44 +0100 Subject: [PATCH 313/332] Backends: SDL: no support for ImGuiBackendFlags_HasMouseHoveredViewport under OSX/LInux (#4960) --- backends/imgui_impl_sdl.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 7f74f91b..7255ccd5 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -367,10 +367,13 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) if (mouse_can_use_global_state) - { io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + + // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) +#ifdef _WIN32 + if (mouse_can_use_global_state) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;// We can call io.AddMouseViewportEvent() with correct data (optional) - } +#endif bd->Window = window; bd->MouseCanUseGlobalState = mouse_can_use_global_state; @@ -528,11 +531,14 @@ static void ImGui_ImplSDL2_UpdateMouseData() // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). - ImGuiID mouse_viewport_id = 0; - if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) - if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) - mouse_viewport_id = mouse_viewport->ID; - io.AddMouseViewportEvent(mouse_viewport_id); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + { + ImGuiID mouse_viewport_id = 0; + if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) + if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) + mouse_viewport_id = mouse_viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); + } } static void ImGui_ImplSDL2_UpdateMouseCursor() From 422902dbda42de3ac12e614236514044594ab53c Mon Sep 17 00:00:00 2001 From: Clownacy Date: Thu, 27 Jan 2022 17:52:00 +0000 Subject: [PATCH 314/332] Correct typos (#4954, #4955), use IM_STATIC_ASSERT now that old pre-C++11 macro has been obsoleted. --- docs/BACKENDS.md | 2 +- docs/EXAMPLES.md | 2 +- docs/FAQ.md | 2 +- docs/FONTS.md | 2 +- imgui.cpp | 2 +- imgui.h | 4 ++-- imgui_draw.cpp | 7 +++---- misc/cpp/imgui_stdlib.cpp | 4 ---- misc/cpp/imgui_stdlib.h | 4 ---- misc/freetype/README.md | 4 ++-- 10 files changed, 12 insertions(+), 21 deletions(-) diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 1efe1dc7..cac11b6c 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -38,7 +38,7 @@ Dear ImGui is highly portable and only requires a few things to run and render, - Optional: multi-viewports support. etc. -This is essentially what each backends are doing + obligatory portability cruft. Using default backends ensure you can get all those features including the ones that would be harder to implement on your side (e.g. multi-viewports support). +This is essentially what each backend is doing + obligatory portability cruft. Using default backends ensure you can get all those features including the ones that would be harder to implement on your side (e.g. multi-viewports support). It is important to understand the difference between the core Dear ImGui library (files in the root folder) and backends which we are describing here (backends/ folder). diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index cd849060..94f78dcd 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -72,7 +72,7 @@ Please read 'PROGRAMMER GUIDE' in imgui.cpp for notes on how to setup Dear ImGui Please read the comments and instruction at the top of each file. Please read FAQ at http://www.dearimgui.org/faq -If you are using of the backend provided here, you can add the backends/imgui_impl_xxxx(.cpp,.h) +If you are using any of the backends provided here, you can add the backends/imgui_impl_xxxx(.cpp,.h) files to your project and use as-in. Each imgui_impl_xxxx.cpp file comes with its own individual Changelog, so if you want to update them later it will be easier to catch up with what changed. diff --git a/docs/FAQ.md b/docs/FAQ.md index aee42cae..06d51ef4 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -654,7 +654,7 @@ A reasonably skinned application may look like (screenshot from [#2529](https:// ### Q: Why using C++ (as opposed to C)? -Dear ImGui takes advantage of a few C++ languages features for convenience but nothing anywhere Boost insanity/quagmire. Dear ImGui does NOT require C++11 so it can be used with most old C++ compilers. Dear ImGui doesn't use any C++ header file. Language-wise, function overloading and default parameters are used to make the API easier to use and code more terse. Doing so I believe the API is sitting on a sweet spot and giving up on those features would make the API more cumbersome. Other features such as namespace, constructors and templates (in the case of the ImVector<> class) are also relied on as a convenience. +Dear ImGui takes advantage of a few C++ languages features for convenience but nothing anywhere Boost insanity/quagmire. Dear ImGui doesn't use any C++ header file. Dear ImGui uses a very small subset of C++11 features. In particular, function overloading and default parameters are used to make the API easier to use and code more terse. Doing so I believe the API is sitting on a sweet spot and giving up on those features would make the API more cumbersome. Other features such as namespace, constructors and templates (in the case of the ImVector<> class) are also relied on as a convenience. There is an auto-generated [c-api for Dear ImGui (cimgui)](https://github.com/cimgui/cimgui) by Sonoro1234 and Stephan Dilly. It is designed for creating bindings to other languages. If possible, I would suggest using your target language functionalities to try replicating the function overloading and default parameters used in C++ else the API may be harder to use. Also see [Bindings](https://github.com/ocornut/imgui/wiki/Bindings) for various third-party bindings. diff --git a/docs/FONTS.md b/docs/FONTS.md index 023e0490..de7a1c99 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -309,7 +309,7 @@ ImFont* font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(compressed_data_ba ## About filenames -**Please note that many new C/C++ users have issues their files _because the filename they provide is wrong_.** +**Please note that many new C/C++ users have issues loading their files _because the filename they provide is wrong_.** Two things to watch for: - Make sure your IDE/debugger settings starts your executable from the right working directory. In Visual Studio you can change your working directory in project `Properties > General > Debugging > Working Directory`. People assume that their execution will start from the root folder of the project, where by default it oftens start from the folder where object or executable files are stored. diff --git a/imgui.cpp b/imgui.cpp index 0a8a03c1..207329fb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1110,7 +1110,7 @@ ImGuiIO::ImGuiIO() { // Most fields are initialized with zero memset(this, 0, sizeof(*this)); - IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here. + IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Settings ConfigFlags = ImGuiConfigFlags_None; diff --git a/imgui.h b/imgui.h index a4381113..33c4c744 100644 --- a/imgui.h +++ b/imgui.h @@ -224,8 +224,8 @@ typedef signed short ImS16; // 16-bit signed integer typedef unsigned short ImU16; // 16-bit unsigned integer typedef signed int ImS32; // 32-bit signed integer == int typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) -typedef signed long long ImS64; // 64-bit signed integer (C++11) -typedef unsigned long long ImU64; // 64-bit unsigned integer (C++11) +typedef signed long long ImS64; // 64-bit signed integer +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) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d431c61a..73cd6bf5 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -402,10 +402,9 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) void ImDrawList::_ResetForNewFrame() { // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. - // (those should be IM_STATIC_ASSERT() in theory but with our pre C++11 setup the whole check doesn't compile with GCC) - IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); - IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); - IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); + 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)); CmdBuffer.resize(0); IdxBuffer.resize(0); diff --git a/misc/cpp/imgui_stdlib.cpp b/misc/cpp/imgui_stdlib.cpp index cb1fe174..dd6bd8a5 100644 --- a/misc/cpp/imgui_stdlib.cpp +++ b/misc/cpp/imgui_stdlib.cpp @@ -1,10 +1,6 @@ // dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) // This is also an example of how you may wrap your own similar types. -// Compatibility: -// - std::string support is only guaranteed to work from C++11. -// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture) - // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string diff --git a/misc/cpp/imgui_stdlib.h b/misc/cpp/imgui_stdlib.h index f860b0c7..61afc098 100644 --- a/misc/cpp/imgui_stdlib.h +++ b/misc/cpp/imgui_stdlib.h @@ -1,10 +1,6 @@ // dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) // This is also an example of how you may wrap your own similar types. -// Compatibility: -// - std::string support is only guaranteed to work from C++11. -// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture) - // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string diff --git a/misc/freetype/README.md b/misc/freetype/README.md index f7d7bab7..84f6dd06 100644 --- a/misc/freetype/README.md +++ b/misc/freetype/README.md @@ -26,12 +26,12 @@ See https://gist.github.com/ocornut/b3a9ecf13502fd818799a452969649ad ### Comparaison -Small, thin anti-aliased fonts are typically benefiting a lots from Freetype's hinting: +Small, thin anti-aliased fonts typically benefit a lot from FreeType's hinting: ![comparing_font_rasterizers](https://user-images.githubusercontent.com/8225057/107550178-fef87f00-6bd0-11eb-8d09-e2edb2f0ccfc.gif) ### Colorful glyphs/emojis -You can use the `ImGuiFreeTypeBuilderFlags_LoadColor` flag to load certain colorful glyphs. See +You can use the `ImGuiFreeTypeBuilderFlags_LoadColor` flag to load certain colorful glyphs. See the ["Using Colorful Glyphs/Emojis"](https://github.com/ocornut/imgui/edit/master/docs/FONTS.md#using-colorful-glyphsemojis) section of FONTS.md. ![colored glyphs](https://user-images.githubusercontent.com/8225057/106171241-9dc4ba80-6191-11eb-8a69-ca1467b206d1.png) From 551ab84c2d57f64e5710c3473550505bdbfff871 Mon Sep 17 00:00:00 2001 From: rokups Date: Mon, 31 Jan 2022 14:02:22 +0200 Subject: [PATCH 315/332] Backends: OSX: Fix building with old Xcode versions. Builds have failed on Xcode versions that do not yet support `@available` or do not have new APIs (that are unavailable on target OS version) defined at all. Using true build time version checks fixes these issues. --- backends/imgui_impl_osx.mm | 34 ++++++++++++++++++---------------- docs/CHANGELOG.txt | 1 + 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index e21a7e66..d061ff6b 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-31: Fix building with old Xcode versions that are missing gamepad features. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). @@ -46,6 +47,10 @@ // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-07-07: Initial version. +#define APPLE_HAS_BUTTON_OPTIONS (__IPHONE_OS_VERSION_MIN_REQUIRED >= 130000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500 || __TV_OS_VERSION_MIN_REQUIRED >= 130000) +#define APPLE_HAS_CONTROLLER (__IPHONE_OS_VERSION_MIN_REQUIRED >= 140000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 110000 || __TV_OS_VERSION_MIN_REQUIRED >= 140000) +#define APPLE_HAS_THUMBSTICKS (__IPHONE_OS_VERSION_MIN_REQUIRED >= 120100 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101401 || __TV_OS_VERSION_MIN_REQUIRED >= 120100) + @class ImFocusObserver; @class KeyEventResponder; @@ -490,12 +495,11 @@ static void ImGui_ImplOSX_UpdateGamepads() if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; - GCController* controller; - if (@available(macOS 11.0, *)) - controller = GCController.current; - else - controller = GCController.controllers.firstObject; - +#if APPLE_HAS_CONTROLLER + GCController* controller = GCController.current; +#else + GCController* controller = GCController.controllers.firstObject; +#endif if (controller == nil || controller.extendedGamepad == nil) { io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; @@ -509,11 +513,10 @@ static void ImGui_ImplOSX_UpdateGamepads() #define MAP_BUTTON(KEY_NO, BUTTON_NAME) { io.AddKeyEvent(KEY_NO, gp.BUTTON_NAME.isPressed); } #define MAP_ANALOG(KEY_NO, AXIS_NAME, V0, V1) { float vn = (float)(gp.AXIS_NAME.value - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); } const float thumb_dead_zone = 0.0f; - if (@available(macOS 10.15, *)) - { - MAP_BUTTON(ImGuiKey_GamepadStart, buttonMenu); - MAP_BUTTON(ImGuiKey_GamepadBack, buttonOptions); - } + +#if APPLE_HAS_BUTTON_OPTIONS + MAP_BUTTON(ImGuiKey_GamepadBack, buttonOptions); +#endif MAP_BUTTON(ImGuiKey_GamepadFaceDown, buttonA); // Xbox A, PS Cross MAP_BUTTON(ImGuiKey_GamepadFaceRight, buttonB); // Xbox B, PS Circle MAP_BUTTON(ImGuiKey_GamepadFaceLeft, buttonX); // Xbox X, PS Square @@ -526,11 +529,10 @@ static void ImGui_ImplOSX_UpdateGamepads() MAP_ANALOG(ImGuiKey_GamepadR1, rightShoulder, 0.0f, 1.0f); MAP_ANALOG(ImGuiKey_GamepadL2, leftTrigger, 0.0f, 1.0f); MAP_ANALOG(ImGuiKey_GamepadR2, rightTrigger, 0.0f, 1.0f); - if (@available(macOS 10.14.1, *)) - { - MAP_BUTTON(ImGuiKey_GamepadL3, leftThumbstickButton); - MAP_BUTTON(ImGuiKey_GamepadR3, rightThumbstickButton); - } +#if APPLE_HAS_THUMBSTICKS + MAP_BUTTON(ImGuiKey_GamepadL3, leftThumbstickButton); + MAP_BUTTON(ImGuiKey_GamepadR3, rightThumbstickButton); +#endif MAP_ANALOG(ImGuiKey_GamepadLStickLeft, leftThumbstick.xAxis, -thumb_dead_zone, -1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickRight, leftThumbstick.xAxis, +thumb_dead_zone, +1.0f); MAP_ANALOG(ImGuiKey_GamepadLStickUp, leftThumbstick.yAxis, +thumb_dead_zone, +1.0f); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e2fa1ccf..46781bca 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -150,6 +150,7 @@ Other Changes: - Backends: OSX: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) - Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] - Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) +- Backends: OSX: Fix building with old Xcode versions that are missing gamepad features. [@rokups] - Backends: Android, GLUT: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Android, GLUT: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] From 35e197f413756a535bc13813ba87538b2958c07d Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 1 Feb 2022 11:08:47 +0100 Subject: [PATCH 316/332] TestEngine: fixed InvisibleButton() and ArrowButton() not calling the info macro. --- imgui_internal.h | 2 +- imgui_widgets.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 4a5416f3..4fe0ed89 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2907,7 +2907,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #else -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #endif //----------------------------------------------------------------------------- diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f0501979..2a335288 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -731,6 +731,7 @@ bool ImGui::SmallButton(const char* label) // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags) { + ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; @@ -748,16 +749,17 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiBut bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); return pressed; } bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) { + ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - ImGuiContext& g = *GImGui; const ImGuiID id = window->GetID(str_id); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); const float default_size = GetFrameHeight(); @@ -778,6 +780,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding); RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir); + IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags); return pressed; } From 6f73dbea8017cefbc6e26b894ec3274932ec0c76 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 1 Feb 2022 15:02:14 +0100 Subject: [PATCH 317/332] TestEngine: submit window data to item system. --- imgui.cpp | 15 +++++++++++++-- imgui.h | 2 +- imgui_internal.h | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 207329fb..06424553 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6306,6 +6306,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } + // [Test Engine] Register whole window in the item system +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (g.TestEngineHookItems) + { + IM_ASSERT(window->IDStack.Size == 1); + window->IDStack.Size = 0; + IMGUI_TEST_ENGINE_ITEM_ADD(window->Rect(), window->ID); + IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0); + window->IDStack.Size = 1; + } +#endif + // Handle manual resize: Resize Grips, Borders, Gamepad int border_held = -1; ImU32 resize_grip_col[4] = {}; @@ -6527,10 +6539,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // 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); -#ifdef IMGUI_ENABLE_TEST_ENGINE + // [Test Engine] Register title bar / tab if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID); -#endif } else { diff --git a/imgui.h b/imgui.h index 33c4c744..94fb4e35 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18615 +#define IMGUI_VERSION_NUM 18616 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_internal.h b/imgui_internal.h index 4fe0ed89..04948be7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2907,6 +2907,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #else +#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #endif From 43177324c081868116a99f634e2d5bf38b54056b Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 1 Feb 2022 15:27:10 +0100 Subject: [PATCH 318/332] Backends: GLFW: Fixed miss untranslation of keypad keys. (#4921, #452) --- backends/imgui_impl_glfw.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 7afb3169..3fd2819d 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -282,6 +282,8 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) // See https://github.com/glfw/glfw/issues/1502 for details. // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). // This won't cover edge cases but this is at least going to cover common cases. + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) + return key; const char* key_name = glfwGetKeyName(key, scancode); if (key_name && key_name[0] != 0 && key_name[1] == 0) { From c1ab3c406f3497f77c1a455557c331f0a2fa4bb5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 2 Feb 2022 23:10:14 +0100 Subject: [PATCH 319/332] Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5cf9a9af..3f324ea2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -231,6 +231,8 @@ Other Changes: Docking+Viewports Branch: +- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) +- Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). - Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ for the backend to have to ignore viewports with the _NoInputs flag when call io.AddMouseViewportEvent(). It is @@ -240,7 +242,6 @@ Docking+Viewports Branch: io.AddMouseViewportEvent() function, then Dear ImGui will revert to its flawed heuristic to find the viewport under. By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few drag and drop situations rather that relying on it everywhere. -- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) - Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) diff --git a/imgui.cpp b/imgui.cpp index dbea7ad9..29ac68f4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6383,7 +6383,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) IM_ASSERT(window->DockNode != NULL); // Docking currently override constraints - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; + if (window->DockIsActive) + g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Amend the Appearing flag if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing) From 8eb86893914b7865ac3594f7d4dbb121acd39e06 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 2 Feb 2022 23:23:13 +0100 Subject: [PATCH 320/332] Docking: Tabs use their own identifier (in order to make window->ID refer to whole window in test engine). Also prevents Tab ID from clashing with "" which was common. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 39 ++++++++++++++++++++++----------------- imgui_internal.h | 1 + imgui_widgets.cpp | 8 ++++---- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3f324ea2..971015de 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -232,6 +232,8 @@ Other Changes: Docking+Viewports Branch: - Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) +- Docking: Tabs use their own identifier instead of the Window identifier. + (This will invalidate some stored .ini data such as last selected tab, sorry!) - Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). - Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ diff --git a/imgui.cpp b/imgui.cpp index 29ac68f4..0aa1a82c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3264,6 +3264,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst ViewportAllowPlatformMonitorExtend = -1; ViewportPos = ImVec2(FLT_MAX, FLT_MAX); MoveId = GetID("#MOVE"); + TabId = GetID("#TAB"); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); AutoFitFramesX = AutoFitFramesY = -1; @@ -3541,8 +3542,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Test if another item is active (e.g. being dragged) if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) - return false; + if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap) + if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId) + return false; // Test if interactions on this window are blocked by an active popup or modal. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. @@ -3554,7 +3556,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin) + // will never be overwritten so we need to detect the case. if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) return false; } @@ -7299,7 +7302,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) // Select in dock node if (dock_node && dock_node->TabBar) - dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID; + dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId; // Bring to front BringWindowToFocusFront(focus_front_window); @@ -12456,6 +12459,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl buf->appendf("Collapsed=%d\n", settings->Collapsed); if (settings->DockId != 0) { + //buf->appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings->ID)); // window->TabId: this is not read back but writing it makes "debugging" the .ini data easier. if (settings->DockOrder == -1) buf->appendf("DockId=0x%08X\n", settings->DockId); else @@ -14063,7 +14067,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (payload_node && payload_node->IsLeafNode()) next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; if (payload_node == NULL) - next_selected_id = payload_window->ID; + next_selected_id = payload_window->TabId; } // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well @@ -14340,7 +14344,7 @@ int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) ImGuiTabBar* tab_bar = window->DockNode->TabBar; if (tab_bar == NULL) return -1; - ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID); + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId); return tab ? tab_bar->GetTabOrder(tab) : -1; } @@ -14444,7 +14448,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window node->WantHiddenTabBarUpdate = true; if (node->TabBar) { - TabBarRemoveTab(node->TabBar, window->ID); + TabBarRemoveTab(node->TabBar, window->TabId); const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2; if (node->Windows.Size < tab_count_threshold_for_tab_bar) DockNodeRemoveTabBar(node); @@ -14627,7 +14631,7 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); bool remove = false; remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); - remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame + remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame remove |= (window->DockTabWantClose); if (remove) { @@ -15159,7 +15163,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused || root_node->VisibleWindow == NULL) root_node->VisibleWindow = node->VisibleWindow; if (node->TabBar) - node->TabBar->VisibleTabId = node->VisibleWindow->ID; + node->TabBar->VisibleTabId = node->VisibleWindow->TabId; } return; } @@ -15210,7 +15214,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; - if (TabBarFindTabByID(tab_bar, window->ID) == NULL) + if (TabBarFindTabByID(tab_bar, window->TabId) == NULL) TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); } @@ -15255,7 +15259,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; else if (tab_bar->Tabs.Size > tabs_count_old) - tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId; // Begin tab bar ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons); @@ -15275,7 +15279,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; - if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) + if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) continue; if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) { @@ -15290,11 +15294,12 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]); + // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so) bool tab_open = true; TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); if (!tab_open) - node->WantCloseTabId = window->ID; - if (tab_bar->VisibleTabId == window->ID) + node->WantCloseTabId = window->TabId; + if (tab_bar->VisibleTabId == window->TabId) node->VisibleWindow = window; // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call @@ -15303,7 +15308,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Update navigation ID on menu layer if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) - host_window->NavLastIds[1] = window->ID; + host_window->NavLastIds[1] = window->TabId; } } @@ -15317,7 +15322,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w root_node->VisibleWindow = node->VisibleWindow; // Close button (after VisibleWindow was updated) - // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId + // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton; const bool close_button_is_visible = node->HasCloseButton; //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one) @@ -16931,7 +16936,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (node->TabBar && window->WasActive) window->DockOrder = (short)DockNodeGetTabOrder(window); - if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL) + if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL) *p_open = false; // Update ChildId to allow returning from Child to Parent with Escape diff --git a/imgui_internal.h b/imgui_internal.h index 2697ac85..b7c9437b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2231,6 +2231,7 @@ struct IMGUI_API ImGuiWindow float WindowBorderSize; // Window border size at the time of Begin(). int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") + ImGuiID TabId; // == window->GetID("#TAB") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImVec2 Scroll; ImVec2 ScrollMax; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d6ee5296..bb9c16b7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7584,7 +7584,7 @@ static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, I { IM_UNUSED(tab_bar); IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); - ImGuiID id = ImHashStr(label); + ImGuiID id = docked_window->TabId; KeepAliveID(id); return id; } @@ -7629,14 +7629,14 @@ ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBa void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window) { ImGuiContext& g = *GImGui; - IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL); + IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL); IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) if (!window->HasCloseButton) tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation. ImGuiTabItem new_tab; - new_tab.ID = window->ID; + new_tab.ID = window->TabId; new_tab.Flags = tab_flags; new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab if (new_tab.LastFrameVisible == -1) @@ -8209,7 +8209,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; // Render tab label, process close button - const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0; + const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; bool just_closed; bool text_clipped; TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); From 37958ca1b5ecabeba1636a3d2e80661bf011a31d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Feb 2022 00:19:31 +0100 Subject: [PATCH 321/332] Docking: Fixed CTRL+TAB back into a docked window not selecting menu layer when no item are on main layer. Could merge on master. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 971015de..1979d1da 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -235,6 +235,7 @@ Docking+Viewports Branch: - Docking: Tabs use their own identifier instead of the Window identifier. (This will invalidate some stored .ini data such as last selected tab, sorry!) - Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). +- Docking: Fixed CTRL+TAB back into a docked window not selecting menu layer when no item are on main layer. - Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ for the backend to have to ignore viewports with the _NoInputs flag when call io.AddMouseViewportEvent(). It is diff --git a/imgui.cpp b/imgui.cpp index 0aa1a82c..676d830e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6964,7 +6964,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; - window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); @@ -7125,6 +7124,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) skip_items = true; window->SkipItems = skip_items; + // Only clear NavLayersActiveMaskNext when marked as visible, so a CTRL+Tab back can use a safe value. + if (!window->SkipItems) + window->DC.NavLayersActiveMaskNext = 0x00; + // Sanity check: there are two spots which can set Appearing = true // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert. From 5017602752d87da9dbffa191fdb3b68c8bcac982 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Feb 2022 11:49:36 +0100 Subject: [PATCH 322/332] Backends: SDL: Amend 08350e5, multi-viewports mouse tracking works under Linux. (#4960) + Reword tests to help static analysis. --- backends/imgui_impl_sdl.cpp | 2 +- imgui.cpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 7255ccd5..48812a04 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -370,7 +370,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) -#ifdef _WIN32 +#ifndef __APPLE__ if (mouse_can_use_global_state) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;// We can call io.AddMouseViewportEvent() with correct data (optional) #endif diff --git a/imgui.cpp b/imgui.cpp index 676d830e..3e17a387 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6383,11 +6383,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) BeginDocked(window, p_open); flags = window->Flags; if (window->DockIsActive) + { IM_ASSERT(window->DockNode != NULL); - - // Docking currently override constraints - if (window->DockIsActive) - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; + g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints + } // Amend the Appearing flag if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing) From 31762a81734301e620f4f075a7ddaf41ab8f9de7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Feb 2022 12:04:11 +0100 Subject: [PATCH 323/332] Docking: fixed potential crash if a passthrough dock node is submitted without a child intermediate (currently not possible via API) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 3e17a387..614016bb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14979,7 +14979,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (central_node_hole && !hole_rect.IsInverted()) { SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min); - SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min); + if (host_window->ParentWindow) + SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min); } } From c39192ba6403d963d5983fa3e5973f5170cb0e31 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Sun, 30 Jan 2022 22:42:21 +0000 Subject: [PATCH 324/332] Backends: SDL_Renderer: Fix texture atlas format on big-endian hardware (#4927) SDL_PIXELFORMAT_RGBA32 is intended for byte arrays where the channels are specifically in the RGBA order. However, this is not what GetTexDataAsRGBA32 does: rather, it constructs an array where each pixel is an unsigned int in ABGR order. On little-endian platforms, this does indeed result in an RGBA byte order, however, on big-endian platforms, this results in an ABGR byte order, which does not match the PIXELFORMAT enum. What should be used is the SDL_PIXELFORMAT_ABGR8888 enum, which specifies that the pixels are in native-endian ABGR, which they are. --- backends/imgui_impl_sdlrenderer.cpp | 2 +- docs/CHANGELOG.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index ca4c432f..8d20b043 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -210,7 +210,7 @@ bool ImGui_ImplSDLRenderer_CreateFontsTexture() io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. // Upload texture to graphics system - bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); + bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); if (bd->FontTexture == NULL) { SDL_Log("error creating texture"); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 46781bca..bcc4b0a9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -160,6 +160,7 @@ Other Changes: - Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but can theorically be created by user code manipulating a ImDrawList. (#4857) - Backends: Vulkan: Added support for ImTextureID as VkDescriptorSet, add ImGui_ImplVulkan_AddTexture(). (#914) [@martty] +- Backends: SDL_Renderer: Fix texture atlas format on big-endian hardware (#4927) [@Clownacy] - Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and wgpuRenderPassEncoderSetVertexBuffer() calls. (#4891) [@FeepsDev] From c6cab1f352bbf80e51439be8311a84f7fc9acbbb Mon Sep 17 00:00:00 2001 From: Clownacy Date: Sat, 22 Jan 2022 13:55:03 +0000 Subject: [PATCH 325/332] Backends: SDL: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(). Use SDL_GetRendererOutputSize() instead of SDL_GL_GetDrawableSize() when bound to a SDL_Renderer. (#4927) This is (kind of) an OpenGL-only function, which should be avoided when SDL2 isn't using OpenGL. The only alternative that is recommended is SDL_GetRendererOutputSize, which limits this fix to the SDL_Renderer backend. Still, I think it's better than nothing. I say that SDL_GL_GetDrawableSize is "kind of" OpenGL-only because it does technically work even when SDL2 isn't using OpenGL. It's just that it becomes a shim to SDL_GetWindowSize, which is not suitable for high-DPI usage because it reflects the size of the window in screen coordinates, not actual pixels, so it really should be avoided when not using OpenGL. --- backends/imgui_impl_sdl.cpp | 34 +++++++++++++---------- backends/imgui_impl_sdl.h | 3 +- docs/CHANGELOG.txt | 2 ++ examples/example_sdl_sdlrenderer/main.cpp | 6 ++-- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 0b9f9c0a..9764cb2d 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-02-04: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(), so we can use SDL_GetRendererOutputSize() instead of SDL_GL_GetDrawableSize() when bound to a SDL_Renderer. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). @@ -78,12 +79,13 @@ // SDL Data struct ImGui_ImplSDL2_Data { - SDL_Window* Window; - Uint64 Time; - int MouseButtonsDown; - SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; - char* ClipboardTextData; - bool MouseCanUseGlobalState; + SDL_Window* Window; + SDL_Renderer* Renderer; + Uint64 Time; + int MouseButtonsDown; + SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; + char* ClipboardTextData; + bool MouseCanUseGlobalState; ImGui_ImplSDL2_Data() { memset(this, 0, sizeof(*this)); } }; @@ -299,7 +301,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) return false; } -static bool ImGui_ImplSDL2_Init(SDL_Window* window) +static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); @@ -323,6 +325,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) bd->Window = window; + bd->Renderer = renderer; bd->MouseCanUseGlobalState = mouse_can_use_global_state; io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; @@ -365,7 +368,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { IM_UNUSED(sdl_gl_context); // Viewport branch will need this. - return ImGui_ImplSDL2_Init(window); + return ImGui_ImplSDL2_Init(window, NULL); } bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window) @@ -373,7 +376,7 @@ bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window) #if !SDL_HAS_VULKAN IM_ASSERT(0 && "Unsupported"); #endif - return ImGui_ImplSDL2_Init(window); + return ImGui_ImplSDL2_Init(window, NULL); } bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window) @@ -381,17 +384,17 @@ bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window) #if !defined(_WIN32) IM_ASSERT(0 && "Unsupported"); #endif - return ImGui_ImplSDL2_Init(window); + return ImGui_ImplSDL2_Init(window, NULL); } bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window) { - return ImGui_ImplSDL2_Init(window); + return ImGui_ImplSDL2_Init(window, NULL); } -bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window) +bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer) { - return ImGui_ImplSDL2_Init(window); + return ImGui_ImplSDL2_Init(window, renderer); } void ImGui_ImplSDL2_Shutdown() @@ -520,7 +523,10 @@ void ImGui_ImplSDL2_NewFrame() SDL_GetWindowSize(bd->Window, &w, &h); if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED) w = h = 0; - SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h); + if (bd->Renderer != NULL) + SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h); + else + SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h index 3db454c5..dc18c2cc 100644 --- a/backends/imgui_impl_sdl.h +++ b/backends/imgui_impl_sdl.h @@ -19,13 +19,14 @@ #include "imgui.h" // IMGUI_IMPL_API struct SDL_Window; +struct SDL_Renderer; typedef union SDL_Event SDL_Event; IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window); -IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer); IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(); IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bcc4b0a9..edfd4667 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -86,6 +86,8 @@ Breaking Changes: - If calling ImGui_ImplGlfw_InitXXX with install_callbacks=true: nothing to do. is already done for you. - If calling ImGui_ImplGlfw_InitXXX with install_callbacks=false: you WILL NEED to register the GLFW callback using glfwSetCursorPosCallback() and forward it to the backend function ImGui_ImplGlfw_CursorPosCallback(). +- Backends: SDL: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(), so backend can call + SDL_GetRendererOutputSize() to obtain framebuffer size valid for hi-dpi. (#4927) [@Clownacy] - Commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen() - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x diff --git a/examples/example_sdl_sdlrenderer/main.cpp b/examples/example_sdl_sdlrenderer/main.cpp index aee4a785..582b5a15 100644 --- a/examples/example_sdl_sdlrenderer/main.cpp +++ b/examples/example_sdl_sdlrenderer/main.cpp @@ -30,7 +30,7 @@ int main(int, char**) } // Setup window - SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+SDL_Renderer example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); // Setup SDL_Renderer instance @@ -56,7 +56,7 @@ int main(int, char**) //ImGui::StyleColorsClassic(); // Setup Platform/Renderer backends - ImGui_ImplSDL2_InitForSDLRenderer(window); + ImGui_ImplSDL2_InitForSDLRenderer(window, renderer); ImGui_ImplSDLRenderer_Init(renderer); // Load Fonts @@ -100,7 +100,7 @@ int main(int, char**) // Start the Dear ImGui frame ImGui_ImplSDLRenderer_NewFrame(); - ImGui_ImplSDL2_NewFrame(window); + ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). From 2554b512c08b83d88d60137262bada6ad3bc4235 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 7 Feb 2022 11:55:24 +0100 Subject: [PATCH 326/332] Backends: OSX: Forward keyDown/keyUp events to OS when unused by Dear ImGui. --- backends/imgui_impl_osx.mm | 7 +++++-- docs/CHANGELOG.txt | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index d061ff6b..205d2dee 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-02-07: Inputs: Forward keyDown/keyUp events to OS when unused by dear imgui. // 2022-01-31: Fix building with old Xcode versions that are missing gamepad features. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. @@ -131,7 +132,8 @@ static double GetMachAbsoluteTimeInSeconds() - (void)keyDown:(NSEvent*)event { - ImGui_ImplOSX_HandleEvent(event, self); + if (!ImGui_ImplOSX_HandleEvent(event, self)) + [super keyDown:event]; // Call to the macOS input manager system. [self interpretKeyEvents:@[event]]; @@ -139,7 +141,8 @@ static double GetMachAbsoluteTimeInSeconds() - (void)keyUp:(NSEvent*)event { - ImGui_ImplOSX_HandleEvent(event, self); + if (!ImGui_ImplOSX_HandleEvent(event, self)) + [super keyUp:event]; } - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index edfd4667..bbc5e85d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -153,6 +153,7 @@ Other Changes: - Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] - Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) - Backends: OSX: Fix building with old Xcode versions that are missing gamepad features. [@rokups] +- Backends: OSX: Forward keyDown/keyUp events to OS when unused by Dear ImGui. - Backends: Android, GLUT: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Android, GLUT: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] From 40fd163a3b760c0fa2201db2e8f2b52966d0c361 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 2 Feb 2022 10:38:37 +0200 Subject: [PATCH 327/332] Backends: OpenGL3: Fix OpenGL ES2 includes on Apple systems. # Conflicts: # backends/imgui_impl_opengl3.cpp # docs/CHANGELOG.txt --- backends/imgui_impl_opengl3.cpp | 6 +++++- docs/CHANGELOG.txt | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 32778c1d..0d3489ce 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -108,7 +108,11 @@ // GL includes #if defined(IMGUI_IMPL_OPENGL_ES2) -#include +#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) +#include // Use GL ES 2 +#else +#include // Use GL ES 2 +#endif #if defined(__EMSCRIPTEN__) #ifndef GL_GLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bbc5e85d..f1de15c2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -158,6 +158,7 @@ Other Changes: - Backends: Android, GLUT: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk] It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers. +- Backends: OpenGL3: Fix OpenGL ES2 includes on Apple systems. [@rokups] - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. - Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but From 732bc2058edc847059aaac73001ddd5c85b793d7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 7 Feb 2022 15:59:28 +0100 Subject: [PATCH 328/332] Backends: GLFW: Handle spurious leave/enter sequences apparently submitted by X11/GLFW combo. (#4984) --- backends/imgui_impl_glfw.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 3fd2819d..57012eba 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -91,6 +91,7 @@ struct ImGui_ImplGlfw_Data double Time; GLFWwindow* MouseWindow; GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + ImVec2 LastMousePos; bool InstalledCallbacks; // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. @@ -338,8 +339,11 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) ImGuiIO& io = ImGui::GetIO(); io.AddMousePosEvent((float)x, (float)y); + bd->LastMousePos = ImVec2((float)x, (float)y); } +// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, +// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); @@ -348,9 +352,13 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) ImGuiIO& io = ImGui::GetIO(); if (entered) + { bd->MouseWindow = window; - if (!entered && bd->MouseWindow == window) + io.AddMousePosEvent(bd->LastMousePos.x, bd->LastMousePos.y); + } + else if (!entered && bd->MouseWindow == window) { + bd->LastMousePos = io.MousePos; bd->MouseWindow = NULL; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } From e52bb147a71d69fe93204566b9a46fc9cbc80f5f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 7 Feb 2022 16:00:13 +0100 Subject: [PATCH 329/332] Backends: GLFW: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks(). (#4981) --- backends/imgui_impl_glfw.cpp | 92 +++++++++++++++++++++--------------- backends/imgui_impl_glfw.h | 10 ++-- docs/CHANGELOG.txt | 2 + 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 57012eba..516aa3c2 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after iniitializing backend. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. // 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+). @@ -68,13 +69,13 @@ #define GLFW_EXPOSE_NATIVE_WIN32 #include // for glfwGetWin32Window #endif -#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? -#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR +#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? +#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else -#define GLFW_HAS_NEW_CURSORS (0) +#define GLFW_HAS_NEW_CURSORS (0) #endif -#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api -#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName() +#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api +#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName() // GLFW data enum GlfwClientApi @@ -91,7 +92,7 @@ struct ImGui_ImplGlfw_Data double Time; GLFWwindow* MouseWindow; GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; - ImVec2 LastMousePos; + ImVec2 LastValidMousePos; bool InstalledCallbacks; // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. @@ -339,7 +340,7 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) ImGuiIO& io = ImGui::GetIO(); io.AddMousePosEvent((float)x, (float)y); - bd->LastMousePos = ImVec2((float)x, (float)y); + bd->LastValidMousePos = ImVec2((float)x, (float)y); } // Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, @@ -354,11 +355,11 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) if (entered) { bd->MouseWindow = window; - io.AddMousePosEvent(bd->LastMousePos.x, bd->LastMousePos.y); + io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y); } else if (!entered && bd->MouseWindow == window) { - bd->LastMousePos = io.MousePos; + bd->LastValidMousePos = io.MousePos; bd->MouseWindow = NULL; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } @@ -379,6 +380,48 @@ void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. } +void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); + IM_ASSERT(bd->Window == window); + + bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); + bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); + bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); + bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + bd->InstalledCallbacks = true; +} + +void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); + IM_ASSERT(bd->Window == window); + + glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus); + glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter); + glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos); + glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton); + glfwSetScrollCallback(window, bd->PrevUserCallbackScroll); + glfwSetKeyCallback(window, bd->PrevUserCallbackKey); + glfwSetCharCallback(window, bd->PrevUserCallbackChar); + glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); + bd->InstalledCallbacks = false; + bd->PrevUserCallbackWindowFocus = NULL; + bd->PrevUserCallbackCursorEnter = NULL; + bd->PrevUserCallbackCursorPos = NULL; + bd->PrevUserCallbackMousebutton = NULL; + bd->PrevUserCallbackScroll = NULL; + bd->PrevUserCallbackKey = NULL; + bd->PrevUserCallbackChar = NULL; + bd->PrevUserCallbackMonitor = NULL; +} + static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { ImGuiIO& io = ImGui::GetIO(); @@ -427,25 +470,8 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw glfwSetErrorCallback(prev_error_callback); // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. - bd->PrevUserCallbackWindowFocus = NULL; - bd->PrevUserCallbackCursorEnter = NULL; - bd->PrevUserCallbackMousebutton = NULL; - bd->PrevUserCallbackScroll = NULL; - bd->PrevUserCallbackKey = NULL; - bd->PrevUserCallbackChar = NULL; - bd->PrevUserCallbackMonitor = NULL; if (install_callbacks) - { - bd->InstalledCallbacks = true; - bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback); - bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback); - bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); - bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); - bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); - } + ImGui_ImplGlfw_InstallCallbacks(window); bd->ClientApi = client_api; return true; @@ -473,16 +499,7 @@ void ImGui_ImplGlfw_Shutdown() ImGuiIO& io = ImGui::GetIO(); if (bd->InstalledCallbacks) - { - glfwSetWindowFocusCallback(bd->Window, bd->PrevUserCallbackWindowFocus); - glfwSetCursorEnterCallback(bd->Window, bd->PrevUserCallbackCursorEnter); - glfwSetCursorPosCallback(bd->Window, bd->PrevUserCallbackCursorPos); - glfwSetMouseButtonCallback(bd->Window, bd->PrevUserCallbackMousebutton); - glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll); - glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey); - glfwSetCharCallback(bd->Window, bd->PrevUserCallbackChar); - glfwSetMonitorCallback(bd->PrevUserCallbackMonitor); - } + ImGui_ImplGlfw_RestoreCallbacks(bd->Window); for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) glfwDestroyCursor(bd->MouseCursors[cursor_n]); @@ -514,6 +531,7 @@ static void ImGui_ImplGlfw_UpdateMouseData() double mouse_x, mouse_y; glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y); io.AddMousePosEvent((float)mouse_x, (float)mouse_y); + bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); } } } diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 46585c0a..58712dee 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -29,9 +29,13 @@ 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(); -// GLFW callbacks -// - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will 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 those function yourself from your own GLFW callbacks. +// GLFW callbacks (installer) +// - 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. +IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); +IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); + +// GLFW callbacks (individual callbacks to call if you didn't install callbacks) IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f1de15c2..f21afcb5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -134,6 +134,8 @@ Other Changes: - Backends: GLFW: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) - Backends: GLFW: Retrieve mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. - Backends: GLFW: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) +- Backends: GLFW: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing + callbacks after iniitializing backend. (#4981) - Backends: Win32: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: Win32: Retrieve mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) From c71a50deb5ddf1ea386b91e60fa2e4a26d080074 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 7 Feb 2022 17:41:24 +0100 Subject: [PATCH 330/332] Version 1.87 --- docs/CHANGELOG.txt | 46 ++++++++++++++++++++++++++-------------------- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 35 insertions(+), 29 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f21afcb5..3b7fe232 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -32,14 +32,16 @@ HOW TO UPDATE? ----------------------------------------------------------------------- - VERSION 1.87 WIP (In Progress) + VERSION 1.87 (Released 2022-02-07) ----------------------------------------------------------------------- +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.87 + Breaking Changes: - Removed support for pre-C++11 compilers. We'll stop supporting VS2010. (#4537) - Reworked IO mouse input API: (#4921, #4858) [@thedmd, @ocornut] - - Added io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions, + - Added io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() functions, obsoleting writing directly to io.MousePos, io.MouseDown[], io.MouseWheel, etc. - This enable input queue trickling to support low framerates. (#2787, #1992, #3383, #2525, #1320) - Reworked IO keyboard input API: (#4921, #2625, #3724) [@thedmd, @ocornut] @@ -70,10 +72,9 @@ Breaking Changes: so ImGuiKey values can still express a legacy native keycode, and new named keys are all >= 512. - This will enable a few things in the future: - Access to portable keys allows for backend-agnostic keyboard input code. Until now it was difficult - to share code using keyboard accross project because of this gap. (#2625, #3724) + to share code using keyboard across project because of this gap. (#2625, #3724) - Access to full key ranges will allow us to develop a proper keyboard shortcut system. (#456) - - io.AddKeyEvent() will later be turned into a trickling IO queue (for all inputs) to handle very low framerate better. (#2525, #2787, #3383) - - io.SetKeyEventNativeData() include native keycode/scancode which will later be exposed. (#3141, #2959) + - io.SetKeyEventNativeData() include native keycode/scancode which may later be exposed. (#3141, #2959) - Reworked IO nav/gamepad input API and unifying inputs sources: (#4921, #4858, #787) - Added full range of ImGuiKey_GamepadXXXX enums (e.g. ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadR2) to use with io.AddKeyEvent(), io.AddKeyAnalogEvent(). @@ -110,7 +111,8 @@ Other Changes: handled by core automatically for all kind of inputs. (#4858, #2787, #1992, #3383, #2525, #1320) - New IO functions for keyboard/gamepad: AddKeyEvent(), AddKeyAnalogEvent(). - New IO functions for mouse: AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent(). -- Fixed CTRL+Tab into an empty window causing artefacts on the highlight rectangle due to bad reordering on ImDrawCmd. +- IO: Unified key enums allow using key functions on key mods and gamepad values. +- Fixed CTRL+Tab into an empty window causing artifacts on the highlight rectangle due to bad reordering on ImDrawCmd. - Fixed a situation where CTRL+Tab or Modal can occasionally lead to the creation of ImDrawCmd with zero triangles, which would makes the draw operation of some backends assert (e.g. Metal with debugging). (#4857) - Popups: Fixed a regression crash when a new window is created after a modal on the same frame. (#4920) [@rokups] @@ -128,30 +130,30 @@ Other Changes: - Platform IME: add ImGuiPlatformImeData::InputLineHeight. (#3113) [@liuliu] - Platform IME: [windows] call ImmSetCandidateWindow() to position candidate window. - Backends: GLFW: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. - We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of every + We are now converting GLFW untranslated keycodes back to translated keycodes in order to match the behavior of other backend, and facilitate the use of GLFW with lettered-shortcuts API. (#456, #2625) -- Backends: GLFW: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) -- Backends: GLFW: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) +- Backends: GLFW: Submit keys and key mods using io.AddKeyEvent(). (#2625, #4921) +- Backends: GLFW: Submit mouse data using io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() functions. (#4921) - Backends: GLFW: Retrieve mouse position using glfwSetCursorPosCallback() + fallback when focused but not hovered/captured. -- Backends: GLFW: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) +- Backends: GLFW: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4921) - Backends: GLFW: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after iniitializing backend. (#4981) -- Backends: Win32: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) +- Backends: Win32: Submit keys and key mods using io.AddKeyEvent(). (#2625, #4921) - Backends: Win32: Retrieve mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. -- Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) +- Backends: Win32: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4921) - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. -- Backends: Win32: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) -- Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. +- Backends: Win32: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4921) +- Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. (#456, #2625) - Backends: SDL: Submit key data using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) - Backends: SDL: Retrieve mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. -- Backends: SDL: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) +- Backends: SDL: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4921) - Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. -- Backends: SDL: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) +- Backends: SDL: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4921) - Backends: Allegro5: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) -- Backends: Allegro5: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) +- Backends: Allegro5: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4921) - Backends: OSX: Submit keys using io.AddKeyEvent(). Submit keymods using io.AddKeyModsEvent() at the same time. (#2625) -- Backends: OSX: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4858) -- Backends: OSX: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4858) +- Backends: OSX: Submit mouse data using io.AddMousePosEvent(), AddMouseButtonEvent(), AddMouseWheelEvent() functions. (#4921) +- Backends: OSX: Submit gamepad data using io.AddKeyEvent/AddKeyAnalogEvent() functions, stopped writing to io.NavInputs[]. (#4921) - Backends: OSX: Added basic Platform IME support. (#3108, #2598) [@liuliu] - Backends: OSX: Fix Game Controller nav mapping to use shoulder for both focusing and tweak speed. (#4759) - Backends: OSX: Fix building with old Xcode versions that are missing gamepad features. [@rokups] @@ -164,7 +166,7 @@ Other Changes: - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. - Backends: Metal: Ignore ImDrawCmd where ElemCount == 0, which are normally not emitted by the library but - can theorically be created by user code manipulating a ImDrawList. (#4857) + can theoretically be created by user code manipulating a ImDrawList. (#4857) - Backends: Vulkan: Added support for ImTextureID as VkDescriptorSet, add ImGui_ImplVulkan_AddTexture(). (#914) [@martty] - Backends: SDL_Renderer: Fix texture atlas format on big-endian hardware (#4927) [@Clownacy] - Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and @@ -175,6 +177,8 @@ Other Changes: VERSION 1.86 (Released 2021-12-22) ----------------------------------------------------------------------- +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.86 + Breaking Changes: - Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. @@ -271,6 +275,8 @@ Other Changes: VERSION 1.85 (Released 2021-10-12) ----------------------------------------------------------------------- +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.85 + This is the last release officially supporting C++03 and Visual Studio 2008/2010. (#4537) We expect that the next release will require a subset of the C++11 language (VS 2012~, GCC 4.8.1, Clang 3.3). We may use some C++11 language features but we will not use any C++ library headers. diff --git a/imgui.cpp b/imgui.cpp index 06424553..efa69604 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.87 WIP +// dear imgui, v1.87 // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 94fb4e35..871b75f0 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.87 WIP +// dear imgui, v1.87 // (headers) // Help: @@ -64,8 +64,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18616 +#define IMGUI_VERSION "1.87" +#define IMGUI_VERSION_NUM 18700 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b59eb4d3..72d98caa 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.87 WIP +// dear imgui, v1.87 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 73cd6bf5..a99e6b24 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.87 WIP +// dear imgui, v1.87 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 04948be7..baa4a4de 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.87 WIP +// dear imgui, v1.87 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 287c2d47..27f1d538 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.87 WIP +// dear imgui, v1.87 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2a335288..56723cdd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.87 WIP +// dear imgui, v1.87 // (widgets code) /* From aa8680009248061c83f3d6722ec53c1a320d872b Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 8 Feb 2022 12:22:46 +0100 Subject: [PATCH 331/332] Internals: rework RenderMouseCursor() signature so we can use it in docking branch more naturally. (Merged from master+ rework for docking) # Conflicts: # imgui.cpp # imgui_draw.cpp --- imgui.cpp | 43 +++++++++++++++++++++++++++++++------------ imgui.h | 2 +- imgui_draw.cpp | 22 ---------------------- imgui_internal.h | 2 +- 4 files changed, 33 insertions(+), 36 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b5fa1bf2..5bc81335 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3249,6 +3249,34 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl } } +void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) +{ + 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++) + { + // 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 * viewport->DpiScale; + if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale))) + continue; + ImDrawList* draw_list = GetForegroundDrawList(viewport); + ImTextureID tex_id = font_atlas->TexID; + draw_list->PushTextureID(tex_id); + draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border); + draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill); + draw_list->PopTextureID(); + } +} + + //----------------------------------------------------------------------------- // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) //----------------------------------------------------------------------------- @@ -5129,9 +5157,9 @@ void ImGui::Render() if (first_render_of_frame) RenderDimmedBackgrounds(); - ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; - if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) - g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); + // Draw software mouse cursor if requested by io.MouseDrawCursor flag + if (g.IO.MouseDrawCursor && first_render_of_frame && 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; @@ -5140,15 +5168,6 @@ void ImGui::Render() ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); - // Draw software mouse cursor if requested by io.MouseDrawCursor flag - // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor) - if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f && first_render_of_frame) - { - float scale = g.Style.MouseCursorScale * viewport->DpiScale; - if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) - RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); - } - // Add foreground ImDrawList (for each active viewport) if (viewport->DrawLists[1] != NULL) AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); diff --git a/imgui.h b/imgui.h index 079fb565..32383207 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87" -#define IMGUI_VERSION_NUM 18700 +#define IMGUI_VERSION_NUM 18701 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch diff --git a/imgui_draw.cpp b/imgui_draw.cpp index bb01786c..40d5f2d7 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3744,7 +3744,6 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderArrow() // - RenderBullet() // - RenderCheckMark() -// - RenderMouseCursor() // - RenderArrowDockMenu() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() @@ -3806,27 +3805,6 @@ void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float draw_list->PathStroke(col, 0, thickness); } -void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) -{ - if (mouse_cursor == ImGuiMouseCursor_None) - return; - IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT); - - ImFontAtlas* font_atlas = draw_list->_Data->Font->ContainerAtlas; - ImVec2 offset, size, uv[4]; - if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) - { - pos -= offset; - ImTextureID tex_id = font_atlas->TexID; - draw_list->PushTextureID(tex_id); - draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill); - draw_list->PopTextureID(); - } -} - // Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) { diff --git a/imgui_internal.h b/imgui_internal.h index b24e190e..b8212198 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3051,12 +3051,12 @@ namespace ImGui 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 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); // Render helpers (those functions don't access any ImGui state!) IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); - IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); From 1e14cc5cae703b7cdf0e33c50165d6dd289fcb1f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 10 Feb 2022 18:50:28 +0100 Subject: [PATCH 332/332] Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) --- docs/CHANGELOG.txt | 10 ++++++++++ imgui.cpp | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f15ba372..63632159 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -98,6 +98,16 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.88 WIP (In Progress) +----------------------------------------------------------------------- + +Docking+Viewports Branch: + +- Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize + when multi-viewports are disabled. (#4900) + + ----------------------------------------------------------------------- VERSION 1.87 (Released 2022-02-07) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 5bc81335..44dc0c83 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12903,9 +12903,10 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { - if (!viewport->PlatformRequestMove) + // Always update for main viewport as we are already pulling correct platform pos/size (see #4900) + if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) viewport->Pos = pos; - if (!viewport->PlatformRequestResize) + if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) viewport->Size = size; viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags }