From a8e1dde3572660886990e21493e56a892d025743 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 15:17:58 +0100 Subject: [PATCH 01/12] Fix static analyser, update readme. --- docs/README.md | 4 ++-- imgui.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7997a80b..58fcdb20 100644 --- a/docs/README.md +++ b/docs/README.md @@ -201,10 +201,10 @@ Ongoing Dear ImGui development is currently financially supported by users and p - [Blizzard](https://careers.blizzard.com/en-us/openings/engineering/all/all/all/1) *Double-chocolate sponsors* -- [Google](https://github.com/google/filament), [Nvidia](https://developer.nvidia.com/nvidia-omniverse), [Ubisoft](https://montreal.ubisoft.com/en/ubisoft-sponsors-user-interface-library-for-c-dear-imgui) +- [Ubisoft](https://montreal.ubisoft.com/en/ubisoft-sponsors-user-interface-library-for-c-dear-imgui), [Supercell](https://supercell.com) *Chocolate sponsors* -- [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras Pranckevičius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [RAD Game Tools](http://www.radgametools.com/), [Supercell](https://supercell.com) +- [Activision](https://careers.activision.com/c/programmingsoftware-engineering-jobs), [Adobe](https://www.adobe.com/products/medium.html), [Aras Pranckevičius](https://aras-p.info), [Arkane Studios](https://www.arkane-studios.com), [Epic](https://www.unrealengine.com/en-US/megagrants), [Google](https://github.com/google/filament), [Nvidia](https://developer.nvidia.com/nvidia-omniverse), [RAD Game Tools](http://www.radgametools.com/) *Salty-caramel sponsors* - [Framefield](http://framefield.com), [Grinding Gear Games](https://www.grindinggear.com), [Kylotonn](https://www.kylotonn.com), [Next Level Games](https://www.nextlevelgames.com), [O-Net Communications (USA)](http://en.o-netcom.com) diff --git a/imgui.cpp b/imgui.cpp index 5b2a6830..40946144 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12006,6 +12006,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list + IM_ASSERT(fg_draw_list != NULL); // For static analyzers if (window && IsItemHovered()) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) From c80179921819818c9743f168ceae6f300851b014 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 15:35:02 +0100 Subject: [PATCH 02/12] Added an assertion for the common user mistake of using "" as an identifier at the root level of a window. (#1414, #2562, #2807, #4008, #4158, #4375, #4548, #4657, #4796) #4158, #4375, #4548, #4657, #4796) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 66e0926e..bd4224a0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,10 @@ Breaking Changes: Other Changes: +- Added an assertion for the common user mistake of using "" as an identifier at the root level of a window + instead of using "##something". Empty identifiers are valid and useful in a very small amount of cases, + but 99.9% of the time if you need an empty label you should use "##something". (#1414, #2562, #2807, #4008, + #4158, #4375, #4548, #4657, #4796). READ THE FAQ ABOUT HOW THE ID STACK WORKS -> https://dearimgui.org/faq - Added GetMouseClickedCount() function, returning the number of successive clicks. (#3229) [@kudaba] (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, but it allows testing for triple clicks and more). diff --git a/imgui.cpp b/imgui.cpp index 40946144..968a5be2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7760,6 +7760,11 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) NavProcessItem(); + // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". + // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". + // READ THE FAQ: https://dearimgui.org/faq + IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); + // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX if (id == g.DebugItemPickerBreakId) From 8a9fe268662292ed9495048d942d8d0341c03185 Mon Sep 17 00:00:00 2001 From: xndcn Date: Fri, 10 Dec 2021 18:47:54 +0100 Subject: [PATCH 03/12] Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 3 +-- imgui.h | 2 +- imgui_widgets.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bd4224a0..bc72a06b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -65,6 +65,7 @@ Other Changes: - Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when already at edge) reactivates the navigation highlight. +- Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) [@xndcn] - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs diff --git a/imgui.cpp b/imgui.cpp index 968a5be2..6e752f6e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12011,8 +12011,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list - IM_ASSERT(fg_draw_list != NULL); // For static analyzers - if (window && IsItemHovered()) + if (window && IsItemHovered() && fg_draw_list) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; diff --git a/imgui.h b/imgui.h index 60e57820..4d0ecaa1 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18518 +#define IMGUI_VERSION_NUM 18519 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index dbdc0e5c..c5c52ab1 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6901,7 +6901,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) ImVec2 label_size = CalcTextSize(label, NULL, true); bool pressed; - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + bool menuset_is_open = (window->Flags & ImGuiWindowFlags_MenuBar) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) From b720f1f03c70c1b83482a902ad0f0c9a34a6861a Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 11 Dec 2021 08:10:21 +1100 Subject: [PATCH 04/12] Backends: OSX: Add Game Controller support. (#4759) --- backends/imgui_impl_osx.h | 1 + backends/imgui_impl_osx.mm | 48 +++++++++++++++++++ docs/CHANGELOG.txt | 1 + .../project.pbxproj | 4 ++ .../project.pbxproj | 4 ++ 5 files changed, 58 insertions(+) diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index e4c1d04a..c223c2cc 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -5,6 +5,7 @@ // Implemented features: // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // Issues: // [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 97555e6c..0c1b3dec 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -5,6 +5,7 @@ // Implemented features: // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // Issues: // [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. @@ -17,9 +18,11 @@ #include "imgui_impl_osx.h" #import #include +#import // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. // 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. // 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. @@ -234,6 +237,50 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() } } +void ImGui_ImplOSX_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + + GCController* controller; + if (@available(macOS 11.0, *)) + controller = GCController.current; + else + controller = GCController.controllers.firstObject; + + if (controller == nil || controller.extendedGamepad == nil) + { + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + return; + } + + GCExtendedGamepad* gp = controller.extendedGamepad; + +#define MAP_BUTTON(NAV_NO, NAME) { io.NavInputs[NAV_NO] = gp.NAME.isPressed ? 1.0 : 0.0; } + MAP_BUTTON(ImGuiNavInput_Activate, buttonA); + MAP_BUTTON(ImGuiNavInput_Cancel, buttonB); + MAP_BUTTON(ImGuiNavInput_Menu, buttonX); + MAP_BUTTON(ImGuiNavInput_Input, buttonY); + MAP_BUTTON(ImGuiNavInput_DpadLeft, dpad.left); + MAP_BUTTON(ImGuiNavInput_DpadRight, dpad.right); + MAP_BUTTON(ImGuiNavInput_DpadUp, dpad.up); + MAP_BUTTON(ImGuiNavInput_DpadDown, dpad.down); + MAP_BUTTON(ImGuiNavInput_FocusPrev, leftShoulder); + MAP_BUTTON(ImGuiNavInput_FocusNext, rightShoulder); + MAP_BUTTON(ImGuiNavInput_TweakSlow, leftTrigger); + MAP_BUTTON(ImGuiNavInput_TweakFast, rightTrigger); +#undef MAP_BUTTON + + io.NavInputs[ImGuiNavInput_LStickLeft] = gp.leftThumbstick.left.value; + io.NavInputs[ImGuiNavInput_LStickRight] = gp.leftThumbstick.right.value; + io.NavInputs[ImGuiNavInput_LStickUp] = gp.leftThumbstick.up.value; + io.NavInputs[ImGuiNavInput_LStickDown] = gp.leftThumbstick.down.value; + + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; +} + void ImGui_ImplOSX_NewFrame(NSView* view) { // Setup display size @@ -256,6 +303,7 @@ void ImGui_ImplOSX_NewFrame(NSView* view) g_Time = current_time; ImGui_ImplOSX_UpdateMouseCursorAndButtons(); + ImGui_ImplOSX_UpdateGamepads(); } static int mapCharacterToKey(int c) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bc72a06b..6af83a6e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -103,6 +103,7 @@ Other Changes: - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] +- Backends: OSX: Add Game Controller support (need linking GameController framework) (#4759) [@stuartcarnie] - Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the in-flux specs says. (#4766) [@meshula] diff --git a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj index 040fcd64..4bb4fc28 100644 --- a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj +++ b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05318E0E274C397200A8DE2E /* GameController.framework */; }; 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; }; @@ -32,6 +33,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 05318E0E274C397200A8DE2E /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; 07A82ED62139413C0078D120 /* imgui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = ../../imgui_internal.h; sourceTree = ""; }; 07A82ED72139413C0078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = ""; }; 5079822D257677DB0038A28D /* imgui_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = ../../imgui_tables.cpp; sourceTree = ""; }; @@ -76,6 +78,7 @@ files = ( 8309BDC6253CCCFE0045E2A1 /* AppKit.framework in Frameworks */, 83BBE9EC20EB471700295997 /* MetalKit.framework in Frameworks */, + 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */, 83BBE9ED20EB471700295997 /* Metal.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -133,6 +136,7 @@ 83BBE9E320EB46B800295997 /* Frameworks */ = { isa = PBXGroup; children = ( + 05318E0E274C397200A8DE2E /* GameController.framework */, 8309BDC5253CCCFE0045E2A1 /* AppKit.framework */, 8309BD8E253CCAAA0045E2A1 /* UIKit.framework */, 83BBE9EE20EB471C00295997 /* ModelIO.framework */, diff --git a/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj b/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj index 82fb267c..a168373d 100644 --- a/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj +++ b/examples/example_apple_opengl2/example_apple_opengl2.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05E31B57274EF0360083FCB6 /* GameController.framework */; }; 07A82EDB213941D00078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82EDA213941D00078D120 /* imgui_widgets.cpp */; }; 4080A99820B02D340036BA46 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4080A98A20B02CD90036BA46 /* main.mm */; }; 4080A9A220B034280036BA46 /* imgui_impl_opengl2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A99E20B034280036BA46 /* imgui_impl_opengl2.cpp */; }; @@ -32,6 +33,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 05E31B57274EF0360083FCB6 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; 07A82EDA213941D00078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = ""; }; 4080A96B20B029B00036BA46 /* example_osx_opengl2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_osx_opengl2; sourceTree = BUILT_PRODUCTS_DIR; }; 4080A98A20B02CD90036BA46 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = SOURCE_ROOT; }; @@ -57,6 +59,7 @@ files = ( 4080A9B520B034EA0036BA46 /* OpenGL.framework in Frameworks */, 4080A9B320B034E40036BA46 /* Cocoa.framework in Frameworks */, + 05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -95,6 +98,7 @@ 4080A9B120B034E40036BA46 /* Frameworks */ = { isa = PBXGroup; children = ( + 05E31B57274EF0360083FCB6 /* GameController.framework */, 4080A9B420B034EA0036BA46 /* OpenGL.framework */, 4080A9B220B034E40036BA46 /* Cocoa.framework */, ); From f71ee5203e6f2e3fa9f0a43731626cfcc1603c9f Mon Sep 17 00:00:00 2001 From: SlavicPotato Date: Sat, 11 Dec 2021 15:03:30 +0100 Subject: [PATCH 05/12] Fix infinite loop in ImFontGlyphRangesBuilder::AddRanges if the user passes upper range = UINT16_MAX without IMGUI_USE_WCHAR32. (#4802) --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6af83a6e..638109dc 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -88,6 +88,8 @@ Other Changes: - Clipper: fixed invalid state when number of frozen table row is smaller than ItemCount. - Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceAllowNullID doesn't lose tooltip when scrolling. (#143) +- Fonts: fixed infinite loop in ImFontGlyphRangesBuilder::AddRanges() when passing UINT16_MAX without + the IMGUI_USE_WCHAR32 compile-time option. (#4802) [@SlavicPotato] - Metrics: Added a node showing windows in submission order and showing the Begin() stack. - Misc: Added missing ImGuiMouseCursor_NotAllowed cursor for software rendering (when the io.MouseDrawCursor flag is enabled). (#4713) [@nobody-special666] diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6b5e70e5..92570da7 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3080,8 +3080,8 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) - for (ImWchar c = ranges[0]; c <= ranges[1]; c++) - AddChar(c); + for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) + AddChar((ImWchar)c); } void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) From 1b6b8602c1115276bc3cf1868e6a07571a566f97 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 11 Dec 2021 09:20:17 +1100 Subject: [PATCH 06/12] Backends: OSX: Fix keyboard support. Handle scroll cancel. Don't set mouse cursor shape unconditionally. (#4759, #4253, #1873) Note the original FIXME: refered to GLFWs Cocoa implementation, which is largely what this commit provides. --- backends/imgui_impl_osx.h | 7 +- backends/imgui_impl_osx.mm | 293 +++++++++++++++++-------- docs/CHANGELOG.txt | 6 + examples/example_apple_metal/main.mm | 2 +- examples/example_apple_opengl2/main.mm | 5 +- 5 files changed, 212 insertions(+), 101 deletions(-) diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index c223c2cc..6cfe7eb2 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -6,10 +6,9 @@ // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// Issues: -// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. +// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space). -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs @@ -19,7 +18,7 @@ @class NSEvent; @class NSView; -IMGUI_IMPL_API bool ImGui_ImplOSX_Init(); +IMGUI_IMPL_API bool ImGui_ImplOSX_Init(NSView* _Nonnull view); IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view); IMGUI_IMPL_API bool ImGui_ImplOSX_HandleEvent(NSEvent* _Nonnull event, NSView* _Nullable view); diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 0c1b3dec..e1222fa7 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -6,22 +6,23 @@ // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// Issues: -// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. +// [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs -#include "imgui.h" -#include "imgui_impl_osx.h" +#import "imgui.h" +#import "imgui_impl_osx.h" #import -#include +#import +#import #import // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. // 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events. @@ -40,15 +41,17 @@ // 2018-07-07: Initial version. @class ImFocusObserver; +@class KeyEventResponder; // Data -static double g_HostClockPeriod = 0.0; -static double g_Time = 0.0; -static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; -static bool g_MouseCursorHidden = false; -static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; -static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; -static ImFocusObserver* g_FocusObserver = NULL; +static double g_HostClockPeriod = 0.0; +static double g_Time = 0.0; +static NSCursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; +static bool g_MouseCursorHidden = false; +static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; +static bool g_MouseDown[ImGuiMouseButton_COUNT] = {}; +static ImFocusObserver* g_FocusObserver = nil; +static KeyEventResponder* g_KeyEventResponder = nil; // Undocumented methods for creating cursors. @interface NSCursor() @@ -77,6 +80,102 @@ static void resetKeys() io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false; } +/** + KeyEventResponder implements the NSTextInputClient protocol as is required by the macOS text input manager. + + The macOS text input manager is invoked by calling the interpretKeyEvents method from the keyDown method. + Keyboard events are then evaluated by the macOS input manager and valid text input is passed back via the + insertText:replacementRange method. + + This is the same approach employed by other cross-platform libraries such as SDL2: + https://github.com/spurious/SDL-mirror/blob/e17aacbd09e65a4fd1e166621e011e581fb017a8/src/video/cocoa/SDL_cocoakeyboard.m#L53 + and GLFW: + https://github.com/glfw/glfw/blob/b55a517ae0c7b5127dffa79a64f5406021bf9076/src/cocoa_window.m#L722-L723 + */ +@interface KeyEventResponder: NSView +@end + +@implementation KeyEventResponder + +- (void)viewDidMoveToWindow +{ + // Ensure self is a first responder to receive the input events. + [self.window makeFirstResponder:self]; +} + +- (void)keyDown:(NSEvent*)event +{ + // Call to the macOS input manager system. + [self interpretKeyEvents:@[event]]; +} + +- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange +{ + ImGuiIO& io = ImGui::GetIO(); + + NSString* characters; + if ([aString isKindOfClass:[NSAttributedString class]]) + characters = [aString string]; + else + characters = (NSString*)aString; + + io.AddInputCharactersUTF8(characters.UTF8String); +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)doCommandBySelector:(SEL)myselector +{ +} + +- (nullable NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange +{ + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange +{ + return NSZeroRect; +} + +- (BOOL)hasMarkedText +{ + return NO; +} + +- (NSRange)markedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)selectedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (void)setMarkedText:(nonnull id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +{ +} + +- (void)unmarkText +{ +} + +- (nonnull NSArray*)validAttributesForMarkedText +{ + return @[]; +} + +@end + @interface ImFocusObserver : NSObject - (void)onApplicationBecomeActive:(NSNotification*)aNotification; @@ -106,7 +205,7 @@ static void resetKeys() @end // Functions -bool ImGui_ImplOSX_Init() +bool ImGui_ImplOSX_Init(NSView* view) { ImGuiIO& io = ImGui::GetIO(); @@ -118,29 +217,28 @@ bool ImGui_ImplOSX_Init() io.BackendPlatformName = "imgui_impl_osx"; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array. - const int offset_for_function_keys = 256 - 0xF700; - io.KeyMap[ImGuiKey_Tab] = '\t'; - io.KeyMap[ImGuiKey_LeftArrow] = NSLeftArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_RightArrow] = NSRightArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_UpArrow] = NSUpArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_DownArrow] = NSDownArrowFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageUp] = NSPageUpFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_PageDown] = NSPageDownFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Home] = NSHomeFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_End] = NSEndFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Insert] = NSInsertFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Delete] = NSDeleteFunctionKey + offset_for_function_keys; - io.KeyMap[ImGuiKey_Backspace] = 127; - io.KeyMap[ImGuiKey_Space] = 32; - io.KeyMap[ImGuiKey_Enter] = 13; - io.KeyMap[ImGuiKey_Escape] = 27; - io.KeyMap[ImGuiKey_KeyPadEnter] = 3; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; + io.KeyMap[ImGuiKey_Tab] = kVK_Tab; + io.KeyMap[ImGuiKey_LeftArrow] = kVK_LeftArrow; + io.KeyMap[ImGuiKey_RightArrow] = kVK_RightArrow; + io.KeyMap[ImGuiKey_UpArrow] = kVK_UpArrow; + io.KeyMap[ImGuiKey_DownArrow] = kVK_DownArrow; + io.KeyMap[ImGuiKey_PageUp] = kVK_PageUp; + io.KeyMap[ImGuiKey_PageDown] = kVK_PageDown; + io.KeyMap[ImGuiKey_Home] = kVK_Home; + io.KeyMap[ImGuiKey_End] = kVK_End; + io.KeyMap[ImGuiKey_Insert] = kVK_F13; + io.KeyMap[ImGuiKey_Delete] = kVK_ForwardDelete; + io.KeyMap[ImGuiKey_Backspace] = kVK_Delete; + io.KeyMap[ImGuiKey_Space] = kVK_Space; + io.KeyMap[ImGuiKey_Enter] = kVK_Return; + io.KeyMap[ImGuiKey_Escape] = kVK_Escape; + io.KeyMap[ImGuiKey_KeyPadEnter] = kVK_ANSI_KeypadEnter; + io.KeyMap[ImGuiKey_A] = kVK_ANSI_A; + io.KeyMap[ImGuiKey_C] = kVK_ANSI_C; + io.KeyMap[ImGuiKey_V] = kVK_ANSI_V; + io.KeyMap[ImGuiKey_X] = kVK_ANSI_X; + io.KeyMap[ImGuiKey_Y] = kVK_ANSI_Y; + io.KeyMap[ImGuiKey_Z] = kVK_ANSI_Z; // Load cursors. Some of them are undocumented. g_MouseCursorHidden = false; @@ -193,6 +291,11 @@ bool ImGui_ImplOSX_Init() name:NSApplicationDidResignActiveNotification object:nil]; + // Add the NSTextInputClient to the view hierarchy, + // to receive keyboard events and translate them to input text. + g_KeyEventResponder = [[KeyEventResponder alloc] initWithFrame:NSZeroRect]; + [view addSubview:g_KeyEventResponder]; + return true; } @@ -227,8 +330,12 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons() } else { - // Show OS mouse cursor - [g_MouseCursors[g_MouseCursors[imgui_cursor] ? imgui_cursor : ImGuiMouseCursor_Arrow] set]; + NSCursor* desired = g_MouseCursors[imgui_cursor] ?: g_MouseCursors[ImGuiMouseCursor_Arrow]; + // -[NSCursor set] generates measureable overhead if called unconditionally. + if (desired != NSCursor.currentCursor) + { + [desired set]; + } if (g_MouseCursorHidden) { g_MouseCursorHidden = false; @@ -306,17 +413,22 @@ void ImGui_ImplOSX_NewFrame(NSView* view) ImGui_ImplOSX_UpdateGamepads(); } -static int mapCharacterToKey(int c) +NSString* NSStringFromPhase(NSEventPhase phase) { - if (c >= 'a' && c <= 'z') - return c - 'a' + 'A'; - if (c == 25) // SHIFT+TAB -> TAB - return 9; - if (c >= 0 && c < 256) - return c; - if (c >= 0xF700 && c < 0xF700 + 256) - return c - 0xF700 + 256; - return -1; + static NSString* strings[] = + { + @"none", + @"began", + @"stationary", + @"changed", + @"ended", + @"cancelled", + @"mayBegin", + }; + + int pos = phase == NSEventPhaseNone ? 0 : __builtin_ctzl((NSUInteger)phase) + 1; + + return strings[pos]; } bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) @@ -349,6 +461,21 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if (event.type == NSEventTypeScrollWheel) { + // Ignore canceled events. + // + // From macOS 12.1, scrolling with two fingers and then decelerating + // by tapping two fingers results in two events appearing: + // + // 1. A scroll wheel NSEvent, with a phase == NSEventPhaseMayBegin, when the user taps + // two fingers to decelerate or stop the scroll events. + // + // 2. A scroll wheel NSEvent, with a phase == NSEventPhaseCancelled, when the user releases the + // two-finger tap. It is this event that sometimes contains large values for scrollingDeltaX and + // scrollingDeltaY. When these are added to the current x and y positions of the scrolling view, + // it appears to jump up or down. It can be observed in Preview, various JetBrains IDEs and here. + if (event.phase == NSEventPhaseCancelled) + return false; + double wheel_dx = 0.0; double wheel_dy = 0.0; @@ -370,6 +497,8 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) wheel_dy = [event deltaY]; } + //NSLog(@"dx=%0.3ff, dy=%0.3f, phase=%@", wheel_dx, wheel_dy, NSStringFromPhase(event.phase)); + if (fabs(wheel_dx) > 0.0) io.MouseWheelH += (float)wheel_dx * 0.1f; if (fabs(wheel_dy) > 0.0) @@ -377,59 +506,39 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) return io.WantCaptureMouse; } - // FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm. - if (event.type == NSEventTypeKeyDown) + if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) { - NSString* str = [event characters]; - NSUInteger len = [str length]; - for (NSUInteger i = 0; i < len; i++) - { - int c = [str characterAtIndex:i]; - if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) - io.AddInputCharacter((unsigned int)c); - - // We must reset in case we're pressing a sequence of special keys while keeping the command pressed - int key = mapCharacterToKey(c); - if (key != -1 && key < 256 && !io.KeySuper) - resetKeys(); - if (key != -1) - io.KeysDown[key] = true; - } + unsigned short code = event.keyCode; + IM_ASSERT(code >= 0 && code < IM_ARRAYSIZE(io.KeysDown)); + io.KeysDown[code] = event.type == NSEventTypeKeyDown; + NSEventModifierFlags flags = event.modifierFlags; + io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0; + io.KeyShift = (flags & NSEventModifierFlagShift) != 0; + io.KeyAlt = (flags & NSEventModifierFlagOption) != 0; + io.KeySuper = (flags & NSEventModifierFlagCommand) != 0; return io.WantCaptureKeyboard; } - if (event.type == NSEventTypeKeyUp) + if (event.type == NSEventTypeFlagsChanged) { - NSString* str = [event characters]; - NSUInteger len = [str length]; - for (NSUInteger i = 0; i < len; i++) + NSEventModifierFlags flags = event.modifierFlags; + switch (event.keyCode) { - int c = [str characterAtIndex:i]; - int key = mapCharacterToKey(c); - if (key != -1) - io.KeysDown[key] = false; + case kVK_Control: + io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0; + break; + case kVK_Shift: + io.KeyShift = (flags & NSEventModifierFlagShift) != 0; + break; + case kVK_Option: + io.KeyAlt = (flags & NSEventModifierFlagOption) != 0; + break; + case kVK_Command: + io.KeySuper = (flags & NSEventModifierFlagCommand) != 0; + break; } return io.WantCaptureKeyboard; } - if (event.type == NSEventTypeFlagsChanged) - { - unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; - - bool oldKeyCtrl = io.KeyCtrl; - bool oldKeyShift = io.KeyShift; - bool oldKeyAlt = io.KeyAlt; - bool oldKeySuper = io.KeySuper; - io.KeyCtrl = flags & NSEventModifierFlagControl; - io.KeyShift = flags & NSEventModifierFlagShift; - io.KeyAlt = flags & NSEventModifierFlagOption; - io.KeySuper = flags & NSEventModifierFlagCommand; - - // We must reset them as we will not receive any keyUp event if they where pressed with a modifier - if ((oldKeyShift && !io.KeyShift) || (oldKeyCtrl && !io.KeyCtrl) || (oldKeyAlt && !io.KeyAlt) || (oldKeySuper && !io.KeySuper)) - resetKeys(); - return io.WantCaptureKeyboard; - } - return false; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 638109dc..639a49f0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,9 @@ Breaking Changes: - Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. (#3841) +- Backends: OSX: Added NSView* parameter to ImGui_ImplOSX_Init(). (#4759) [@stuartcarnie] + Updated Apple+Metal and Apple+GL example applications accordingly. + Other Changes: @@ -105,6 +108,9 @@ Other Changes: - Backends: DX12: Fixed DRAW_EMPTY_SCISSOR_RECTANGLE warnings. (#4775) - Backends: SDL_Renderer: Added support for large meshes (64k+ vertices) with 16-bit indices, enabling 'ImGuiBackendFlags_RendererHasVtxOffset' in the backend. (#3926) [@rokups] +- Backends: OSX: Generally fix keyboard support. Keyboard arrays indexed using kVK_* codes, e.g. + ImGui::IsKeyPressed(kVK_Space). Don't set mouse cursor shape unconditionally. Handle two fingers scroll + cancel event. (#4759, #4253, #1873) [@stuartcarnie] - Backends: OSX: Add Game Controller support (need linking GameController framework) (#4759) [@stuartcarnie] - Backends: WebGPU: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer() and wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index 5d4b7710..bbe51d31 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -119,7 +119,7 @@ return event; }]; - ImGui_ImplOSX_Init(); + ImGui_ImplOSX_Init(self.view); #endif } diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 825c8a8f..92774fbf 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -58,7 +58,7 @@ //ImGui::StyleColorsClassic(); // Setup Platform/Renderer backends - ImGui_ImplOSX_Init(); + ImGui_ImplOSX_Init(self); ImGui_ImplOpenGL2_Init(); // Load Fonts @@ -149,9 +149,6 @@ -(void)reshape { [[self openGLContext] update]; [self updateAndDrawDemoView]; } -(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; } -(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; } --(BOOL)acceptsFirstResponder { return (YES); } --(BOOL)becomeFirstResponder { return (YES); } --(BOOL)resignFirstResponder { return (YES); } -(void)dealloc { animationTimer = nil; } //----------------------------------------------------------------------------------- From d7350668d1ddfb58ced1facc08bd0e8d5759299c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Dec 2021 12:02:12 +0100 Subject: [PATCH 07/12] Fix OSX warnings + Examples/Apple+GL2 calls super reshape. (#4759, #1873) --- examples/example_apple_opengl2/main.mm | 2 +- imgui_draw.cpp | 2 +- imgui_widgets.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 92774fbf..f859162e 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -146,7 +146,7 @@ animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES]; } --(void)reshape { [[self openGLContext] update]; [self updateAndDrawDemoView]; } +-(void)reshape { [super reshape]; [[self openGLContext] update]; [self updateAndDrawDemoView]; } -(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; } -(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; } -(void)dealloc { animationTimer = nil; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 92570da7..3095d832 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3080,7 +3080,7 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) - for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) + for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 AddChar((ImWchar)c); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c5c52ab1..3cca2877 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3695,11 +3695,11 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h #ifdef __APPLE__ // FIXME: Move setting to IO structure #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC #else +static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN #endif From 0e8c199e873fc1e328ea61eda02257e940e2626e Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 13 Dec 2021 13:57:33 +0200 Subject: [PATCH 08/12] CI: Use regex to fix vcxproj platform and toolset versions. (#3249) Remove Discord integration. --- .github/workflows/build.yml | 41 +++---------------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc8cbba7..13744f54 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,14 +37,10 @@ jobs: - name: Fix Projects shell: powershell run: | - # WARNING: This will need updating if toolset/sdk change in project files! + # CI workers do not supporter older Visual Studio versions. Fix projects to target newer available version. gci -recurse -filter "*.vcxproj" | ForEach-Object { - # Fix SDK and toolset for most samples. - (Get-Content $_.FullName) -Replace "v110","v142" | Set-Content -Path $_.FullName - (Get-Content $_.FullName) -Replace "8.1","10.0.18362.0" | Set-Content -Path $_.FullName - # Fix SDK and toolset for samples that require newer SDK/toolset. At the moment it is only dx12. - (Get-Content $_.FullName) -Replace "v140","v142" | Set-Content -Path $_.FullName - (Get-Content $_.FullName) -Replace "10.0.14393.0","10.0.18362.0" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "v\d{3}","v142" | Set-Content -Path $_.FullName + (Get-Content $_.FullName) -Replace "[\d\.]+","10.0.18362.0" | Set-Content -Path $_.FullName } # Not using matrix here because it would inflate job count too much. Check out and setup is done for every job and that makes build times way too long. @@ -497,34 +493,3 @@ jobs: run: | cd examples/example_android_opengl3/android gradle assembleDebug - - Discord-CI: - runs-on: ubuntu-18.04 - if: always() - needs: [Windows, Linux, MacOS, iOS, Emscripten, Android] - steps: - - uses: dearimgui/github_discord_notifier@latest - with: - discord-webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} - github-token: ${{ github.token }} - action-task: discord-jobs - discord-filter: "'{{ github.branch }}'.match(/master|docking/g) != null && '{{ run.conclusion }}' != '{{ last_run.conclusion }}'" - discord-username: GitHub Actions - discord-job-new-failure-message: '' - discord-job-fixed-failure-message: '' - discord-job-new-failure-embed: | - { - "title": "`{{ job.name }}` job is failing on `{{ github.branch }}`!", - "description": "Commit [{{ github.context.payload.head_commit.title }}]({{ github.context.payload.head_commit.url }}) pushed to [{{ github.branch }}]({{ github.branch_url }}) broke [{{ job.name }}]({{ job.url }}) build job.\nFailing steps: {{ failing_steps }}", - "url": "{{ job.url }}", - "color": "0xFF0000", - "timestamp": "{{ run.updated_at }}" - } - discord-job-fixed-failure-embed: | - { - "title": "`{{ github.branch }}` branch is no longer failing!", - "description": "Build failures were fixed on [{{ github.branch }}]({{ github.branch_url }}) branch.", - "color": "0x00FF00", - "url": "{{ github.context.payload.head_commit.url }}", - "timestamp": "{{ run.completed_at }}" - } From 11638fdf7d86b04907d69732320909a3af871a7b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Dec 2021 19:35:16 +0100 Subject: [PATCH 09/12] Menus: reuse more menu windows when stacked over popups. This is actually mostly aimed at ensuring the _ChildWindow flag on menu window doesn't vary as this would cause issues with our current g.WindowsFocusOrder[] scheme --- imgui.cpp | 6 +++++- imgui_internal.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 6e752f6e..99c8f0d9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5990,6 +5990,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window_stack_data.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindowStack.push_back(window_stack_data); g.CurrentWindow = NULL; + if (flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount++; if (flags & ImGuiWindowFlags_Popup) { @@ -6595,6 +6597,8 @@ void ImGui::End() // Pop from window stack g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; + if (window->Flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); @@ -8710,7 +8714,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) char name[20]; if (flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame diff --git a/imgui_internal.h b/imgui_internal.h index 0a1cc100..02a5ebf3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1580,6 +1580,7 @@ struct ImGuiContext ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + int BeginMenuCount; // Viewports ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. @@ -1801,6 +1802,7 @@ struct ImGuiContext LastActiveIdTimer = 0.0f; CurrentItemFlags = ImGuiItemFlags_None; + BeginMenuCount = 0; NavWindow = NULL; NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; From a528398c7780647d8a7d3bc46593dec987e76a1f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Dec 2021 16:17:31 +0100 Subject: [PATCH 10/12] Internals: support for varying _ChildWindow flag for menu windows. (#3496, #4797) --- imgui.cpp | 31 +++++++++++++++++++++++++------ imgui.h | 2 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 99c8f0d9..39917cdb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5286,6 +5286,27 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin window->Collapsed = settings->Collapsed; } +static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiWindowFlags old_flags = window->Flags; + const bool child_flag_changed = (new_flags & ImGuiWindowFlags_ChildWindow) != (old_flags & ImGuiWindowFlags_ChildWindow); + + if ((just_created || child_flag_changed) && !(new_flags & ImGuiWindowFlags_ChildWindow)) + { + g.WindowsFocusOrder.push_back(window); + window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); + } + else if (child_flag_changed && (new_flags & ImGuiWindowFlags_ChildWindow)) + { + IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); + for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) + g.WindowsFocusOrder[n]->FocusOrder--; + g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); + window->FocusOrder = -1; + } +} + static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -5325,16 +5346,12 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - if (!(flags & ImGuiWindowFlags_ChildWindow)) - { - g.WindowsFocusOrder.push_back(window); - window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); - } - if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); + UpdateWindowInFocusOrderList(window, true, window->Flags); + return window; } @@ -5934,6 +5951,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_created = (window == NULL); if (window_just_created) window = CreateNewWindow(name, flags); + else + UpdateWindowInFocusOrderList(window, window_just_created, flags); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) diff --git a/imgui.h b/imgui.h index 4d0ecaa1..73375295 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18519 +#define IMGUI_VERSION_NUM 18520 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE From 48f263336bc7651e0b648d9aaacd7828e31b23f8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Dec 2021 19:46:01 +0100 Subject: [PATCH 11/12] Menus: fixed closing a menu inside a popup/modal. Fixed menu inside a popup/modal not inhibiting hovering of items in the popup/modal. (#3496, #4797) Fixed sub-menu items inside a popups from closing the popup (debatable). --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 +- imgui.h | 2 +- imgui_demo.cpp | 21 ++++++++++++++++++--- imgui_widgets.cpp | 42 +++++++++++++++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 639a49f0..05fdaeee 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -68,7 +68,10 @@ Other Changes: - Nav: with ImGuiConfigFlags_NavEnableSetMousePos enabled: Fixed absolute mouse position when using Home/End leads to scrolling. Fixed not setting mouse position when a failed move request (e.g. when already at edge) reactivates the navigation highlight. +- Menus: fixed closing a menu inside a popup/modal by clicking on the popup/modal. (#3496, #4797) - Menus: fixed closing a menu by clicking on its menu-bar item when inside a popup. (#3496, #4797) [@xndcn] +- Menus: fixed menu inside a popup/modal not inhibiting hovering of items in the popup/modal. (#3496, #4797) +- Menus: fixed sub-menu items inside a popups from closing the popup. - InputText, Nav: fixed repeated calls to SetKeyboardFocusHere() preventing to use InputText(). (#4682) - Inputtext, Nav: fixed using SetKeyboardFocusHere() on InputTextMultiline(). (#4761) - InputText: made double-click select word, triple-line select line. Word delimitation logic differs diff --git a/imgui.cpp b/imgui.cpp index 39917cdb..71f7a477 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8705,7 +8705,7 @@ void ImGui::CloseCurrentPopup() ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; bool close_parent = false; if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) - if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) + if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar)) close_parent = true; if (!close_parent) break; diff --git a/imgui.h b/imgui.h index 73375295..3a33653f 100644 --- a/imgui.h +++ b/imgui.h @@ -64,7 +64,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18520 +#define IMGUI_VERSION_NUM 18521 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 97963ae1..b517aa2e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3345,11 +3345,26 @@ static void ShowDemoWindowPopups() } // Call the more complete ShowExampleMenuFile which we use in various places of this demo - if (ImGui::Button("File Menu..")) + if (ImGui::Button("With a menu..")) ImGui::OpenPopup("my_file_popup"); - if (ImGui::BeginPopup("my_file_popup")) + if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar)) { - ShowExampleMenuFile(); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Edit")) + { + ImGui::MenuItem("Dummy"); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + ImGui::Text("Hello from popup!"); + ImGui::Button("This is a dummy button.."); ImGui::EndPopup(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3cca2877..62151301 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6868,6 +6868,23 @@ void ImGui::EndMainMenuBar() End(); } +static bool IsRootOfOpenMenuSet() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) + return false; + + // Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID. + // This would however prevent the use of e.g. PuhsID() user code submitting menus. + // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, + // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. + // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup + // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. + const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; + return (upper_popup && /*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); +} + bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); @@ -6880,8 +6897,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + // The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) + if (window->Flags & ImGuiWindowFlags_ChildMenu) flags |= ImGuiWindowFlags_ChildWindow; // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). @@ -6900,11 +6918,12 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) g.MenusIdSubmittedThisFrame.push_back(id); ImVec2 label_size = CalcTextSize(label, NULL, true); - bool pressed; - bool menuset_is_open = (window->Flags & ImGuiWindowFlags_MenuBar) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + + // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) + const bool menuset_is_open = IsRootOfOpenMenuSet(); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + g.NavWindow = window; // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). @@ -6914,6 +6933,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) if (!enabled) BeginDisabled(); const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + bool pressed; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar @@ -7073,13 +7093,19 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut ImVec2 pos = window->DC.CursorPos; ImVec2 label_size = CalcTextSize(label, NULL, true); + const bool menuset_is_open = IsRootOfOpenMenuSet(); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) + g.NavWindow = window; + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. bool pressed; PushID(label); if (!enabled) BeginDisabled(); - const ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; + + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -7089,7 +7115,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - pressed = Selectable("", selected, flags, ImVec2(w, 0.0f)); + pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); RenderText(text_pos, label); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). @@ -7104,7 +7130,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable("", false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); if (icon_w > 0.0f) RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); @@ -7121,6 +7147,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut if (!enabled) EndDisabled(); PopID(); + if (menuset_is_open) + g.NavWindow = backed_nav_window; return pressed; } From 389982eb5afbb9f93873d87f1201842ccbc82dec Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 26 Aug 2021 17:10:58 +0200 Subject: [PATCH 12/12] Backends: OpenGL3: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.. (#4468, #4504, #2981, #3381) --- backends/imgui_impl_opengl3.cpp | 19 +++++++++++++++++-- backends/imgui_impl_opengl3_loader.h | 5 +++++ docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 5a802887..0ed8348d 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers. // 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions. // 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -174,6 +175,8 @@ struct ImGui_ImplOpenGL3_Data GLuint AttribLocationVtxUV; GLuint AttribLocationVtxColor; unsigned int VboHandle, ElementsHandle; + GLsizeiptr VertexBufferSize; + GLsizeiptr IndexBufferSize; bool HasClipOrigin; ImGui_ImplOpenGL3_Data() { memset(this, 0, sizeof(*this)); } @@ -425,8 +428,20 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) const ImDrawList* cmd_list = draw_data->CmdLists[n]; // Upload vertex/index buffers - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert); + GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx); + if (bd->VertexBufferSize < vtx_buffer_size) + { + bd->VertexBufferSize = vtx_buffer_size; + glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW); + } + if (bd->IndexBufferSize < idx_buffer_size) + { + bd->IndexBufferSize = idx_buffer_size; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW); + } + glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data); for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index 9313deda..8452301e 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -249,11 +249,13 @@ typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); #endif #endif /* GL_VERSION_1_5 */ #ifndef GL_VERSION_2_0 @@ -447,6 +449,7 @@ union GL3WProcs { PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate; PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate; PFNGLBUFFERDATAPROC BufferData; + PFNGLBUFFERSUBDATAPROC BufferSubData; PFNGLCLEARPROC Clear; PFNGLCLEARCOLORPROC ClearColor; PFNGLCOMPILESHADERPROC CompileShader; @@ -506,6 +509,7 @@ GL3W_API extern union GL3WProcs imgl3wProcs; #define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate #define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate #define glBufferData imgl3wProcs.gl.BufferData +#define glBufferSubData imgl3wProcs.gl.BufferSubData #define glClear imgl3wProcs.gl.Clear #define glClearColor imgl3wProcs.gl.ClearColor #define glCompileShader imgl3wProcs.gl.CompileShader @@ -692,6 +696,7 @@ static const char *proc_names[] = { "glBlendEquationSeparate", "glBlendFuncSeparate", "glBufferData", + "glBufferSubData", "glClear", "glClearColor", "glCompileShader", diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 05fdaeee..ecf781fa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -105,6 +105,8 @@ Other Changes: - Backends: Vulkan: Call vkCmdSetScissor() at the end of render with a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. (#4644) +- Backends: OpenGL3: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports + with some Intel HD drivers, and perhaps improve performances. (#4468, #4504, #2981, #3381) [@parbo] - Backends: OpenGL2, Allegro5, Marmalade: Fixed mishandling of the ImDrawCmd::IdxOffset field. This is an old bug, but due to the way we created drawlists, it never had any visible side-effect before. The new code for handling Modal and CTRL+Tab dimming/whitening recently made the bug surface. (#4790) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 62151301..cfdbb96e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6882,7 +6882,7 @@ static bool IsRootOfOpenMenuSet() // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; - return (upper_popup && /*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); + return (/*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); } bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)