From 16ddc1698dc2d36b8054568477498cf32ae4117c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 24 Mar 2022 10:57:28 +0100 Subject: [PATCH 01/13] Fixed creating multiple-context (regression in 28ba54a). (#5135) --- imgui.cpp | 30 ++++++++++++++---------------- imgui.h | 2 +- imgui_internal.h | 4 ++-- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0949f430..7ceb9e13 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3697,20 +3697,23 @@ void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeF ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) { + ImGuiContext* prev_ctx = GetCurrentContext(); ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); - if (GImGui == NULL) - SetCurrentContext(ctx); - Initialize(ctx); + SetCurrentContext(ctx); + Initialize(); + if (prev_ctx != NULL) + SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one. return ctx; } void ImGui::DestroyContext(ImGuiContext* ctx) { + ImGuiContext* prev_ctx = GetCurrentContext(); if (ctx == NULL) - ctx = GImGui; - Shutdown(ctx); - if (GImGui == ctx) - SetCurrentContext(NULL); + ctx = prev_ctx; + SetCurrentContext(ctx); + Shutdown(); + SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL); IM_DELETE(ctx); } @@ -4464,9 +4467,9 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -void ImGui::Initialize(ImGuiContext* context) +void ImGui::Initialize() { - ImGuiContext& g = *context; + ImGuiContext& g = *GImGui; IM_ASSERT(!g.Initialized && !g.SettingsLoaded); // Add .ini handle for ImGuiWindow type @@ -4496,10 +4499,10 @@ void ImGui::Initialize(ImGuiContext* context) } // This function is merely here to free heap allocations. -void ImGui::Shutdown(ImGuiContext* context) +void ImGui::Shutdown() { // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) - ImGuiContext& g = *context; + ImGuiContext& g = *GImGui; if (g.IO.Fonts && g.FontAtlasOwnedByContext) { g.IO.Fonts->Locked = false; @@ -4513,12 +4516,7 @@ void ImGui::Shutdown(ImGuiContext* context) // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) if (g.SettingsLoaded && g.IO.IniFilename != NULL) - { - ImGuiContext* backup_context = GImGui; - SetCurrentContext(&g); SaveIniSettingsToDisk(g.IO.IniFilename); - SetCurrentContext(backup_context); - } CallContextHooks(&g, ImGuiContextHookType_Shutdown); diff --git a/imgui.h b/imgui.h index f92c70dc..6223fbcb 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.88 WIP" -#define IMGUI_VERSION_NUM 18710 +#define IMGUI_VERSION_NUM 18711 #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 8cf412ba..b648bbc5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2519,8 +2519,8 @@ namespace ImGui IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. // Init - IMGUI_API void Initialize(ImGuiContext* context); - 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(). + IMGUI_API void Initialize(); + IMGUI_API void Shutdown(); // 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); From dfbe938e54858b8b443ca2981d037cc2f35cc931 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 24 Mar 2022 12:04:49 +0100 Subject: [PATCH 02/13] InputText: Fixed pressing Tab emitting two tabs characters. (#2467, #1336) Using Tab keys entirely now, ignoring Tab character. Technically affect who owns the repeat rate. --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 19 +++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9fb774ff..9bb43451 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,8 @@ Other Changes: clipper instance. High-level languages (Lua,Rust etc.) would typically be affected. (#4822) - IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the return value is overriden by focus when gamepad/keyboard navigation is active. +- InputText: Fixed pressing Tab emitting two tabs characters because of dual Keys/Chars events being + trickled with the new input queue (happened on some backends only). (#2467, #1336) - Tables: Fixed incorrect border height used for logic when resizing one of several synchronized instance of a same table ID, when instances have a different height. (#3955). - Inputs: Fixed IsMouseClicked() repeat mode rate being half of keyboard repeat rate. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 407ae51c..5b5013c9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3815,7 +3815,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f if (c < 0x20) { bool pass = false; - pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); + pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); if (!pass) return false; @@ -4201,16 +4201,15 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (state->SelectedAllMouseLock && !io.MouseDown[0]) state->SelectedAllMouseLock = false; - // 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. + // We except backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) + // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); 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 - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) - state->OnKeyPressed((int)c); - } + { + unsigned int c = '\t'; // Insert TAB + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) + state->OnKeyPressed((int)c); + } // Process regular text input (before we check for Return because using some IME will effectively send a Return?) // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. @@ -4221,7 +4220,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Insert character if they pass filtering unsigned int c = (unsigned int)io.InputQueueCharacters[n]; - if (c == '\t' && io.KeyShift) + if (c == '\t') // Skip Tab, see above. continue; if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); From 3587ee492bbee8144dbfbf5f660d4a7fd6316638 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 25 Mar 2022 12:36:52 +0100 Subject: [PATCH 03/13] Viewports: store Viewport field in ImGuiWindow to facilitate using code accross branches + fix PVS warnings. --- imgui.cpp | 12 ++++++++++-- imgui.h | 2 +- imgui_internal.h | 4 ++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7ceb9e13..1a05f963 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3709,7 +3709,7 @@ ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) void ImGui::DestroyContext(ImGuiContext* ctx) { ImGuiContext* prev_ctx = GetCurrentContext(); - if (ctx == NULL) + if (ctx == NULL) //-V1051 ctx = prev_ctx; SetCurrentContext(ctx); Shutdown(); @@ -6179,6 +6179,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // SELECT VIEWPORT // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) + + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); + SetWindowViewport(window, viewport); SetCurrentWindow(window); // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) @@ -6292,7 +6295,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. - ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); ImRect viewport_rect(viewport->GetMainRect()); ImRect viewport_work_rect(viewport->GetWorkRect()); ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); @@ -11846,6 +11848,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- // - GetMainViewport() +// - SetWindowViewport() [Internal] // - UpdateViewportsNewFrame() [Internal] // (this section is more complete in the 'docking' branch) //----------------------------------------------------------------------------- @@ -11856,6 +11859,11 @@ ImGuiViewport* ImGui::GetMainViewport() return g.Viewports[0]; } +void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +{ + window->Viewport = viewport; +} + // Update viewports and monitor infos static void ImGui::UpdateViewportsNewFrame() { diff --git a/imgui.h b/imgui.h index 6223fbcb..f61158ca 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.88 WIP" -#define IMGUI_VERSION_NUM 18711 +#define IMGUI_VERSION_NUM 18712 #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 b648bbc5..a7adbcd5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2017,6 +2017,7 @@ struct IMGUI_API ImGuiWindow char* Name; // Window name, owned by the window. ImGuiID ID; // == ImHashStr(Name) ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded. ImVec2 Pos; // Position (always rounded-up to nearest pixel) ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed @@ -2534,6 +2535,9 @@ namespace ImGui IMGUI_API void RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove); IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); + // Viewports + IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); + // Settings IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); From 1ba2905017ea81545b2fd1d45fee093a4982d9b1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 25 Mar 2022 15:40:29 +0100 Subject: [PATCH 04/13] Update README, quote, mission statement --- docs/README.md | 38 ++++++++++++++++++++++++-------------- imgui.cpp | 1 + 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/docs/README.md b/docs/README.md index 54ca5c78..5fddc7c9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,18 +1,25 @@ Dear ImGui ===== -[![Build Status](https://github.com/ocornut/imgui/workflows/build/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=build) [![Static Analysis Status](https://github.com/ocornut/imgui/workflows/static-analysis/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=static-analysis) +
"Give someone state and they'll have a bug one day, but teach them how to represent state in two separate locations that have to be kept in sync and they'll have bugs for a lifetime."
-ryg + +---- + +[![Build Status](https://github.com/ocornut/imgui/workflows/build/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=build) [![Static Analysis Status](https://github.com/ocornut/imgui/workflows/static-analysis/badge.svg)](https://github.com/ocornut/imgui/actions?workflow=static-analysis) (This library is available under a free and permissive license, but needs financial support to sustain its continued improvements. In addition to maintenance and stability there are many desirable features yet to be added. If your company is using Dear ImGui, please consider reaching out.) Businesses: support continued development and maintenance via invoiced technical support, maintenance, sponsoring contracts:
  _E-mail: contact @ dearimgui dot com_ -Individuals: support continued development and maintenance [here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WGHNC6MBFLZ2S). +Individuals: support continued development and maintenance [here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WGHNC6MBFLZ2S). Also see [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors) page. -Also see [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors) page. +| [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Integration](#integration) | +:----------------------------------------------------------: | +| [Upcoming changes](#upcoming-changes) - [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) - [How to help](#how-to-help) - [Sponsors](#sponsors) - [Credits](#credits) - [License](#license) | +| [Wiki](https://github.com/ocornut/imgui/wiki) - [Languages & frameworks backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | ----- +### The Pitch Dear ImGui is a **bloat-free graphical user interface library for C++**. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline enabled application. It is fast, portable, renderer agnostic and self-contained (no external dependencies). @@ -20,10 +27,16 @@ Dear ImGui is designed to **enable fast iterations** and to **empower programmer Dear ImGui is particularly suited to integration in games engine (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any applications on consoles platforms where operating system features are non-standard. -| [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Integration](#integration) | -:----------------------------------------------------------: | -| [Upcoming changes](#upcoming-changes) - [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) - [How to help](#how-to-help) - [Sponsors](#sponsors) - [Credits](#credits) - [License](#license) | -| [Wiki](https://github.com/ocornut/imgui/wiki) - [Languages & frameworks backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | + + - Easy to use to create code-driven and data-driven tools. + - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools. + - Easy to hack and improve. + - Minimize setup and maintenance. + - Minimize state storage on user side. + - Minimize state synchronization. + - Portable, minimize dependencies, run on target (consoles, phones, etc.). + - Efficient runtime and memory consumption. + - Battle-tested, used by many major actors in the game industry. ### Usage @@ -31,9 +44,7 @@ Dear ImGui is particularly suited to integration in games engine (for tooling), **No specific build process is required**. You can add the .cpp files to your existing project. -You will need a backend to integrate Dear ImGui in your app. The backend passes mouse/keyboard/gamepad inputs and variety of settings to Dear ImGui, and is in charge of rendering the resulting vertices. - -**Backends for a variety of graphics api and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. See the [Integration](#integration) section of this document for details. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. +You will need a backend to integrate Dear ImGui in your app. The backend passes mouse/keyboard/gamepad inputs and variety of settings to Dear ImGui, and is in charge of rendering the resulting vertices. **Backends for a variety of graphics api and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. See the [Integration](#integration) section of this document for details. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. After Dear ImGui is setup in your application, you can use it from \_anywhere\_ in your program loop: @@ -47,7 +58,6 @@ ImGui::SliderFloat("float", &f, 0.0f, 1.0f); ``` Result:
![sample code output (dark)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v175/capture_readme_styles_0001.png) ![sample code output (light)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v175/capture_readme_styles_0002.png) -
_(settings: Dark style (left), Light style (right) / Font: Roboto-Medium, 16px)_ Code: ```cpp @@ -145,10 +155,10 @@ For more user-submitted screenshots of projects using Dear ImGui, check out the For a list of third-party widgets and extensions, check out the [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page. -Custom engine +Custom engine for [Wonder Boy: The Dragon's Trap](http://www.TheDragonsTrap.com) (2017) [![screenshot game](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v149/gallery_TheDragonsTrap-01-thumb.jpg)](https://cloud.githubusercontent.com/assets/8225057/20628927/33e14cac-b329-11e6-80f6-9524e93b048a.png) -Custom engine +Custom engine (untitled) [![screenshot tool](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v160/editor_white_preview.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v160/editor_white.png) [Tracy Profiler](https://github.com/wolfpld/tracy) diff --git a/imgui.cpp b/imgui.cpp index 1a05f963..91bd1f8b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -101,6 +101,7 @@ CODE - Easy to hack and improve. - Minimize setup and maintenance. - Minimize state storage on user side. + - Minimize state synchronization. - Portable, minimize dependencies, run on target (consoles, phones, etc.). - Efficient runtime and memory consumption. From 2d76b09d2e054831ddb353e3fb620f47b8fa07aa Mon Sep 17 00:00:00 2001 From: whale02 <34067397+whale02@users.noreply.github.com> Date: Mon, 28 Mar 2022 09:58:27 +0100 Subject: [PATCH 05/13] Backends: Metal: Conditional check, amend 7602277 (#5122, #5123) Co-authored-by: Wvader <34067397+wvader@users.noreply.github.com> --- backends/imgui_impl_metal.mm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index d2eb4651..6b3680cd 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -582,8 +582,11 @@ void ImGui_ImplMetal_DestroyDeviceObjects() vertexBufferOffset += (size_t)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert); indexBufferOffset += (size_t)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx); } - - id weakSelf = self; +#if __has_feature(objc_arc) + __weak id weakSelf = self; +#else + __unsafe_unretained id weakSelf = self; +#endif [commandBuffer addCompletedHandler:^(id) { dispatch_async(dispatch_get_main_queue(), ^{ From 0dec4307072758a7f65cdb03ce2e22b29bb62359 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 28 Mar 2022 11:14:07 +0200 Subject: [PATCH 06/13] Menus: Adjusted BeginMenu() closing logic so hovering void or non-MenuItem() in parent window always lead to menu closure. --- docs/CHANGELOG.txt | 3 +++ imgui.h | 2 +- imgui_widgets.cpp | 14 +++++++++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9bb43451..5a849df0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -61,6 +61,9 @@ Other Changes: instance of a same table ID, when instances have a different height. (#3955). - Inputs: Fixed IsMouseClicked() repeat mode rate being half of keyboard repeat rate. - ColorEdit: Fixed text baseline alignment after a SameLine() after a ColorEdit() with visible label. +- Menus: Adjusted BeginMenu() closing logic so hovering void or non-MenuItem() in parent window + always lead to menu closure. Fixes using items that are not MenuItem() or BeginItem() at the root + level of a popup with a child menu opened. - Stack Tool: Added option to copy item path to clipboard. (#4631) - Drawlist: Fixed PathArcTo() emitting terminating vertices too close to arc vertices. (#4993) [@thedmd] - DrawList: Fixed texture-based anti-aliasing path with RGBA textures (#5132, #3245) [@cfillion] diff --git a/imgui.h b/imgui.h index f61158ca..1c5412fc 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.88 WIP" -#define IMGUI_VERSION_NUM 18712 +#define IMGUI_VERSION_NUM 18713 #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 5b5013c9..f4080145 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6935,7 +6935,7 @@ 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; + bool moving_toward_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)) { @@ -6946,18 +6946,22 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) 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, 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? + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle has maximum height to limit the slope and the bias toward large sub-menus tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); - moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); + moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } - if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) + + // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not) + // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon. + // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.) + if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu) want_close = true; // 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 + else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open want_open = true; if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { From 2747a8ca4075d566218124024cbdd274471f4cea Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Mar 2022 11:46:30 +0200 Subject: [PATCH 07/13] Turned ImVec2 to const ref to RenderChar(), RenderText(), ColorButton(), PushClipRect(). for consistency. Comments about polygon filling winding order. --- imgui.h | 14 ++++++++------ imgui_draw.cpp | 28 ++++++++++++++-------------- imgui_internal.h | 2 +- imgui_widgets.cpp | 9 +++------ 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/imgui.h b/imgui.h index 1c5412fc..ea794ae9 100644 --- a/imgui.h +++ b/imgui.h @@ -583,7 +583,7 @@ namespace ImGui IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); - IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed. + IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed. IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. // Widgets: Trees @@ -2519,7 +2519,7 @@ struct ImDrawList ImDrawList(const ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; } ~ImDrawList() { _ClearFreeMemory(); } - IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) + IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) IMGUI_API void PushClipRectFullScreen(); IMGUI_API void PopClipRect(); IMGUI_API void PushTextureID(ImTextureID texture_id); @@ -2528,6 +2528,7 @@ struct ImDrawList inline ImVec2 GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); } // Primitives + // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. // - For rectangular primitives, "p_min" and "p_max" represent the upper-left and lower-right corners. // - For circle primitives, use "num_segments == 0" to automatically calculate tessellation (preferred). // In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12. @@ -2548,7 +2549,7 @@ struct ImDrawList IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); - IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); IMGUI_API void AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0); // Quadratic Bezier (3 control points) @@ -2561,10 +2562,11 @@ struct ImDrawList IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() + // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. inline void PathClear() { _Path.Size = 0; } inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); } - inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order. + inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle @@ -2875,8 +2877,8 @@ struct ImFont // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; - IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const; - IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; + IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const; + IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; // [Internal] Don't use! IMGUI_API void BuildLookupTable(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 3fbe99bf..387bf193 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -580,7 +580,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const } // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) -void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) +void ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool intersect_with_current_clip_rect) { ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); if (intersect_with_current_clip_rect) @@ -973,7 +973,8 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } } -// We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds. +// - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds. +// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) { if (points_count < 3) @@ -3523,7 +3524,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons } // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. -void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const +void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const { const ImFontGlyph* glyph = FindGlyph(c); if (!glyph || !glyph->Visible) @@ -3531,26 +3532,25 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col if (glyph->Colored) col |= ~IM_COL32_A_MASK; float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; - pos.x = IM_FLOOR(pos.x); - pos.y = IM_FLOOR(pos.y); + float x = IM_FLOOR(pos.x); + float y = IM_FLOOR(pos.y); draw_list->PrimReserve(6, 4); - draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); + draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); } // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. -void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const +void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const { if (!text_end) text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. // Align to be pixel perfect - pos.x = IM_FLOOR(pos.x); - pos.y = IM_FLOOR(pos.y); - float x = pos.x; - float y = pos.y; + float x = IM_FLOOR(pos.x); + float y = IM_FLOOR(pos.y); if (y > clip_rect.w) return; + const float start_x = x; const float scale = size / FontSize; const float line_height = FontSize * scale; const bool word_wrap_enabled = (wrap_width > 0.0f); @@ -3602,14 +3602,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. if (!word_wrap_eol) { - word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - pos.x)); + word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x)); if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below } if (s >= word_wrap_eol) { - x = pos.x; + x = start_x; y += line_height; word_wrap_eol = NULL; @@ -3640,7 +3640,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col { if (c == '\n') { - x = pos.x; + x = start_x; y += line_height; if (y > clip_rect.w) break; // break out of main loop diff --git a/imgui_internal.h b/imgui_internal.h index a7adbcd5..a5ee7afe 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1294,7 +1294,7 @@ enum ImGuiNavHighlightFlags_ enum ImGuiNavDirSourceFlags_ { ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), faciliate use of some functions before we can unify nav and keys + ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), facilitate use of some functions before we can unify nav and keys ImGuiNavDirSourceFlags_Keyboard = 1 << 1, ImGuiNavDirSourceFlags_PadDPad = 1 << 2, ImGuiNavDirSourceFlags_PadLStick = 1 << 3 diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f4080145..a7baa397 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5454,7 +5454,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. // Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set. -bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) +bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -5462,11 +5462,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl ImGuiContext& g = *GImGui; const ImGuiID id = window->GetID(desc_id); - float default_size = GetFrameHeight(); - if (size.x == 0.0f) - size.x = default_size; - if (size.y == 0.0f) - size.y = default_size; + const float default_size = GetFrameHeight(); + const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); if (!ItemAdd(bb, id)) From 88fbc31ee08238750ed1070ca5181fb2d67d0927 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Mar 2022 14:23:30 +0200 Subject: [PATCH 08/13] stb_truetype: fix a division by zero (unused chain of result, but triggering debuggers). (#5139, #5075) Amend 0cff5ac (mislabeled as stb_textedit) --- imstb_truetype.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imstb_truetype.h b/imstb_truetype.h index bd143556..643d3789 100644 --- a/imstb_truetype.h +++ b/imstb_truetype.h @@ -3200,8 +3200,11 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, // check if final y_crossing is blown up; no test case for this if (y_final > y_bottom) { + int denom = (x2 - (x1+1)); y_final = y_bottom; - dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom + if (denom != 0) { // [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316) + dy = (y_final - y_crossing ) / denom; // if denom=0, y_final = y_crossing, so y_final <= y_bottom + } } // in second pixel, area covered by line segment found in first pixel From f5c5926fb91764c2ec0e995970818d79b5873d42 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Mar 2022 14:49:18 +0200 Subject: [PATCH 09/13] Internals: remove obsolete variants of RenderArrow(), RenderBullet(), ImHash() + misc comments. --- imgui.cpp | 16 ++++++++++------ imgui.h | 40 +++++++++++++++++++--------------------- imgui_internal.h | 36 +++++++++++++++--------------------- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 91bd1f8b..80629247 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7962,9 +7962,12 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit -// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code -// may see different structures than what imgui.cpp sees, which is problematic. -// We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui. +// If this triggers you have an issue: +// - Most commonly: mismatched headers and compiled code version. +// - Or: mismatched configuration #define, compilation settings, packing pragma etc. +// The configuration settings mentioned in imconfig.h must be set for all compilation units involved with Dear ImGui, +// which is way it is required you put them in your imconfig file (and not just before including imgui.h). +// Otherwise it is possible that different compilation units would see different structure layout bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) { bool error = false; @@ -7998,10 +8001,11 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()"); IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); - IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); + IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); 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); + IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right); #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)"); @@ -8030,7 +8034,7 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); IM_UNUSED(key_mod_flags); - // Recover from errors + // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame(). //ErrorCheckEndFrameRecover(); // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you @@ -8212,7 +8216,7 @@ void ImGuiStackSizes::CompareWithCurrentState() // - GetWindowContentRegionMin(), GetWindowContentRegionMax() // - BeginGroup() // - EndGroup() -// Also see in imgui_widgets: tab bars, columns. +// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns. //----------------------------------------------------------------------------- // Advance cursor given item size for layout. diff --git a/imgui.h b/imgui.h index ea794ae9..5e769f9f 100644 --- a/imgui.h +++ b/imgui.h @@ -704,11 +704,10 @@ namespace ImGui // Tables // - Full-featured replacement for old Columns API. - // - See Demo->Tables for demo code. - // - See top of imgui_tables.cpp for general commentary. + // - See Demo->Tables for demo code. See top of imgui_tables.cpp for general commentary. // - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags. // The typical call flow is: - // - 1. Call BeginTable(). + // - 1. Call BeginTable(), early out if returning false. // - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults. // - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows. // - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data. @@ -727,10 +726,10 @@ namespace ImGui // -------------------------------------------------------------------------------------------------------- // - 5. Call EndTable() IMGUI_API bool BeginTable(const char* str_id, int column, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f); - IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! + IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. - IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. - IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. + IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. + IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. // Tables: Headers & Columns declaration // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. @@ -741,20 +740,17 @@ namespace ImGui // some advanced use cases (e.g. adding custom widgets in header row). // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); - IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. - IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu - IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) - - // Tables: Sorting - // - Call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. - // - When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed - // since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, else you may - // wastefully sort your data every frame! - // - Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). - IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). - - // Tables: Miscellaneous functions + IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. + IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu + IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) + + // Tables: Sorting & Miscellaneous functions + // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. + // When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have + // changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, + // else you may wastefully sort your data every frame! // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index. + IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) IMGUI_API int TableGetColumnIndex(); // return current column index. IMGUI_API int TableGetRowIndex(); // return current row index. @@ -849,13 +845,15 @@ namespace ImGui // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL. + // Background/Foreground Draw Lists + IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendered one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. IMGUI_API double GetTime(); // get global imgui time. incremented by io.DeltaTime every frame. IMGUI_API int GetFrameCount(); // get global imgui frame count. incremented by 1 every frame. - IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. - IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances. 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) diff --git a/imgui_internal.h b/imgui_internal.h index a5ee7afe..4053f384 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -279,7 +279,8 @@ namespace ImStb // - Helpers: Hashing // - Helpers: Sorting // - Helpers: Bit manipulation -// - Helpers: String, Formatting +// - Helpers: String +// - Helpers: Formatting // - Helpers: UTF-8 <> wchar conversions // - Helpers: ImVec2/ImVec4 operators // - Helpers: Maths @@ -297,9 +298,6 @@ namespace ImStb // Helpers: Hashing IMGUI_API ImGuiID ImHashData(const void* data, size_t data_size, ImU32 seed = 0); IMGUI_API ImGuiID ImHashStr(const char* data, size_t data_size = 0, ImU32 seed = 0); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -static inline ImGuiID ImHash(const void* data, int size, ImU32 seed = 0) { return size ? ImHashData(data, (size_t)size, seed) : ImHashStr((const char*)data, 0, seed); } // [moved to ImHashStr/ImHashData in 1.68] -#endif // Helpers: Sorting #ifndef ImQsort @@ -314,7 +312,7 @@ static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & (v - 1)) == 0; } static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } -// Helpers: String, Formatting +// Helpers: String IMGUI_API int ImStricmp(const char* str1, const char* str2); IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); @@ -327,14 +325,16 @@ IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* bu IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); IMGUI_API void ImStrTrimBlanks(char* str); IMGUI_API const char* ImStrSkipBlank(const char* str); +static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } +static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } + +// Helpers: Formatting IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API const char* ImParseFormatFindStart(const char* format); IMGUI_API const char* ImParseFormatFindEnd(const char* format); IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size); IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); -static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } -static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } // Helpers: UTF-8 <> wchar conversions IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf @@ -776,9 +776,9 @@ enum ImGuiItemStatusFlags_ #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] - ImGuiItemStatusFlags_Openable = 1 << 20, // + ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode) ImGuiItemStatusFlags_Opened = 1 << 21, // - ImGuiItemStatusFlags_Checkable = 1 << 22, // + ImGuiItemStatusFlags_Checkable = 1 << 22, // Item is a checkable (e.g. CheckBox, MenuItem) ImGuiItemStatusFlags_Checked = 1 << 23 // #endif }; @@ -931,8 +931,8 @@ enum ImGuiDataTypePrivate_ // Stacked color modifier, backup of modified data so we can restore it struct ImGuiColorMod { - ImGuiCol Col; - ImVec4 BackupValue; + ImGuiCol Col; + ImVec4 BackupValue; }; // Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. @@ -1549,7 +1549,7 @@ struct ImGuiContextHook }; //----------------------------------------------------------------------------- -// [SECTION] ImGuiContext (main imgui context) +// [SECTION] ImGuiContext (main Dear ImGui context) //----------------------------------------------------------------------------- struct ImGuiContext @@ -1646,7 +1646,7 @@ struct ImGuiContext ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. // Gamepad/keyboard Navigation - ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' + ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavId; // Focused item for navigation ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set) ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() @@ -1726,7 +1726,7 @@ struct ImGuiContext int ClipperTempDataStacked; ImVector ClipperTempData; - // Table + // Tables ImGuiTable* CurrentTable; 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) @@ -2682,7 +2682,7 @@ namespace ImGui 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); } + inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87] #endif // Drag and Drop @@ -2790,12 +2790,6 @@ namespace ImGui 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, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding); -#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] - inline void RenderArrow(ImVec2 pos, ImGuiDir dir, float scale=1.0f) { ImGuiWindow* window = GetCurrentWindow(); RenderArrow(window->DrawList, pos, GetColorU32(ImGuiCol_Text), dir, scale); } - inline void RenderBullet(ImVec2 pos) { ImGuiWindow* window = GetCurrentWindow(); RenderBullet(window->DrawList, pos, GetColorU32(ImGuiCol_Text)); } -#endif - // Widgets IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); From 508c9aaf6003056acff3b8c79895750d83910902 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 4 Apr 2022 14:20:26 +0200 Subject: [PATCH 10/13] Sliders, Drags: Fixed using hexadecimal display format strings (pretty much never worked). (#5165, #3133) Ditched unnecessary code. When transitioning from float in 3e8087458 we added an unnecessary path there, which evolved in c5fb92955. --- docs/CHANGELOG.txt | 1 + imgui.h | 2 +- imgui_demo.cpp | 2 ++ imgui_internal.h | 2 +- imgui_widgets.cpp | 47 +++++++++++++++++++++++----------------------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5a849df0..07c988b5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,6 +53,7 @@ Other Changes: or interacting with a game/3D view). - Clipper: Fixed a regression in 1.86 when not calling clipper.End() and late destructing the clipper instance. High-level languages (Lua,Rust etc.) would typically be affected. (#4822) +- Sliders, Drags: Fixed using hexadecimal display format strings (it never worked). (#5165, #3133) - IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the return value is overriden by focus when gamepad/keyboard navigation is active. - InputText: Fixed pressing Tab emitting two tabs characters because of dual Keys/Chars events being diff --git a/imgui.h b/imgui.h index 5e769f9f..f58d74f7 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.88 WIP" -#define IMGUI_VERSION_NUM 18713 +#define IMGUI_VERSION_NUM 18714 #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 5f9742d1..5ab9041b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1921,6 +1921,7 @@ static void ShowDemoWindowWidgets() ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags); ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags); ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); + ImGui::DragInt("DragInt (gex)", &drag_i, 0.5f, 0, 100, "0x%04X", flags); // Sliders static float slider_f = 0.5f; @@ -1928,6 +1929,7 @@ static void ShowDemoWindowWidgets() ImGui::Text("Underlying float value: %f", slider_f); ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags); ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags); + ImGui::SliderInt("SliderInt (hex)", &slider_i, 0, 100, "0x%04X", flags); ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index 4053f384..14aaa081 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2823,7 +2823,7 @@ namespace ImGui template IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size); template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags); template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); - template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); + template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); template IMGUI_API bool CheckboxFlagsT(const char* label, T* flags, T flags_value); // Data type helpers diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a7baa397..1d3dd8e0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2104,19 +2104,6 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision) return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); } -template -static const char* ImAtoi(const char* src, TYPE* output) -{ - int negative = 0; - if (*src == '-') { negative = 1; src++; } - if (*src == '+') { src++; } - TYPE v = 0; - while (*src >= '0' && *src <= '9') - v = (v * 10) + (*src++ - '0'); - *output = negative ? -v : v; - return src; -} - // Sanitize format // - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi // - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. @@ -2134,9 +2121,10 @@ static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_ *fmt_out = 0; // Zero-terminate } -template +template TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) { + IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); const char* fmt_start = ImParseFormatFindStart(format); if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string return v; @@ -2152,10 +2140,8 @@ TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, const char* p = v_str; while (*p == ' ') p++; - if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) - v = (TYPE)ImAtof(p); - else - ImAtoi(p, (SIGNEDTYPE*)&v); + v = (TYPE)ImAtof(p); + return v; } @@ -2259,8 +2245,8 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const } // Round to user desired precision based on format string - if (!(flags & ImGuiSliderFlags_NoRoundToFormat)) - v_cur = RoundScalarWithFormatT(format, data_type, v_cur); + if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) + v_cur = RoundScalarWithFormatT(format, data_type, v_cur); // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. g.DragCurrentAccumDirty = false; @@ -2856,8 +2842,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ // Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - if (!(flags & ImGuiSliderFlags_NoRoundToFormat)) - v_new = RoundScalarWithFormatT(format, data_type, v_new); + if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) + v_new = RoundScalarWithFormatT(format, data_type, v_new); float new_clicked_t = ScaleRatioFromValueT(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (delta > 0) @@ -2875,8 +2861,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); // Round to user desired precision based on format string - if (!(flags & ImGuiSliderFlags_NoRoundToFormat)) - v_new = RoundScalarWithFormatT(format, data_type, v_new); + if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat)) + v_new = RoundScalarWithFormatT(format, data_type, v_new); // Apply result if (*v != v_new) @@ -3282,6 +3268,19 @@ const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_ return buf; } +template +static const char* ImAtoi(const char* src, TYPE* output) +{ + int negative = 0; + if (*src == '-') { negative = 1; src++; } + if (*src == '+') { src++; } + TYPE v = 0; + while (*src >= '0' && *src <= '9') + v = (v * 10) + (*src++ - '0'); + *output = negative ? -v : v; + return src; +} + // Parse display precision back from the display format string // FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. int ImParseFormatPrecision(const char* fmt, int default_precision) From c521883be4824a045528a4389f3ada5cf3803289 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 4 Apr 2022 18:20:04 +0200 Subject: [PATCH 11/13] Sliders, Drags: Fixed manual input when using hexadecimal display format string. (#5165, #3133) InputScalar: Fixed manual input when using %03d style width in display format string. (amended once) --- docs/CHANGELOG.txt | 4 +- imgui.h | 2 +- imgui_internal.h | 2 + imgui_widgets.cpp | 91 +++++++++++++++++++++++++++++----------------- 4 files changed, 63 insertions(+), 36 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 07c988b5..121221e7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,7 +53,9 @@ Other Changes: or interacting with a game/3D view). - Clipper: Fixed a regression in 1.86 when not calling clipper.End() and late destructing the clipper instance. High-level languages (Lua,Rust etc.) would typically be affected. (#4822) -- Sliders, Drags: Fixed using hexadecimal display format strings (it never worked). (#5165, #3133) +- Sliders, Drags: Fixed dragging when using hexadecimal display format string. (#5165, #3133) +- Sliders, Drags: Fixed manual input when using hexadecimal display format string. (#5165, #3133) +- InputScalar: Fixed manual input when using %03d style width in display format string. (#5165, #3133) - IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the return value is overriden by focus when gamepad/keyboard navigation is active. - InputText: Fixed pressing Tab emitting two tabs characters because of dual Keys/Chars events being diff --git a/imgui.h b/imgui.h index f58d74f7..803ba0f6 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.88 WIP" -#define IMGUI_VERSION_NUM 18714 +#define IMGUI_VERSION_NUM 18715 #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 14aaa081..ed4ea5c7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -334,6 +334,8 @@ IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* IMGUI_API const char* ImParseFormatFindStart(const char* format); IMGUI_API const char* ImParseFormatFindEnd(const char* format); IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size); +IMGUI_API void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size); +IMGUI_API const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size); IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); // Helpers: UTF-8 <> wchar conversions diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1d3dd8e0..f43fa1e3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2007,23 +2007,20 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void ImGuiDataTypeTempStorage data_backup; memcpy(&data_backup, p_data, type_info->Size); - if (format == NULL) + // Sanitize format + // 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 + char format_sanitized[32]; + if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) format = type_info->ScanFmt; - - 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) - { - // 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; - } else + format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized)); + + // Small types need a 32-bit buffer to receive the result from scanf() + int v32 = 0; + if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1) + return false; + if (type_info->Size < 4) { - // Small types need a 32-bit buffer to receive the result from scanf() - int v32; - if (sscanf(buf, format, &v32) < 1) - return false; if (data_type == ImGuiDataType_S8) *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); else if (data_type == ImGuiDataType_U8) @@ -2104,26 +2101,10 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision) return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); } -// Sanitize format -// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi -// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. -static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_size) -{ - IM_UNUSED(fmt_out_size); - const char* fmt_end = ImParseFormatFindEnd(fmt); - IM_ASSERT((size_t)(fmt_end - fmt + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! - while (fmt < fmt_end) - { - char c = *(fmt++); - if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. - *(fmt_out++) = c; - } - *fmt_out = 0; // Zero-terminate -} - template TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) { + IM_UNUSED(data_type); IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); const char* fmt_start = ImParseFormatFindStart(format); if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string @@ -2131,7 +2112,7 @@ TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, // Sanitize format char fmt_sanitized[32]; - SanitizeFormatString(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); + ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized)); fmt_start = fmt_sanitized; // Format value with our rounding, and read back @@ -3205,6 +3186,8 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i // - ImParseFormatFindStart() [Internal] // - ImParseFormatFindEnd() [Internal] // - ImParseFormatTrimDecorations() [Internal] +// - ImParseFormatSanitizeForPrinting() [Internal] +// - ImParseFormatSanitizeForScanning() [Internal] // - ImParseFormatPrecision() [Internal] // - TempInputTextScalar() [Internal] // - InputScalar() @@ -3268,6 +3251,44 @@ const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_ return buf; } +// Sanitize format +// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi +// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi. +void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size) +{ + const char* fmt_end = ImParseFormatFindEnd(fmt_in); + IM_UNUSED(fmt_out_size); + IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! + while (fmt_in < fmt_end) + { + char c = *fmt_in++; + if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. + *(fmt_out++) = c; + } + *fmt_out = 0; // Zero-terminate +} + +// - For scanning we need to remove all width and precision fields "%3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" +const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size) +{ + const char* fmt_end = ImParseFormatFindEnd(fmt_in); + const char* fmt_out_begin = fmt_out; + IM_UNUSED(fmt_out_size); + IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you! + bool has_type = false; + while (fmt_in < fmt_end) + { + char c = *fmt_in++; + if (!has_type && ((c >= '0' && c <= '9') || c == '.')) + continue; + has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits + if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. + *(fmt_out++) = c; + } + *fmt_out = 0; // Zero-terminate + return fmt_out_begin; +} + template static const char* ImAtoi(const char* src, TYPE* output) { @@ -3337,9 +3358,11 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); + const bool is_floating_point = (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); + const bool is_hexadecimal = !is_floating_point && format[0] && toupper(format[strlen(format) - 1]) == 'X'; ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; - flags |= ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); + flags |= is_floating_point ? ImGuiInputTextFlags_CharsScientific : is_hexadecimal ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal; bool value_changed = false; if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) { @@ -3349,7 +3372,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 - DataTypeApplyFromText(data_buf, data_type, p_data, NULL); + DataTypeApplyFromText(data_buf, data_type, p_data, format); if (p_clamp_min || p_clamp_max) { if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0) From a472e8834be45baf5229e6b49b9aa2c997fccb2d Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 5 Apr 2022 15:13:37 +0200 Subject: [PATCH 12/13] InputScalar: Automatically allow hexadecimal/scientific input when format is adequate. --- docs/CHANGELOG.txt | 2 ++ imgui_demo.cpp | 8 ++++---- imgui_widgets.cpp | 23 +++++++++++++++-------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 121221e7..4860b5f1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,8 @@ Other Changes: - Sliders, Drags: Fixed dragging when using hexadecimal display format string. (#5165, #3133) - Sliders, Drags: Fixed manual input when using hexadecimal display format string. (#5165, #3133) - InputScalar: Fixed manual input when using %03d style width in display format string. (#5165, #3133) +- InputScalar: Automatically allow hexadecimal input when format is %X (without extra flag). +- InputScalar: Automatically allow scientific input when format is float/double (without extra flag). - IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the return value is overriden by focus when gamepad/keyboard navigation is active. - InputText: Fixed pressing Tab emitting two tabs characters because of dual Keys/Chars events being diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5ab9041b..94d78cc2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1921,7 +1921,6 @@ static void ShowDemoWindowWidgets() ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags); ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags); ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); - ImGui::DragInt("DragInt (gex)", &drag_i, 0.5f, 0, 100, "0x%04X", flags); // Sliders static float slider_f = 0.5f; @@ -1929,7 +1928,6 @@ static void ShowDemoWindowWidgets() ImGui::Text("Underlying float value: %f", slider_f); ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags); ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags); - ImGui::SliderInt("SliderInt (hex)", &slider_i, 0, 100, "0x%04X", flags); ImGui::TreePop(); } @@ -2006,6 +2004,7 @@ static void ShowDemoWindowWidgets() ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); + ImGui::DragScalar("drag s32 hex", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X"); ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); @@ -2023,6 +2022,7 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); + ImGui::SliderScalar("slider s32 hex", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty, "0x%04X"); ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); @@ -2056,9 +2056,9 @@ static void ShowDemoWindowWidgets() ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d"); ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u"); ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); - ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X"); ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); - ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X"); ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL); ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL); ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f43fa1e3..7b00df22 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3348,6 +3348,14 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* return value_changed; } +static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(ImGuiDataType data_type, const char* format) +{ + if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) + return ImGuiInputTextFlags_CharsScientific; + const char format_last_char = format[0] ? format[strlen(format) - 1] : 0; + return (format_last_char == 'x' || format_last_char == 'X') ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal; +} + // Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set! // This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility. // However this may not be ideal for all uses, as some user code may break on out of bound values. @@ -3358,11 +3366,10 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); - const bool is_floating_point = (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); - const bool is_hexadecimal = !is_floating_point && format[0] && toupper(format[strlen(format) - 1]) == 'X'; ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; - flags |= is_floating_point ? ImGuiInputTextFlags_CharsScientific : is_hexadecimal ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal; + flags |= InputScalar_DefaultCharsFilter(data_type, format); + bool value_changed = false; if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) { @@ -3405,12 +3412,12 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data char buf[64]; DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); - bool value_changed = false; - if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) - flags |= ImGuiInputTextFlags_CharsDecimal; - flags |= ImGuiInputTextFlags_AutoSelectAll; - flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. + // Testing ActiveId as a minor optimization as filtering is not needed until active + if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) + flags |= InputScalar_DefaultCharsFilter(data_type, format); + flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. + bool value_changed = false; if (p_step != NULL) { const float button_size = GetFrameHeight(); From 2c03aac6d30720d8f717dcd9cbb8b9a6799396b8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 5 Apr 2022 15:42:19 +0200 Subject: [PATCH 13/13] Renamed ImGuiKeyModFlags to ImGuiModFlags (Breaking but technically never advertised type) --- docs/CHANGELOG.txt | 5 ++++- imgui.cpp | 28 +++++++++++++++------------- imgui.h | 22 +++++++++++++--------- imgui_internal.h | 10 +++++----- imgui_widgets.cpp | 9 ++++----- 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4860b5f1..6af6e1de 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,9 @@ HOW TO UPDATE? Breaking changes: +- Renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). + (This was never used in public API functions but technically present in imgui.h and ImGuiIO). + Other Changes: - IO: Fixed backward-compatibility regression introduced in 1.87: (#4921, #4858) @@ -340,7 +343,7 @@ 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. +- 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. diff --git a/imgui.cpp b/imgui.cpp index 80629247..e4cbb9a2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -385,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/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO. - 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). @@ -1251,7 +1252,7 @@ void ImGuiIO::ClearInputKeys() KeysData[n].DownDurationPrev = -1.0f; } KeyCtrl = KeyShift = KeyAlt = KeySuper = false; - KeyMods = ImGuiKeyModFlags_None; + KeyMods = ImGuiModFlags_None; for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++) NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; } @@ -3994,7 +3995,7 @@ static void ImGui::UpdateKeyboardInputs() #endif // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools - io.KeyMods = GetMergedKeyModFlags(); + io.KeyMods = GetMergedModFlags(); // Clear gamepad data if disabled if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) @@ -4246,15 +4247,16 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } -ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() +// [Internal] Do not use directly (can read io.KeyMods instead) +ImGuiModFlags ImGui::GetMergedModFlags() { ImGuiContext& g = *GImGui; - ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None; - if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; } - if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; } - if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; } - if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; } - return key_mod_flags; + ImGuiModFlags key_mods = ImGuiModFlags_None; + if (g.IO.KeyCtrl) { key_mods |= ImGuiModFlags_Ctrl; } + if (g.IO.KeyShift) { key_mods |= ImGuiModFlags_Shift; } + if (g.IO.KeyAlt) { key_mods |= ImGuiModFlags_Alt; } + if (g.IO.KeySuper) { key_mods |= ImGuiModFlags_Super; } + return key_mods; } void ImGui::NewFrame() @@ -7897,7 +7899,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) 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(); + io.KeyMods = GetMergedModFlags(); } // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends @@ -8030,9 +8032,9 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), // while still correctly asserting on mid-frame key press events. - const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags(); - IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); - IM_UNUSED(key_mod_flags); + const ImGuiModFlags key_mods = GetMergedModFlags(); + IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); + IM_UNUSED(key_mods); // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame(). //ErrorCheckEndFrameRecover(); diff --git a/imgui.h b/imgui.h index 803ba0f6..7ff46372 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.88 WIP" -#define IMGUI_VERSION_NUM 18715 +#define IMGUI_VERSION_NUM 18716 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -189,7 +189,7 @@ typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: f typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() -typedef int ImGuiKeyModFlags; // -> enum ImGuiKeyModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super) +typedef int ImGuiModFlags; // -> enum ImGuiModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super) typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. @@ -1452,13 +1452,13 @@ enum ImGuiKey_ }; // 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_ +enum ImGuiModFlags_ { - ImGuiKeyModFlags_None = 0, - ImGuiKeyModFlags_Ctrl = 1 << 0, - ImGuiKeyModFlags_Shift = 1 << 1, - ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key + ImGuiModFlags_None = 0, + ImGuiModFlags_Ctrl = 1 << 0, + ImGuiModFlags_Shift = 1 << 1, + ImGuiModFlags_Alt = 1 << 2, // Menu + ImGuiModFlags_Super = 1 << 3 // Cmd/Super/Windows key }; // Gamepad/Keyboard navigation @@ -2025,7 +2025,7 @@ 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(). // 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() + ImGuiModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) @@ -3018,6 +3018,10 @@ enum ImDrawCornerFlags_ ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight }; +// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022) +typedef int ImGuiKeyModFlags; +enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = ImGuiModFlags_None, ImGuiKeyModFlags_Ctrl = ImGuiModFlags_Ctrl, ImGuiKeyModFlags_Shift = ImGuiModFlags_Shift, ImGuiKeyModFlags_Alt = ImGuiModFlags_Alt, ImGuiKeyModFlags_Super = ImGuiModFlags_Super }; + #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index ed4ea5c7..36143744 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1658,7 +1658,7 @@ struct ImGuiContext ImGuiActivateFlags NavActivateFlags; 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; + ImGuiModFlags 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. @@ -1679,7 +1679,7 @@ struct ImGuiContext bool NavMoveForwardToNextFrame; ImGuiNavMoveFlags NavMoveFlags; ImGuiScrollFlags NavMoveScrollFlags; - ImGuiKeyModFlags NavMoveKeyMods; + ImGuiModFlags 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? @@ -1870,7 +1870,7 @@ struct ImGuiContext NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; - NavJustMovedToKeyMods = ImGuiKeyModFlags_None; + NavJustMovedToKeyMods = ImGuiModFlags_None; NavInputSource = ImGuiInputSource_None; NavLayer = ImGuiNavLayer_Main; NavIdIsAlive = false; @@ -1886,7 +1886,7 @@ struct ImGuiContext NavMoveForwardToNextFrame = false; NavMoveFlags = ImGuiNavMoveFlags_None; NavMoveScrollFlags = ImGuiScrollFlags_None; - NavMoveKeyMods = ImGuiKeyModFlags_None; + NavMoveKeyMods = ImGuiModFlags_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavScoringDebugCount = 0; NavTabbingDir = 0; @@ -2682,7 +2682,7 @@ namespace ImGui 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(); + IMGUI_API ImGuiModFlags GetMergedModFlags(); #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87] #endif diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7b00df22..8e83b5c0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4265,19 +4265,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) { IM_ASSERT(state != NULL); - IM_ASSERT(io.KeyMods == GetMergedKeyModFlags() && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); // We rarely do this check, but if anything let's do it here. const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1); state->Stb.row_count_per_page = row_count_per_page; const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_osx = io.ConfigMacOSXBehaviors; - const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiKeyModFlags_Super | ImGuiKeyModFlags_Shift)); + const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiModFlags_Super | ImGuiModFlags_Shift)); const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End - const bool is_ctrl_key_only = (io.KeyMods == ImGuiKeyModFlags_Ctrl); - 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_ctrl_key_only = (io.KeyMods == ImGuiModFlags_Ctrl); + const bool is_shift_key_only = (io.KeyMods == ImGuiModFlags_Shift); + const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiModFlags_Super) : (io.KeyMods == ImGuiModFlags_Ctrl); 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());