From 929522febe5a379ece623d3730227964e72eb245 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 25 May 2018 12:51:24 +0200 Subject: [PATCH 01/16] Missing examples changelog bits and todo list --- TODO.txt | 6 ++++-- examples/opengl3_example/imgui_impl_glfw_gl3.cpp | 1 + examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index 19ff8d4d..0bf925e4 100644 --- a/TODO.txt +++ b/TODO.txt @@ -160,6 +160,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - popups: clicking outside (to close popup) and holding shouldn't drag window below. - popups: add variant using global identifier similar to Begin/End (#402) - popups: border options. richer api like BeginChild() perhaps? (#197) + - tooltip: drag and drop with tooltip near monitor edges lose/changes its last direction instead of locking one. The drag and drop tooltip should always follow without changing direction. - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred direction" and may teleport when moving mouse. - tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. - tooltip: allow tooltips with timers? or general timer policy? (instantaneous vs timed) @@ -225,7 +226,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i !- font: need handling of missing glyphs by not packing/rasterizing glyph 0 of a given font. - font: MergeMode: flags to select overwriting or not. - font: MergeMode: duplicate glyphs are stored in the atlas texture which is suboptimal. - - font: better vertical centering (based e.g on height of lowercase 'x'?). currently Roboto-Medium size 16 px isn't currently centered. (#1619) - font: free the Alpha buffer if user only requested RGBA. !- font: better CalcTextSizeA() API, at least for simple use cases. current one is horrible (perhaps have simple vs extended versions). - font: a CalcTextHeight() helper could run faster than CalcTextSize().y @@ -236,7 +236,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font/atlas: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. - font/atlas: allow user to submit its own primitive to be rectpacked, and allow to map them on a Unicode point. - font: MemoryTTF taking ownership confusing/not obvious, maybe default should be opposite? - - font/text: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise + - font draw: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise + - font draw: need to be able to specify wrap start position. - font: imgui_freetype.h alternative renderer (#618) - font: optimization: for monospace font (like the default one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance (need to make sure TAB is still correct). - font: add support for kerning, probably optional. A) perhaps default to (32..128)^2 matrix ~ 9K entries = 36KB, then hash for non-ascii?. B) or sparse lookup into per-char list? @@ -250,6 +251,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: allow input system to be be more tolerant of io.DeltaTime=0.0f - nav: ESC within a menu of a child window seems to exit the child window. - nav: ESC on a flattened child + - nav: init request doesn't select items that are part of a NavFlattened child - nav: Left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?) - nav: menus: pressing left-right on a vertically clipped menu bar tends to jump to the collapse/close buttons. - nav: menus: allow pressing Menu to leave a sub-menu. diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index a1e58a99..0971b1d4 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplGlfwGL3_Init() so user can override the GLSL version e.g. "#version 150". // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 8dee20e6..4a479936 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -15,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. // 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText). // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplSdlGL3_Init() so user can override the GLSL version e.g. "#version 150". From 0e83d74698fdf02dcd1e677d1fca7d70dc2a6334 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 27 May 2018 22:32:46 +0200 Subject: [PATCH 02/16] Documentation: FAQ, ID Stack, Fonts (#1839, #1840), #1842) --- imgui.cpp | 70 ++++++++++++++++++++++++++++--------------- misc/fonts/README.txt | 19 ++++++++---- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bdfefb40..afeebb86 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -448,26 +448,46 @@ It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc. At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render. Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing. - (C++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!) + + // The example OpenGL back-end uses integers to identify textures. + // You can safely store an integer into a void* by casting it. e.g. (void*)(intptr_t)MY_GL_UINT to cast to void*. + GLuint my_opengl_texture; + glGenTextures(1, &my_opengl_texture); + // [...] load image, render to texture, etc. + ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(512,512)); + + // The example DirectX11 back-end uses ID3D11ShaderResourceView* to identify textures. + ID3D11ShaderResourceView* my_texture_view; + device->CreateShaderResourceView(my_texture, &my_shader_resource_view_desc, &my_texture_view); + ImGui::Image((void*)my_texture_view, ImVec2(512,512)); + To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions. Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use. You may call ImGui::ShowMetricsWindow() to explore active draw lists and visualize/understand how the draw data is generated. It is your responsibility to get textures uploaded to your GPU. Q: How can I have multiple widgets with the same label or without a label? + Q: I have multiple widgets with the same label, and only the first one works. Why is that? A: A primer on labels and the ID Stack... - - Elements that are typically not clickable, such as Text() items don't need an ID. + Dear ImGui internally need to uniquely identify UI elements. + Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. + Interactive widgets (such as calls to Button buttons) need a unique ID. + Unique ID are used internally to track active widgets and occasionally associate state to widgets. + Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element. - - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui - often needs to remember what is the "active" widget). To do so they need a unique ID. Unique ID - are typically derived from a string label, an integer index or a pointer. + - Unique ID are often derived from a string label: - Button("OK"); // Label = "OK", ID = top of id stack + hash of "OK" - Button("Cancel"); // Label = "Cancel", ID = top of id stack + hash of "Cancel" + Button("OK"); // Label = "OK", ID = hash of (..., "OK") + Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel") - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having two buttons labeled "OK" in different windows or different tree locations is fine. + We used "..." above to signify whatever was already pushed to the ID stack previously: + + Begin("MyWindow"); + Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK") + End(); - If you have a same ID twice in the same location, you'll have a conflict: @@ -482,20 +502,22 @@ This helps solving the simple collision cases when you know e.g. at compilation time which items are going to be created: - Button("Play"); // Label = "Play", ID = top of id stack + hash of "Play" - Button("Play##foo1"); // Label = "Play", ID = top of id stack + hash of "Play##foo1" (different from above) - Button("Play##foo2"); // Label = "Play", ID = top of id stack + hash of "Play##foo2" (different from above) + Begin("MyWindow"); + Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play") + Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above + Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above + End(); - If you want to completely hide the label, but still need an ID: - Checkbox("##On", &b); // Label = "", ID = top of id stack + hash of "##On" (no label!) + Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label! - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels. For example you may want to include varying information in a window title bar, but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: - Button("Hello###ID"; // Label = "Hello", ID = top of id stack + hash of "ID" - Button("World###ID"; // Label = "World", ID = top of id stack + hash of "ID" (same as above) + Button("Hello###ID"; // Label = "Hello", ID = hash of (..., "ID") + Button("World###ID"; // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different sprintf(buf, "My game (%f FPS)###MyGame", fps); Begin(buf); // Variable label, ID = hash of "MyGame" @@ -507,45 +529,45 @@ You can push a pointer, a string or an integer value into the ID stack. Remember that ID are formed from the concatenation of _everything_ in the ID stack! + Begin("Window"); for (int i = 0; i < 100; i++) { - PushID(i); - Button("Click"); // Label = "Click", ID = top of id stack + hash of integer + hash of "Click" + PushID(i); // Push i to the id tack + Button("Click"); // Label = "Click", ID = Hash of ("Window", i, "Click") PopID(); } - for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj); - Button("Click"); // Label = "Click", ID = top of id stack + hash of pointer + hash of "Click" + Button("Click"); // Label = "Click", ID = Hash of ("Window", obj pointer, "Click") PopID(); } - for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj->Name); - Button("Click"); // Label = "Click", ID = top of id stack + hash of string + hash of "Click" + Button("Click"); // Label = "Click", ID = Hash of ("Window", obj->Name, "Click") PopID(); } + End(); - More example showing that you can stack multiple prefixes into the ID stack: - Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "Click") PushID("node"); - Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") PushID(my_ptr); - Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of ptr + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click") PopID(); PopID(); - Tree nodes implicitly creates a scope for you by calling PushID(). - Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "Click") if (TreeNode("node")) { - Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click" + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") TreePop(); } diff --git a/misc/fonts/README.txt b/misc/fonts/README.txt index cd51dd83..b42a0721 100644 --- a/misc/fonts/README.txt +++ b/misc/fonts/README.txt @@ -70,7 +70,7 @@ In this document: FONTS LOADING INSTRUCTIONS --------------------------------------- - Load default font with: + Load default font: ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontDefault(); @@ -78,15 +78,22 @@ In this document: Load .TTF/.OTF file with: ImGuiIO& io = ImGui::GetIO(); - io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); + ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels); + ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf", size_pixels); - For advanced options create a ImFontConfig structure and pass it to the AddFont function (it will be copied internally) + // Select font at runtime + ImGui::Text("Hello"); // use the default font (which is the first loaded font) + ImGui::PushFont(font2); + ImGui::Text("Hello with another font"); + ImGui::PopFont(); + + For advanced options create a ImFontConfig structure and pass it to the AddFont function (it will be copied internally): ImFontConfig config; config.OversampleH = 3; config.OversampleV = 1; config.GlyphExtraSpacing.x = 1.0f; - io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config); + ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config); If you have very large number of glyphs or multiple fonts: @@ -99,7 +106,7 @@ In this document: Combine two fonts into one: // Load a first font - io.Fonts->AddFontDefault(); + ImFont* font = io.Fonts->AddFontDefault(); // Add character ranges and merge into the previous font // The ranges array is not copied by the AddFont* functions and is used lazily @@ -144,7 +151,7 @@ In this document: --------------------------------------- You can use the ImFontAtlas::GlyphRangesBuilder helper to create glyph ranges based on text input. - For exemple: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. + For example: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs. ImVector ranges; ImFontAtlas::GlyphRangesBuilder builder; From f843facba4d012e225fb55b198001dbfcd0cc099 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 28 May 2018 15:30:42 +0200 Subject: [PATCH 03/16] Internals: PushItemFlag() flags are inherited by BeginChild(). --- CHANGELOG.txt | 1 + imgui.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index c570a948..4a883c8a 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -49,6 +49,7 @@ Other Changes: - BeginDragDropSource(): Offset tooltip position so it is off the mouse cursor, but also closer to it than regular tooltips, and not clamped by viewport. (#1739) - BeginCombo(), BeginMainMenuBar(), BeginChildFrame(): Temporary style modification are restored at the end of BeginXXX instead of EndXXX, to not affect tooltips and child windows. - Examples: GLFW: Made it possible to Shutdown/Init the backend again (by reseting the time storage properly). (#1827) [@ice1000] + - Internals: PushItemFlag() flags are inherited by BeginChild(). ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index afeebb86..4bacd9b9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6250,7 +6250,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.ItemFlags = ImGuiItemFlags_Default_; + window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled window->DC.ItemFlagsStack.resize(0); From 7fd9199a1dec1e7d0d5d26f14b7e328a497f924f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 24 May 2018 19:10:40 +0200 Subject: [PATCH 04/16] Internals: Selectable: Renamed variables. Todo update. --- TODO.txt | 2 ++ imgui.cpp | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/TODO.txt b/TODO.txt index 0bf925e4..d6c471d2 100644 --- a/TODO.txt +++ b/TODO.txt @@ -215,6 +215,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - filters: handle wildcards (with implicit leading/trailing *), regexps - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) + - drag and drop: allow drag target to alter tooltip / and or disable tooltip. - drag and drop: add demo. (#143, #479) - drag and drop: allow using with other mouse buttons (where activeid won't be set). (#1637) - drag and drop: test with reordering nodes (in a list, or a tree node). (#143) @@ -296,6 +297,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - demo: demonstration Plot offset - examples: window minimize, maximize (#583) - examples: provide a zero-framerate/idle example. + - examples: apple: apple_example should be using modern GL3. - examples: glfw: could go idle when minimized? if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { glfwWaitEvents(); continue; } // issue: DeltaTime will be super high on resume, perhaps provide a way to let impl know (#440) - optimization: replace vsnprintf with stb_printf? or enable the defines/infrastructure to allow it (#1038) - optimization: add clipping for multi-component widgets (SliderFloatX, ColorEditX, etc.). one problem is that nav branch can't easily clip parent group when there is a move request. diff --git a/imgui.cpp b/imgui.cpp index 4bacd9b9..f73a966c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11221,28 +11221,28 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); ImVec2 pos = window->DC.CursorPos; pos.y += window->DC.CurrentLineTextBaseOffset; - ImRect bb(pos, pos + size); - ItemSize(bb); + ImRect bb_inner(pos, pos + size); + ItemSize(bb_inner); // Fill horizontal space. ImVec2 window_padding = window->WindowPadding; float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); - ImRect bb_with_spacing(pos, pos + size_draw); + ImRect bb(pos, pos + size_draw); if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) - bb_with_spacing.Max.x += window_padding.x; + bb.Max.x += window_padding.x; // Selectables are tightly packed together, we extend the box to cover spacing between selectable. float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); float spacing_R = style.ItemSpacing.x - spacing_L; float spacing_D = style.ItemSpacing.y - spacing_U; - bb_with_spacing.Min.x -= spacing_L; - bb_with_spacing.Min.y -= spacing_U; - bb_with_spacing.Max.x += spacing_R; - bb_with_spacing.Max.y += spacing_D; - if (!ItemAdd(bb_with_spacing, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) + bb.Min.x -= spacing_L; + bb.Min.y -= spacing_U; + bb.Max.x += spacing_R; + bb.Max.y += spacing_D; + if (!ItemAdd(bb, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) { if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) PushColumnClipRect(); @@ -11257,7 +11257,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; bool hovered, held; - bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags); + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (flags & ImGuiSelectableFlags_Disabled) selected = false; @@ -11273,18 +11273,18 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (hovered || selected) { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f); - RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) { PushColumnClipRect(); - bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x); + bb.Max.x -= (GetContentRegionMax().x - max_x); } if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); + RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups From 92b7d6bc4fe3ecc33a7fdb93046ecca0eacf6dd4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 28 May 2018 20:50:52 +0200 Subject: [PATCH 05/16] Added ImGuiDragDropFlags_AcceptNoPreviewTooltip flag to disable drag source tooltip from the target site (#143) --- CHANGELOG.txt | 1 + TODO.txt | 4 +++- imgui.cpp | 11 +++++++++++ imgui.h | 1 + imgui_internal.h | 4 +++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4a883c8a..df79549b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -47,6 +47,7 @@ Other Changes: - TreeNode: Fixed nodes with ImGuiTreeNodeFlags_Leaf flag always returning true which was meaningless. - ColorEdit3, ColorEdit4, ColorButton: Added ImGuiColorEditFlags_NoDragDrop flag to disable ColorEditX as drag target and ColorButton as drag source. (#1826) - BeginDragDropSource(): Offset tooltip position so it is off the mouse cursor, but also closer to it than regular tooltips, and not clamped by viewport. (#1739) + - BeginDragDropTarget(): Added ImGuiDragDropFlags_AcceptNoPreviewTooltip flag to request hiding the drag source tooltip from the target site. (#143) - BeginCombo(), BeginMainMenuBar(), BeginChildFrame(): Temporary style modification are restored at the end of BeginXXX instead of EndXXX, to not affect tooltips and child windows. - Examples: GLFW: Made it possible to Shutdown/Init the backend again (by reseting the time storage properly). (#1827) [@ice1000] - Internals: PushItemFlag() flags are inherited by BeginChild(). diff --git a/TODO.txt b/TODO.txt index d6c471d2..8798f225 100644 --- a/TODO.txt +++ b/TODO.txt @@ -215,11 +215,13 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - filters: handle wildcards (with implicit leading/trailing *), regexps - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) - - drag and drop: allow drag target to alter tooltip / and or disable tooltip. + - drag and drop: allow drag target to alter tooltip. - drag and drop: add demo. (#143, #479) + - drag and drop: have some way to know when a drag begin from BeginDragDropSource() pov - drag and drop: allow using with other mouse buttons (where activeid won't be set). (#1637) - drag and drop: test with reordering nodes (in a list, or a tree node). (#143) - drag and drop: test integrating with os drag and drop. + - drag and drop: make payload optional? (#143) - node/graph editor (#306) - pie menus patterns (#434) - markup: simple markup language for color change? (#902) diff --git a/imgui.cpp b/imgui.cpp index f73a966c..a09f3fdf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13145,6 +13145,7 @@ void ImGui::ClearDragDrop() ImGuiContext& g = *GImGui; g.DragDropActive = false; g.DragDropPayload.Clear(); + g.DragDropAcceptFlags = 0; g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; g.DragDropAcceptIdCurrRectSurface = FLT_MAX; g.DragDropAcceptFrameCount = -1; @@ -13239,6 +13240,15 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) SetNextWindowPos(tooltip_pos); PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f)); BeginTooltip(); + + // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) + // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. + if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) + { + ImGuiWindow* tooltip_window = g.CurrentWindow; + tooltip_window->SkipItems = true; + tooltip_window->HiddenFrames = 1; + } } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) @@ -13379,6 +13389,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop float r_surface = r.GetWidth() * r.GetHeight(); if (r_surface < g.DragDropAcceptIdCurrRectSurface) { + g.DragDropAcceptFlags = flags; g.DragDropAcceptIdCurr = g.DragDropTargetId; g.DragDropAcceptIdCurrRectSurface = r_surface; } diff --git a/imgui.h b/imgui.h index 5989164b..f580ce8b 100644 --- a/imgui.h +++ b/imgui.h @@ -717,6 +717,7 @@ enum ImGuiDragDropFlags_ // AcceptDragDropPayload() flags ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. + ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site. ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect // For peeking ahead and inspecting the payload before delivery. }; diff --git a/imgui_internal.h b/imgui_internal.h index 92c56731..ab665794 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -688,7 +688,8 @@ struct ImGuiContext ImGuiPayload DragDropPayload; ImRect DragDropTargetRect; ImGuiID DragDropTargetId; - float DragDropAcceptIdCurrRectSurface; + ImGuiDragDropFlags DragDropAcceptFlags; + float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload) ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets) int DragDropAcceptFrameCount; // Last time a target expressed a desire to accept the source @@ -799,6 +800,7 @@ struct ImGuiContext DragDropSourceFlags = 0; DragDropMouseButton = -1; DragDropTargetId = 0; + DragDropAcceptFlags = 0; DragDropAcceptIdCurrRectSurface = 0.0f; DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; DragDropAcceptFrameCount = -1; From 3218666fb992d522f571160128591f299b6f65eb Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 28 May 2018 21:05:08 +0200 Subject: [PATCH 06/16] Made drag source tooltip display at the same position as a regular tooltip to avoid discontinuity where dynamically swapping tooltip at the target site. Made drag source tooltip override previous tooltip if any. (#1739, #143). --- imgui.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a09f3fdf..8fb4064c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13230,16 +13230,8 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { - // FIXME-DRAG - //SetNextWindowPos(g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding); - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This is better but e.g ColorButton with checkboard has issue with transparent colors :( - - // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) - // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. - ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); - SetNextWindowPos(tooltip_pos); - PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f)); - BeginTooltip(); + SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); + BeginTooltipEx(0, true); // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. @@ -13265,11 +13257,7 @@ void ImGui::EndDragDropSource() IM_ASSERT(g.DragDropActive); if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - { EndTooltip(); - PopStyleColor(); - //PopStyleVar(); - } // Discard the drag if have not called SetDragDropPayload() if (g.DragDropPayload.DataFrameCount == -1) From d4b151076c7207029c3cd7c88ba1f96abc58430a Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 28 May 2018 21:34:32 +0200 Subject: [PATCH 07/16] Internals: Added BeginDragDropTooltip() internal function to convey semantic (drag and drop tooltip doesn't get clipped within display boundaries). Revert part of 3218666fb992d522f571160128591f299b6f65eb. (#1739, #143). --- TODO.txt | 3 +-- imgui.cpp | 25 +++++++++++++++++++++---- imgui_internal.h | 2 ++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/TODO.txt b/TODO.txt index 8798f225..0ad2c630 100644 --- a/TODO.txt +++ b/TODO.txt @@ -163,7 +163,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tooltip: drag and drop with tooltip near monitor edges lose/changes its last direction instead of locking one. The drag and drop tooltip should always follow without changing direction. - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred direction" and may teleport when moving mouse. - tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. - - tooltip: allow tooltips with timers? or general timer policy? (instantaneous vs timed) + - tooltip: tooltips with delay timers? or general timer policy? (instantaneous vs timed) (#1485) - menus: calling BeginMenu() twice with a same name doesn't append as Begin() does for regular windows (#1207) - menus: menu bars inside modals windows are acting weird. @@ -215,7 +215,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - filters: handle wildcards (with implicit leading/trailing *), regexps - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) - - drag and drop: allow drag target to alter tooltip. - drag and drop: add demo. (#143, #479) - drag and drop: have some way to know when a drag begin from BeginDragDropSource() pov - drag and drop: allow using with other mouse buttons (where activeid won't be set). (#1637) diff --git a/imgui.cpp b/imgui.cpp index 8fb4064c..3794e6d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13230,11 +13230,9 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { - SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); - BeginTooltipEx(0, true); - // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. + BeginDragDropTooltip(); if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { ImGuiWindow* tooltip_window = g.CurrentWindow; @@ -13257,13 +13255,32 @@ void ImGui::EndDragDropSource() IM_ASSERT(g.DragDropActive); if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - EndTooltip(); + EndDragDropTooltip(); // Discard the drag if have not called SetDragDropPayload() if (g.DragDropPayload.DataFrameCount == -1) ClearDragDrop(); } +void ImGui::BeginDragDropTooltip() +{ + // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) + // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. + // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. + ImGuiContext& g = *GImGui; + //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; + ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); + SetNextWindowPos(tooltip_pos); + SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); + //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( + BeginTooltipEx(0, true); +} + +void ImGui::EndDragDropTooltip() +{ + EndTooltip(); +} + // Use 'cond' to choose to submit payload on drag start or every frame bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) { diff --git a/imgui_internal.h b/imgui_internal.h index ab665794..96824654 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1111,6 +1111,8 @@ namespace ImGui IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); + IMGUI_API void BeginDragDropTooltip(); + IMGUI_API void EndDragDropTooltip(); // FIXME-WIP: New Columns API IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiColumnsFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). From 08e20ae4653a603ebc581c8900a4fe90f3101697 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 5 Jan 2018 15:33:24 +0100 Subject: [PATCH 08/16] Internals: Reintroducing LastActiveIdTimer because it is useful and to reduce merge conflicts. (#1537) --- imgui.cpp | 8 ++++++++ imgui_internal.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 3794e6d6..4aa51240 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2115,7 +2115,14 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) ImGuiContext& g = *GImGui; g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) + { g.ActiveIdTimer = 0.0f; + if (id != 0) + { + g.LastActiveId = id; + g.LastActiveIdTimer = 0.0f; + } + } g.ActiveId = id; g.ActiveIdAllowNavDirFlags = 0; g.ActiveIdAllowOverlap = false; @@ -3568,6 +3575,7 @@ void ImGui::NewFrame() ClearActiveID(); if (g.ActiveId) g.ActiveIdTimer += g.IO.DeltaTime; + g.LastActiveIdTimer += g.IO.DeltaTime; g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = false; g.ActiveIdIsJustActivated = false; diff --git a/imgui_internal.h b/imgui_internal.h index 96824654..5c441706 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -627,6 +627,8 @@ struct ImGuiContext ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) + ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. + float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() @@ -762,6 +764,8 @@ struct ImGuiContext ActiveIdClickOffset = ImVec2(-1,-1); ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; + LastActiveId = 0; + LastActiveIdTimer = 0.0f; MovingWindow = NULL; NextTreeNodeOpenVal = false; NextTreeNodeOpenCond = 0; From a2198bcf6b2f8f80759a4e2ea2db27b3761c17bc Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 May 2018 10:47:18 +0200 Subject: [PATCH 09/16] Made mouse drag distance calculation more robust to invalid mouse position. (#1845) --- imgui.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4aa51240..5a673511 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3418,7 +3418,8 @@ static void ImGui::UpdateMouseInputs() { if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime) { - if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) + ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) g.IO.MouseDoubleClicked[i] = true; g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click } @@ -3432,10 +3433,11 @@ static void ImGui::UpdateMouseInputs() } else if (g.IO.MouseDown[i]) { - ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i]; - g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x); - g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y); - g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta)); + // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold + ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); + g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); + g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation g.NavDisableMouseHover = false; From 0903a12c2aeb959dcd14b2cf9cb783143a0f1012 Mon Sep 17 00:00:00 2001 From: "Marc J. Schmidt" Date: Wed, 30 May 2018 10:28:27 +0200 Subject: [PATCH 10/16] Fixed documentation example (#1847) --- misc/freetype/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/freetype/README.md b/misc/freetype/README.md index 4038dbcd..ba633f69 100644 --- a/misc/freetype/README.md +++ b/misc/freetype/README.md @@ -12,7 +12,7 @@ By [Vuhdo](https://github.com/Vuhdo) (Aleksei Skriabin). Improvements by @mikesa ```cpp // See ImGuiFreeType::RasterizationFlags -unsigned int flags = ImGuiFreeType::DisableHinting; +unsigned int flags = ImGuiFreeType::NoHinting; ImGuiFreeType::BuildFontAtlas(io.Fonts, flags); io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); ``` From 948009a8b2e98ef35fb8ddfe19299535a16c0834 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 30 May 2018 16:31:34 +0200 Subject: [PATCH 11/16] Intensive FAQ answer for the million of people asking the same questions over and over again. (#1848, #1791, #1840, #1493, #1295) --- imgui.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++----------- imgui.h | 2 +- 2 files changed, 81 insertions(+), 21 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5a673511..07f0b183 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -428,7 +428,7 @@ ====================================== Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application? - A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure. + A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } ) - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application. - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application. - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS). @@ -443,28 +443,88 @@ were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.) Q: How can I display an image? What is ImTextureID, how does it works? - A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function. - Dear ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry! - It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc. - At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render. - Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing. + A: Short explanation: + - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures. + - Actual textures are identified in a way that is up to the user/engine. + - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason). + Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward. - // The example OpenGL back-end uses integers to identify textures. - // You can safely store an integer into a void* by casting it. e.g. (void*)(intptr_t)MY_GL_UINT to cast to void*. - GLuint my_opengl_texture; - glGenTextures(1, &my_opengl_texture); - // [...] load image, render to texture, etc. - ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(512,512)); + Long explanation: + - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices. + At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code + to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.). + - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API. + We carry the information to identify a "texture" in the ImTextureID type. + ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice. + Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function. + - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying + an image from the end-user perspective. This is what the _examples_ rendering functions are using: - // The example DirectX11 back-end uses ID3D11ShaderResourceView* to identify textures. - ID3D11ShaderResourceView* my_texture_view; - device->CreateShaderResourceView(my_texture, &my_shader_resource_view_desc, &my_texture_view); - ImGui::Image((void*)my_texture_view, ImVec2(512,512)); + OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp) + DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp) + DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp) + DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp) - To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions. - Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use. - You may call ImGui::ShowMetricsWindow() to explore active draw lists and visualize/understand how the draw data is generated. - It is your responsibility to get textures uploaded to your GPU. + For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID. + Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure + tying together both the texture and information about its format and how to read it. + - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about + the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase + is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them. + If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID + representation suggested by the example bindings is probably the best choice. + (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer) + + User code may do: + + // Cast our texture type to ImTextureID / void* + MyTexture* texture = g_CoffeeTableTexture; + ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height)); + + The renderer function called after ImGui::Render() will receive that same value that the user code passed: + + // Cast ImTextureID / void* stored in the draw command as our texture type + MyTexture* texture = (MyTexture*)pcmd->TextureId; + MyEngineBindTexture2D(texture); + + Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui. + This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them. + If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using. + + Here's a simplified OpenGL example using stb_image.h: + + // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data: + #define STB_IMAGE_IMPLEMENTATION + #include + [...] + int my_image_width, my_image_height; + unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4); + + // Turn the RGBA pixel data into an OpenGL texture: + GLuint my_opengl_texture; + glGenTextures(1, &my_opengl_texture); + glBindTexture(GL_TEXTURE_2D, image_tex); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); + + // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it: + ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height)); + + C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa. + Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*. + Examples: + + GLuint my_tex = XXX; + void* my_void_ptr; + my_void_ptr = (void*)(intptr_t)my_tex; // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer) + my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint + + ID3D11ShaderResourceView* my_dx11_srv = XXX; + void* my_void_ptr; + my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void* + my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView* + + Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated. Q: How can I have multiple widgets with the same label or without a label? Q: I have multiple widgets with the same label, and only the first one works. Why is that? diff --git a/imgui.h b/imgui.h index f580ce8b..24c24e89 100644 --- a/imgui.h +++ b/imgui.h @@ -54,7 +54,7 @@ struct ImDrawChannel; // Temporary storage for outputting drawing commands out of order, used by ImDrawList::ChannelsSplit() struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call) struct ImDrawData; // All draw command lists required to render the frame -struct ImDrawList; // A single draw command list (generally one per window) +struct ImDrawList; // A single draw command list (generally one per window, conceptually you may see this as a dynamic "mesh" builder) struct ImDrawListSharedData; // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself) struct ImDrawVert; // A single vertex (20 bytes by default, override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) struct ImFont; // Runtime data for a single font within a parent ImFontAtlas From 5a7e98c7cf493fc88f7850bdc89c6ac75fa88ed6 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 4 Jun 2018 14:44:19 +0200 Subject: [PATCH 12/16] Internals: Nav: Removed ParentID from move result (unused, would need to be full-filled for PageUp/PageDown code) + FAQ typo fix. --- imgui.cpp | 3 +-- imgui_internal.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 07f0b183..f227fbd3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -503,7 +503,7 @@ // Turn the RGBA pixel data into an OpenGL texture: GLuint my_opengl_texture; glGenTextures(1, &my_opengl_texture); - glBindTexture(GL_TEXTURE_2D, image_tex); + glBindTexture(GL_TEXTURE_2D, my_opengl_texture); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); @@ -2541,7 +2541,6 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con if (new_best) { result->ID = id; - result->ParentID = window->IDStack.back(); result->Window = window; result->RectRel = nav_bb_rel; } diff --git a/imgui_internal.h b/imgui_internal.h index 5c441706..9c61c814 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -538,7 +538,6 @@ struct ImDrawDataBuilder struct ImGuiNavMoveResult { ImGuiID ID; // Best candidate - ImGuiID ParentID; // Best candidate window->IDStack.back() - to compare context ImGuiWindow* Window; // Best candidate window float DistBox; // Best candidate box distance to current NavId float DistCenter; // Best candidate center distance to current NavId @@ -546,7 +545,7 @@ struct ImGuiNavMoveResult ImRect RectRel; // Best candidate bounding box in window relative space ImGuiNavMoveResult() { Clear(); } - void Clear() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } + void Clear() { ID = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } }; // Storage for SetNexWindow** functions From f4120e20d5256bd4e985b5192ee883df778cb9dc Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 4 Jun 2018 16:30:42 +0200 Subject: [PATCH 13/16] Nav: NavFlattened: Fixed navigation miscrolling parent window when the current window is scrolling enough to keep the item in view. Fix feature added in e11610d6, typically affect large navigation steps (used by PageUp/PageDown). + comments (#787) --- TODO.txt | 4 +++- imgui.cpp | 16 ++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/TODO.txt b/TODO.txt index 0ad2c630..904e6611 100644 --- a/TODO.txt +++ b/TODO.txt @@ -253,7 +253,9 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: allow input system to be be more tolerant of io.DeltaTime=0.0f - nav: ESC within a menu of a child window seems to exit the child window. - nav: ESC on a flattened child - - nav: init request doesn't select items that are part of a NavFlattened child + - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child wouldn't be considered to enter into a NavFlattened child. + - nav: NavFlattened: broken: can accidentally enter a NavFlattened child as child items are scored outside of child visible bounds. + - nav: NavFlattened: init request doesn't select items that are part of a NavFlattened child - nav: Left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?) - nav: menus: pressing left-right on a vertically clipped menu bar tends to jump to the collapse/close buttons. - nav: menus: allow pressing Menu to leave a sub-menu. diff --git a/imgui.cpp b/imgui.cpp index f227fbd3..65dd64b7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3182,23 +3182,27 @@ static void ImGui::NavUpdate() { // Select which result to use ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) // Maybe entering a flattened child? In this case solve the tie using the regular scoring rules + + // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules + if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter)) result = &g.NavMoveResultOther; - IM_ASSERT(g.NavWindow && result->Window); - // Scroll to keep newly navigated item fully into view. Also scroll parent window if necessary. + // Scroll to keep newly navigated item fully into view. if (g.NavLayer == 0) { ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); NavScrollToBringItemIntoView(result->Window, rect_abs); - if (result->Window->Flags & ImGuiWindowFlags_ChildWindow) - NavScrollToBringItemIntoView(result->Window->ParentWindow, rect_abs); // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate() ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false); - result->RectRel.Translate(result->Window->Scroll - next_scroll); + ImVec2 delta_scroll = result->Window->Scroll - next_scroll; + result->RectRel.Translate(delta_scroll); + + // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy). + if (result->Window->Flags & ImGuiWindowFlags_ChildWindow) + NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll)); } // Apply result from previous frame navigation directional move request From c665c15a7d5942037852027cec88de9ccc2494ac Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 4 Jun 2018 17:10:06 +0200 Subject: [PATCH 14/16] Nav: When entering into a NavFlattened child we only consider the visible items for scoring (note that this only work assuming the NavFlattened child window has interactive items). Fixes accidentally hoping into a NavFlattened child. (#767) --- TODO.txt | 3 +-- imgui.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index 904e6611..4fe013cd 100644 --- a/TODO.txt +++ b/TODO.txt @@ -253,8 +253,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: allow input system to be be more tolerant of io.DeltaTime=0.0f - nav: ESC within a menu of a child window seems to exit the child window. - nav: ESC on a flattened child - - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child wouldn't be considered to enter into a NavFlattened child. - - nav: NavFlattened: broken: can accidentally enter a NavFlattened child as child items are scored outside of child visible bounds. + - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child are currently not considered to enter into a NavFlattened child. - nav: NavFlattened: init request doesn't select items that are part of a NavFlattened child - nav: Left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?) - nav: menus: pressing left-right on a vertically clipped menu bar tends to jump to the collapse/close buttons. diff --git a/imgui.cpp b/imgui.cpp index 65dd64b7..c6df6408 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2341,6 +2341,15 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) g.NavScoringCount++; + if (window != g.NavWindow) + { + // When crossing through a NavFlattened border, we score items on the other windows fully clipped + IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); + if (!window->ClipRect.Contains(cand)) + return false; + cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window + } + // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) // For example, this ensure that items in one column are not reached when moving vertically from items in another column. NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); From 2bdf0b54a2c364fbb4d79db5ba13bd1359593037 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Jun 2018 13:39:42 +0200 Subject: [PATCH 15/16] Changelog formatting update, Todo, comments. Tweak Child demo. Shuffle some code in NavUpdate(). --- CHANGELOG.txt | 69 ++++++++++++++++++++++++++++---------------------- TODO.txt | 4 +-- imgui.cpp | 20 +++++++++------ imgui_demo.cpp | 5 ++-- 4 files changed, 55 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index df79549b..4e9e80a7 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,28 +1,19 @@ dear imgui CHANGELOG ------------------------------------------------------------------------ +This document holds the user-facing changelog that we also use in release notes. +We generally fold multiple commits pertaining to the same topic as a single entry. +Changes to the examples/bindings are included within the individual .cpp files in the examples/ folder. -This document holds the programmer changelog that we also use in release notes. -We generally fold multiple commits pertaining to the same topic as a single entry, and simplify various things. - -Release notes: (with links and screenshots) - https://github.com/ocornut/imgui/releases - -Changes to the examples/bindings are included within the individual .cpp files in examples. - -Individual commits: - https://github.com/ocornut/imgui/commits/master - -Report issues, ask questions: - https://github.com/ocornut/imgui/issues - ------------------------------------------------------------------------ +RELEASE NOTES: https://github.com/ocornut/imgui/releases +REPORT ISSUES, ASK QUESTIONS: https://github.com/ocornut/imgui/issues +COMMITS HISTORY: https://github.com/ocornut/imgui/commits/master WHEN TO UPDATE? -It is generally safe to sync to the latest commit in master. The library is fairly stable and regressions tends to be fixed fast when reported. -Keeping your copy of dear imgui updated once in a while is recommended. +- Keeping your copy of dear imgui updated once in a while is recommended. +- It is generally safe to sync to the latest commit in master. + The library is fairly stable and regressions tends to be fixed fast when reported. HOW TO UPDATE? @@ -32,12 +23,15 @@ HOW TO UPDATE? - If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it. - If you are dropping this repository in your codebase, please leave the demo and text files in there, they will be useful. - You may diff your previous Changelog with the one you just copied and read that diff. -- You may enable `IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in imconfig.h to forcefully disable legacy names and symbols. Doing it every once in a while is a good way to make sure you are not using obsolete symbols. Dear ImGui is in active development API updates have a little more frequent lately. They are carefully documented and should not affect all users. +- You may enable `IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in imconfig.h to forcefully disable legacy names and symbols. + Doing it every once in a while is a good way to make sure you are not using obsolete symbols. Dear ImGui is in active development, + and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! ------------------------------------------------------------------------ -VERSION 1.62 WIP (IN PROGRESS) +----------------------------------------------------------------------- + VERSION 1.62 WIP (IN PROGRESS) +----------------------------------------------------------------------- Breaking Changes: @@ -52,9 +46,10 @@ Other Changes: - Examples: GLFW: Made it possible to Shutdown/Init the backend again (by reseting the time storage properly). (#1827) [@ice1000] - Internals: PushItemFlag() flags are inherited by BeginChild(). ------------------------------------------------------------------------ -VERSION 1.61 (Released 2018-05-14) +----------------------------------------------------------------------- + VERSION 1.61 (Released 2018-05-14) +----------------------------------------------------------------------- Breaking Changes: @@ -110,9 +105,11 @@ Other Changes: - Examples: SDL: Fixed clipboard paste memory leak in the SDL binding code. (#1803) [@eliasdaler] - Various minor fixes, tweaks, refactoring, comments. + +----------------------------------------------------------------------- + VERSION 1.60 (Released 2018-04-07) ----------------------------------------------------------------------- -VERSION 1.60 (Released 2018-04-07) Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.60 The gamepad/keyboard navigation branch (which has been in the work since July 2016) has been merged. @@ -271,9 +268,11 @@ Other Changes: - Examples: Visual Studio: Disabled extraneous function-level check in Release build. - Various fixes, tweaks, internal refactoring, optimizations, comments. + +----------------------------------------------------------------------- + VERSION 1.53 (Released 2017-12-25) ----------------------------------------------------------------------- -VERSION 1.53 (Released 2017-12-25) Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.53 Breaking Changes: @@ -409,9 +408,11 @@ Other Changes: - Fix for using alloca() in "Clang with Microsoft Codechain" mode. - Various fixes, optimizations, comments. + +----------------------------------------------------------------------- + VERSION 1.52 (2017-10-27) ----------------------------------------------------------------------- -VERSION 1.52 (2017-10-27) Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.52 Breaking Changes: @@ -510,9 +511,11 @@ Beta Navigation Branch: - Nav: More consistently drawing a (thin) navigation rectangle hover filled frames such as tree nodes, collapsing header, menus. (#787) - Nav: Various internal refactoring. + +----------------------------------------------------------------------- + VERSION 1.51 (2017-08-24) ----------------------------------------------------------------------- -VERSION 1.51 (2017-08-24) Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.51 Breaking Changes: @@ -572,9 +575,11 @@ Other Changes: - Examples: Enabled vsync by default in example applications, so it doesn't confuse people that the sample run at 2000+ fps and waste an entire CPU. (#1213, #1151). - Various other small fixes, tweaks, comments, optimizations. + +----------------------------------------------------------------------- + VERSION 1.50 (2017-06-02) ----------------------------------------------------------------------- -VERSION 1.50 (2017-06-02) Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.50 Breaking Changes: @@ -666,9 +671,11 @@ Other Changes: - Added various links to language/engine bindings. - Various other minor fixes, tweaks, comments, optimizations. + +----------------------------------------------------------------------- + VERSION 1.49 (2016-05-09) ----------------------------------------------------------------------- -VERSION 1.49 (2016-05-09) Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.49 Breaking Changes: @@ -742,9 +749,11 @@ Other changes: - Examples: SDL: Initialize video+timer subsystem only. - Examples: Apple/iOS: lowered XCode project deployment target from 10.7 to 10.11. (#598, #575) + +----------------------------------------------------------------------- + VERSION 1.48 (2016-04-09) ----------------------------------------------------------------------- -VERSION 1.48 (2016-04-09) Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.48 Breaking Changes: diff --git a/TODO.txt b/TODO.txt index 4fe013cd..e10b341e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -252,16 +252,16 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: SetItemDefaultFocus() level of priority, so widget like Selectable when inside a popup could claim a low-priority default focus on the first selected iem - nav: allow input system to be be more tolerant of io.DeltaTime=0.0f - nav: ESC within a menu of a child window seems to exit the child window. - - nav: ESC on a flattened child + - nav: NavFlattened: ESC on a flattened child should select something. - nav: NavFlattened: broken: in typical usage scenario, the items of a fully clipped child are currently not considered to enter into a NavFlattened child. - nav: NavFlattened: init request doesn't select items that are part of a NavFlattened child + - nav: NavFlattened: cannot access menubar of a flattened child window with Alt/menu key (not a very common use case..). - nav: Left within a tree node block as a fallback (ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default?) - nav: menus: pressing left-right on a vertically clipped menu bar tends to jump to the collapse/close buttons. - nav: menus: allow pressing Menu to leave a sub-menu. - nav: simulate right-click or context activation? (SHIFT+F10) - nav: tabs should go through most/all widgets (in submission order?). - nav: when CTRL-Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering. - - nav: cannot access menubar of a flattened child window with Alt/menu key (not a very common use case..). - nav: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys. - nav: when activating a button that changes label (without a static ID) or disappear, can we somehow automatically recover into a nearest highlight item? - nav: there's currently no way to completely clear focus with the keyboard. depending on patterns used by the application to dispatch inputs, it may be desirable. diff --git a/imgui.cpp b/imgui.cpp index c6df6408..3c0853ce 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2519,6 +2519,8 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con const ImGuiItemFlags item_flags = window->DC.ItemFlags; const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + + // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback @@ -2534,7 +2536,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con } } - // Scoring for navigation + // Process Move Request (scoring for navigation) // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) { @@ -3104,10 +3106,10 @@ static void ImGui::NavUpdateWindowing() } } +// Scroll to keep newly navigated item fully into view // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) { - // Scroll to keep newly navigated item fully into view ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1)); //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] if (window_rect.Contains(item_rect)) @@ -3145,12 +3147,16 @@ static void ImGui::NavUpdate() if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad)) + bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + + // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) + if (nav_gamepad_active) if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) g.NavInputSource = ImGuiInputSource_NavGamepad; // Update Keyboard->Nav inputs mapping - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) + if (nav_keyboard_active) { #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); @@ -3256,8 +3262,6 @@ static void ImGui::NavUpdate() NavUpdateWindowing(); // Set output flags for user application - bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; @@ -3369,7 +3373,7 @@ static void ImGui::NavUpdate() // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { - // *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item + // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) @@ -13372,7 +13376,7 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s cond = ImGuiCond_Always; IM_ASSERT(type != NULL); - IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 12 characters long"); + IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1d7d7a20..1ec1404b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1221,11 +1221,10 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Columns(2); for (int i = 0; i < 100; i++) { - if (i == 50) - ImGui::NextColumn(); char buf[32]; - sprintf(buf, "%08x", i*5731); + sprintf(buf, "%03d", i); ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); + ImGui::NextColumn(); } ImGui::EndChild(); ImGui::PopStyleVar(); From 6d98c0323bd37f276c34d9696a79332b9c66d955 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Jun 2018 15:49:33 +0200 Subject: [PATCH 16/16] Nav: Added support for PageUp/PageDown (explorer-style: first aim at bottom/top most item, when scroll a page worth of contents). (#787) --- CHANGELOG.txt | 1 + imgui.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++---- imgui_internal.h | 13 +++++---- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4e9e80a7..4f96c0a4 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -38,6 +38,7 @@ Breaking Changes: Other Changes: - Nav: To keep the navigated item in view we also attempt to scroll the parent window as well as the current window. (#787) + - Nav: Added support for PageUp/PageDown (explorer-style: first aim at bottom/top most item, when scroll a page worth of contents). (#787) - TreeNode: Fixed nodes with ImGuiTreeNodeFlags_Leaf flag always returning true which was meaningless. - ColorEdit3, ColorEdit4, ColorButton: Added ImGuiColorEditFlags_NoDragDrop flag to disable ColorEditX as drag target and ColorButton as drag source. (#1826) - BeginDragDropSource(): Offset tooltip position so it is off the mouse cursor, but also closer to it than regular tooltips, and not clamped by viewport. (#1739) diff --git a/imgui.cpp b/imgui.cpp index 3c0853ce..88a282dd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2538,7 +2538,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // Process Move Request (scoring for navigation) // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) + if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav)) { ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; #if IMGUI_DEBUG_NAV_SCORING @@ -2555,6 +2555,17 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con result->Window = window; result->RectRel = nav_bb_rel; } + + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) + { + result = &g.NavMoveResultLocalVisibleSet; + result->ID = id; + result->Window = window; + result->RectRel = nav_bb_rel; + } } // Update window-relative bounding box of navigated item @@ -3198,9 +3209,14 @@ static void ImGui::NavUpdate() // Select which result to use ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules - if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) - if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter)) + // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. + if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) + result = &g.NavMoveResultLocalVisibleSet; + + // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. + if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) + if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) result = &g.NavMoveResultOther; IM_ASSERT(g.NavWindow && result->Window); @@ -3354,6 +3370,45 @@ static void ImGui::NavUpdate() g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; } + // PageUp/PageDown scroll + float nav_scoring_rect_offset_y = 0.0f; + if (nav_keyboard_active && g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0) + { + ImGuiWindow* window = g.NavWindow; + bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up)); + bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down)); + if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held)) + { + if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) + { + // Fallback manual-scroll when window has no navigable item + if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight()); + else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight()); + } + else + { + const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; + const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + { + nav_scoring_rect_offset_y = -page_offset_y; + g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Up; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + { + nav_scoring_rect_offset_y = +page_offset_y; + g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Down; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + } + } + } + if (g.NavMoveDir != ImGuiDir_None) { g.NavMoveRequest = true; @@ -3401,6 +3456,7 @@ static void ImGui::NavUpdate() // Reset search results g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisibleSet.Clear(); g.NavMoveResultOther.Clear(); // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items @@ -3421,6 +3477,7 @@ static void ImGui::NavUpdate() // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); + g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y); g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). @@ -4658,9 +4715,14 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items return; } + // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect + ImRect unclipped_rect = window->ClipRect; + if (g.NavMoveRequest) + unclipped_rect.Add(g.NavScoringRectScreen); + const ImVec2 pos = window->DC.CursorPos; - int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); - int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); + int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); + int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); // When performing a navigation request, ensure we have one item extra in the direction we are moving to if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) diff --git a/imgui_internal.h b/imgui_internal.h index 9c61c814..eb07f016 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -315,10 +315,12 @@ enum ImGuiNavDirSourceFlags_ enum ImGuiNavMoveFlags_ { - ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side - ImGuiNavMoveFlags_LoopY = 1 << 1, - ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3 // This is not super useful for provided for completeness + ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side + ImGuiNavMoveFlags_LoopY = 1 << 1, + ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness + ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5 // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible. }; enum ImGuiNavForward @@ -673,7 +675,8 @@ struct ImGuiContext ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request ImGuiDir NavMoveClipDir; ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag) + ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) // Render ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user