Merge branch 'master' into viewport

# Conflicts:
#	examples/opengl3_example/imgui_impl_glfw_gl3.cpp
#	examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp
#	imgui.cpp
This commit is contained in:
omar 2018-06-05 15:51:52 +02:00
commit 93b032ea92
8 changed files with 363 additions and 142 deletions

View File

@ -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,27 +23,34 @@ 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)
-----------------------------------------------------------------------
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)
- 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().
-----------------------------------------------------------------------
VERSION 1.61 (Released 2018-05-14)
-----------------------------------------------------------------------
Breaking Changes:
@ -108,9 +106,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.
@ -300,9 +300,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:
@ -438,9 +440,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:
@ -539,9 +543,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:
@ -601,9 +607,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:
@ -695,9 +703,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:
@ -771,9 +781,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:

View File

@ -160,9 +160,10 @@ 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)
- 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,9 +216,11 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb)
- 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)
@ -225,7 +228,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 +238,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?
@ -249,14 +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.
@ -304,6 +309,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.

362
imgui.cpp
View File

@ -437,7 +437,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).
@ -452,31 +452,111 @@
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.
(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!)
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.
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.
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:
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)
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 <stb_image.h>
[...]
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, 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);
// 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?
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:
@ -491,20 +571,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"
@ -516,45 +598,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();
}
@ -2105,7 +2187,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;
@ -2268,6 +2357,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);
@ -2437,6 +2535,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
@ -2452,9 +2552,9 @@ 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))
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
@ -2468,7 +2568,17 @@ 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;
}
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;
}
@ -3055,10 +3165,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));
//GetOverlayDrawList(window)->AddRect(window_rect_rel.Min, window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG]
if (window_rect.Contains(item_rect))
@ -3096,12 +3206,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 );
@ -3142,23 +3256,32 @@ 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
if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter))
result = &g.NavMoveResultOther;
// PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
result = &g.NavMoveResultLocalVisibleSet;
// 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);
// 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
@ -3203,8 +3326,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;
@ -3297,6 +3418,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;
@ -3316,7 +3476,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)
@ -3344,6 +3504,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
@ -3364,6 +3525,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) : ImRect(0,0,0,0);
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().
@ -3822,7 +3984,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
}
@ -3836,10 +3999,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;
@ -4017,6 +4181,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;
@ -5179,9 +5344,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)
@ -7125,7 +7295,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);
@ -12144,28 +12314,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();
@ -12180,7 +12350,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;
@ -12196,18 +12366,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
@ -14068,6 +14238,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;
@ -14152,16 +14323,15 @@ 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();
// 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;
tooltip_window->SkipItems = true;
tooltip_window->HiddenFrames = 1;
}
}
if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
@ -14178,17 +14348,32 @@ void ImGui::EndDragDropSource()
IM_ASSERT(g.DragDropActive);
if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
{
EndTooltip();
PopStyleColor();
//PopStyleVar();
}
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)
{
@ -14198,7 +14383,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()
@ -14302,6 +14487,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;
}

View File

@ -55,7 +55,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
@ -734,6 +734,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.
};

View File

@ -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();

View File

@ -318,7 +318,9 @@ enum ImGuiNavMoveFlags_
ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side
ImGuiNavMoveFlags_LoopY = 1 << 1,
ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)
ImGuiNavMoveFlags_WrapY = 1 << 3 // This is not super useful for provided for completeness
ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful 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
@ -570,7 +572,6 @@ struct ImGuiViewportP : public ImGuiViewport
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
@ -578,7 +579,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
@ -665,6 +666,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<ImGuiColMod> ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
ImVector<ImGuiStyleMod> StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
@ -717,7 +720,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
float ModalWindowDarkeningRatio;
@ -730,7 +734,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
@ -807,6 +812,8 @@ struct ImGuiContext
ActiveIdClickOffset = ImVec2(-1,-1);
ActiveIdWindow = NULL;
ActiveIdSource = ImGuiInputSource_None;
LastActiveId = 0;
LastActiveIdTimer = 0.0f;
MovingWindow = NULL;
NextTreeNodeOpenVal = false;
NextTreeNodeOpenCond = 0;
@ -847,6 +854,7 @@ struct ImGuiContext
DragDropSourceFlags = 0;
DragDropMouseButton = -1;
DragDropTargetId = 0;
DragDropAcceptFlags = 0;
DragDropAcceptIdCurrRectSurface = 0.0f;
DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
DragDropAcceptFrameCount = -1;
@ -1174,6 +1182,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().

View File

@ -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<ImWchar> ranges;
ImFontAtlas::GlyphRangesBuilder builder;

View File

@ -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);
```