Merge branch 'master' into viewport

# Conflicts:
#	examples/imgui_impl_dx10.cpp
#	examples/imgui_impl_dx11.cpp
#	examples/imgui_impl_dx12.cpp
#	examples/imgui_impl_dx9.cpp
#	examples/opengl2_example/imgui_impl_glfw_gl2.cpp
#	examples/opengl3_example/imgui_impl_glfw_gl3.cpp
#	examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp
#	examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h
#	examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp
#	examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h
#	imgui.cpp
This commit is contained in:
omar
2018-04-04 19:39:42 +02:00
10 changed files with 285 additions and 180 deletions

361
imgui.cpp
View File

@ -8,7 +8,13 @@
// Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// This library is free but I need your support to sustain development and maintenance.
// If you work for a company, please consider financial support, see Readme. For individuals: https://www.patreon.com/imgui
// If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui
// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
// to a better solution or official support for them.
/*
@ -25,7 +31,7 @@
- FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
- How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
- How can I display an image? What is ImTextureID, how does it works?
- How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels and the ID stack.
- How can I have multiple widgets with the same label or without a label? A primer on labels and the ID Stack.
- How can I load a different font than the default?
- How can I easily use icons in my application?
- How can I load multiple fonts?
@ -48,8 +54,8 @@
- Minimize setup and maintenance
- Minimize state storage on user side
- Portable, minimize dependencies, run on target (consoles, phones, etc.)
- Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, opening a tree node
for the first time, etc. but a typical frame won't allocate anything)
- Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,
opening a tree node for the first time, etc. but a typical frame should not allocate anything)
Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
- Doesn't look fancy, doesn't animate
@ -86,8 +92,8 @@
READ FIRST
- Read the FAQ below this section!
- Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention
on your side, no state duplication, less sync, less bugs.
- Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction
or destruction steps, less data retention on your side, less state duplication, less state synchronization, less bugs.
- Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
- You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
@ -95,18 +101,18 @@
- Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
- Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed from the public API.
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.
Please report any issue to the GitHub page!
If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
from the public API. 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. Please report any issue to the GitHub page!
- Try to keep your copy of dear imgui reasonably up to date.
GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
- Run and study the examples and demo to get acquainted with the library.
- Add the Dear ImGui source files to your projects, using your preferred build system.
It is recommended you build the .cpp files as part of your project and not as a library.
- You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
- See examples/ folder for standalone sample applications.
- You may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
- You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder.
- When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
- Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize
@ -194,7 +200,8 @@
MyEngineBindTexture(pcmd->TextureId);
// We are using scissoring to clip some objects. All low-level graphics API supports it.
// If your engine doesn't support scissoring yet, you will get some small glitches (some elements outside their bounds) which you can fix later.
// If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
// (some elements visible outside their bounds) but you can fix that once everywhere else works!
MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
// Render 'pcmd->ElemCount/3' indexed triangles.
@ -210,22 +217,25 @@
- When calling NewFrame(), the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'io.WantTextInput' flags are updated.
They tell you if ImGui intends to use your inputs. So for example, if 'io.WantCaptureMouse' is set you would typically want to hide
mouse inputs from the rest of your application. Read the FAQ below for more information about those flags.
- Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues!
USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS [BETA]
- The gamepad/keyboard navigation is in Beta. Ask questions and report issues at https://github.com/ocornut/imgui/issues/787
- The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
- Keyboard:
- Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays.
- When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set.
For more advanced uses, you may want to read from:
- Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays.
- When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
will be set. For more advanced uses, you may want to read from:
- io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
- io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
- or query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions.
Please reach out if you think the game vs navigation input sharing could be improved.
- Gamepad:
- Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
- Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame().
- Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
Note that io.NavInputs[] is cleared by EndFrame().
- See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
- We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
@ -429,63 +439,69 @@
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!)
(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.
Q: Can I have multiple widgets with the same label? Can I have widget without a label?
A: Yes. A primer on labels and the ID stack...
Q: How can I have multiple widgets with the same label or without a label?
A: A primer on labels and the ID Stack...
- Elements that are typically not clickable, such as Text() items don't need an ID.
- 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.
- 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.
Button("OK"); // Label = "OK", ID = hash of "OK"
Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel"
Button("OK"); // Label = "OK", ID = top of id stack + hash of "OK"
Button("Cancel"); // Label = "Cancel", ID = top of id stack + hash of "Cancel"
- ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK"
in two different windows or in two different locations of a tree.
- 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.
- If you have a same ID twice in the same location, you'll have a conflict:
Button("OK");
Button("OK"); // ID collision! Both buttons will be treated as the same.
Button("OK"); // ID collision! Interacting with either button will trigger the first one.
Fear not! this is easy to solve and there are many ways to solve it!
- When passing a label you can optionally specify extra unique ID information within string itself.
- Solving ID conflict in a simple/local context:
When passing a label you can optionally specify extra ID information within string itself.
Use "##" to pass a complement to the ID that won't be visible to the end-user.
This helps solving the simple collision cases when you know which items are going to be created.
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 = hash of "Play"
Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above)
Button("Play##foo2"); // Label = "Play", ID = hash of "Play##foo2" (different from above)
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)
- If you want to completely hide the label, but still need an ID:
Checkbox("##On", &b); // Label = "", ID = hash of "##On" (no label!)
Checkbox("##On", &b); // Label = "", ID = top of id stack + hash of "##On" (no 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:
- 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 = hash of "ID"
Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above)
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)
sprintf(buf, "My game (%f FPS)###MyGame", fps);
Begin(buf); // Variable label, ID = hash of "MyGame"
- Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window.
This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements.
You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of _everything_ in the ID stack!
- Solving ID conflict in a more general manner:
Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
within the same window. This is the most convenient way of distinguishing ID when iterating and
creating many UI elements programmatically.
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!
for (int i = 0; i < 100; i++)
{
PushID(i);
Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique)
Button("Click"); // Label = "Click", ID = top of id stack + hash of integer + hash of "Click"
PopID();
}
@ -493,7 +509,7 @@
{
MyObject* obj = Objects[i];
PushID(obj);
Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique)
Button("Click"); // Label = "Click", ID = top of id stack + hash of pointer + hash of "Click"
PopID();
}
@ -501,50 +517,53 @@
{
MyObject* obj = Objects[i];
PushID(obj->Name);
Button("Click"); // Label = "Click", ID = hash of string + "label" (unique)
Button("Click"); // Label = "Click", ID = top of id stack + hash of string + hash of "Click"
PopID();
}
- More example showing that you can stack multiple prefixes into the ID stack:
Button("Click"); // Label = "Click", ID = hash of "Click"
Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click"
PushID("node");
Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click"
PushID(my_ptr);
Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click"
Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of ptr + hash of "Click"
PopID();
PopID();
- Tree nodes implicitly creates a scope for you by calling PushID().
Button("Click"); // Label = "Click", ID = hash of "Click"
Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click"
if (TreeNode("node"))
{
Button("Click"); // Label = "Click", ID = hash of "node" + "Click"
Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click"
TreePop();
}
- When working with trees, ID are used to preserve the open/close state of each tree node.
Depending on your use cases you may want to use strings, indices or pointers as ID.
e.g. when displaying a single object that may change over time (dynamic 1-1 relationship), using a static string as ID will preserve your
node open/closed state when the targeted object change.
e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently.
experiment and see what makes more sense!
e.g. when following a single pointer that may change over time, using a static string as ID
will preserve your node open/closed state when the targeted object change.
e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
node open/closed state differently. See what makes more sense in your situation!
Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
Q: How can I load a different font than the default?
A: Use the font atlas to load the TTF/OTF file you want:
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
(default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code)
New programmers: remember that in C/C++ and most programming languages if you want to use a backslash \ in a string literal you need to write a double backslash "\\":
io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG
New programmers: remember that in C/C++ and most programming languages if you want to use a
backslash \ within a string literal, you need to write it double backslash "\\":
io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!)
io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT
io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT
Q: How can I easily use icons in my application?
A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your
strings. Read 'How can I load multiple fonts?' and the file 'misc/fonts/README.txt' for instructions and useful header files.
A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?'
and the file 'misc/fonts/README.txt' for instructions and useful header files.
Q: How can I load multiple fonts?
A: Use the font atlas to pack them into a single texture:
@ -589,13 +608,16 @@
builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax.
Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
by using the u8"hello" syntax. Specifying literal in your source code using a local code page
(such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
Text input: it is up to your application to pass the right character code to io.AddInputCharacter(). The applications in examples/ are doing that.
For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle.
The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly.
Text input: it is up to your application to pass the right character code by calling
io.AddInputCharacter(). The applications in examples/ are doing that. For languages relying
on an Input Method Editor (IME), on Windows you can copy the Hwnd of your application in the
io.ImeWindowHandle field. The default implementation of io.ImeSetInputScreenPosFn() will set
your Microsoft IME position correctly.
Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags.
@ -666,14 +688,13 @@
#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
#pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' //
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
#endif
@ -724,7 +745,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const
static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
namespace ImGui
@ -1014,11 +1035,11 @@ char* ImStrdup(const char *str)
return (char*)memcpy(buf, (const void*)str, len);
}
char* ImStrchrRange(const char* str, const char* str_end, char c)
const char* ImStrchrRange(const char* str, const char* str_end, char c)
{
for ( ; str < str_end; str++)
if (*str == c)
return (char*)str;
return str;
return NULL;
}
@ -3258,7 +3279,7 @@ static void ImGui::NavUpdate()
g.NavScoringCount = 0;
#if IMGUI_DEBUG_NAV_RECTS
if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames <= 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
#endif
}
@ -4154,7 +4175,7 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly)
line_end[-1] = 0;
const char* name_end = line_end - 1;
const char* type_start = line + 1;
char* type_end = ImStrchrRange(type_start, name_end, ']');
char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
if (!type_end || !name_start)
{
@ -4235,8 +4256,8 @@ static void MarkIniSettingsDirty(ImGuiWindow* window)
// FIXME: Add a more explicit sort order in the window structure.
static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
{
const ImGuiWindow* a = *(const ImGuiWindow**)lhs;
const ImGuiWindow* b = *(const ImGuiWindow**)rhs;
const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
return d;
if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
@ -4300,7 +4321,7 @@ static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* wi
for (int i = 0; i < window->DC.ChildWindows.Size; i++)
{
ImGuiWindow* child = window->DC.ChildWindows[i];
if (child->Active && child->HiddenFrames <= 0) // clipped children may have been marked not active
if (child->Active && child->HiddenFrames == 0) // clipped children may have been marked not active
AddWindowToDrawData(out_list, child);
}
}
@ -4488,10 +4509,10 @@ void ImGui::Render()
for (int n = 0; n != g.Windows.Size; n++)
{
ImGuiWindow* window = g.Windows[n];
if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0 && window != window_to_render_front_most)
if (window->Active && window->HiddenFrames == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most)
AddWindowToDrawDataSelectLayer(window);
}
if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames <= 0) // NavWindowingTarget is always temporarily displayed as the front-most window
if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames == 0) // NavWindowingTarget is always temporarily displayed as the front-most window
AddWindowToDrawDataSelectLayer(window_to_render_front_most);
// Draw software mouse cursor if requested
@ -5321,7 +5342,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_
if (ImGuiWindow* window = FindWindowByName(window_name))
if (window->Active)
{
// Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one.
// Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
window->HiddenFrames = 1;
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
}
@ -5373,6 +5394,7 @@ void ImGui::OpenPopupEx(ImGuiID id)
popup_ref.OpenMousePos = g.IO.MousePos;
popup_ref.OpenPopupPos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos;
//printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
if (g.OpenPopupStack.Size < current_stack_size + 1)
{
g.OpenPopupStack.push_back(popup_ref);
@ -5643,7 +5665,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
char title[256];
if (name)
ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", parent_window->Name, name);
else
ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
@ -6289,7 +6311,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Find or create
ImGuiWindow* window = FindWindowByName(name);
if (!window)
const bool window_just_created = (window == NULL);
if (window_just_created)
{
ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
window = CreateNewWindow(name, size_on_first_use, flags);
@ -6316,7 +6339,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Update the Appearing flag
bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1);
const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames > 0);
if (flags & ImGuiWindowFlags_Popup)
{
ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
@ -6433,7 +6456,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
if (window->Flags & ImGuiWindowFlags_FullViewport)
window->WindowRounding = 0.0f;
window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
window->WindowPadding = style.WindowPadding;
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
@ -6464,7 +6487,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
if (window->HiddenFrames > 0)
window->HiddenFrames--;
if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user)
if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
{
window->HiddenFrames = 1;
if (flags & ImGuiWindowFlags_AlwaysAutoResize)
@ -6477,10 +6500,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
}
// Hide new windows for one frame until they calculate their size
if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
window->HiddenFrames = 1;
// Calculate auto-fit size, handle automatic resize
const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
if (flags & ImGuiWindowFlags_AlwaysAutoResize && !window->Collapsed)
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
{
// Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
if (!window_size_x_set_by_api)
@ -6490,7 +6517,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
{
// Auto-fit only grows during the first few frames
// Auto-fit may only grow window during the first few frames
// We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
@ -7116,6 +7143,7 @@ void ImGui::FocusWindow(ImGuiWindow* window)
g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
g.NavIdIsAlive = false;
g.NavLayer = 0;
//printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
}
// Passing NULL allow to disable keyboard focus
@ -7316,33 +7344,34 @@ void ImGui::PopStyleColor(int count)
struct ImGuiStyleVarInfo
{
ImGuiDataType Type;
ImU32 Count;
ImU32 Offset;
void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
};
static const ImGuiStyleVarInfo GStyleVarInfo[] =
{
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
};
static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
@ -7355,7 +7384,7 @@ static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
{
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
if (var_info->Type == ImGuiDataType_Float)
if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
{
ImGuiContext& g = *GImGui;
float* pvar = (float*)var_info->GetVarPtr(&g.Style);
@ -7369,7 +7398,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
{
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
if (var_info->Type == ImGuiDataType_Float2)
if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
{
ImGuiContext& g = *GImGui;
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
@ -7385,11 +7414,12 @@ void ImGui::PopStyleVar(int count)
ImGuiContext& g = *GImGui;
while (count > 0)
{
// We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
ImGuiStyleMod& backup = g.StyleModifiers.back();
const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr(&g.Style)) = backup.BackupFloat[0];
else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr(&g.Style)) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr(&g.Style)) = backup.BackupInt[0];
void* data = info->GetVarPtr(&g.Style);
if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
g.StyleModifiers.pop_back();
count--;
}
@ -7567,6 +7597,8 @@ static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
// Test condition (NB: bit 0 is always true) and clear flags for next time
if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
return;
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
@ -7601,6 +7633,8 @@ static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con
// Test condition (NB: bit 0 is always true) and clear flags for next time
if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
return;
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
// Set
@ -7692,6 +7726,7 @@ void ImGui::SetWindowFocus(const char* name)
void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
g.NextWindowData.PosVal = pos;
g.NextWindowData.PosPivotVal = pivot;
g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
@ -7700,6 +7735,7 @@ void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pi
void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
g.NextWindowData.SizeVal = size;
g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
}
@ -7723,6 +7759,7 @@ void ImGui::SetNextWindowContentSize(const ImVec2& size)
void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
g.NextWindowData.CollapsedVal = collapsed;
g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
}
@ -9133,6 +9170,8 @@ static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr,
ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
else if (data_type == ImGuiDataType_Float)
ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
else if (data_type == ImGuiDataType_Double)
ImFormatString(buf, buf_size, display_format, *(double*)data_ptr);
}
static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
@ -9151,27 +9190,44 @@ static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr,
else
ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
}
else if (data_type == ImGuiDataType_Double)
{
if (decimal_precision < 0)
ImFormatString(buf, buf_size, "%f", *(double*)data_ptr);
else
ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(double*)data_ptr);
}
}
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2)
{
IM_ASSERT(op == '+' || op == '-');
if (data_type == ImGuiDataType_Int)
{
if (op == '+')
*(int*)value1 = *(int*)value1 + *(const int*)value2;
else if (op == '-')
*(int*)value1 = *(int*)value1 - *(const int*)value2;
if (op == '+') *(int*)output = *(int*)arg1 + *(const int*)arg2;
else if (op == '-') *(int*)output = *(int*)arg1 - *(const int*)arg2;
}
else if (data_type == ImGuiDataType_Float)
{
if (op == '+')
*(float*)value1 = *(float*)value1 + *(const float*)value2;
else if (op == '-')
*(float*)value1 = *(float*)value1 - *(const float*)value2;
if (op == '+') *(float*)output = *(float*)arg1 + *(const float*)arg2;
else if (op == '-') *(float*)output = *(float*)arg1 - *(const float*)arg2;
}
else if (data_type == ImGuiDataType_Double)
{
if (op == '+') *(double*)output = *(double*)arg1 + *(const double*)arg2;
else if (op == '-') *(double*)output = *(double*)arg1 - *(const double*)arg2;
}
}
static size_t GDataTypeSize[ImGuiDataType_COUNT] =
{
sizeof(int),
sizeof(float),
sizeof(double)
};
// User can input math operators (e.g. +100) to edit a numerical values.
// NB: This is _not_ a full expression evaluator. We should probably add one though..
static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
{
while (ImCharIsSpace(*buf))
@ -9193,45 +9249,56 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
if (!buf[0])
return false;
IM_ASSERT(data_type < ImGuiDataType_COUNT);
int data_backup[2];
IM_ASSERT(GDataTypeSize[data_type] <= sizeof(data_backup));
memcpy(data_backup, data_ptr, GDataTypeSize[data_type]);
if (data_type == ImGuiDataType_Int)
{
if (!scalar_format)
scalar_format = "%d";
int* v = (int*)data_ptr;
const int old_v = *v;
int arg0i = *v;
if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1)
return false;
// Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
float arg1f = 0.0f;
if (op == '+') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f); } // Add (use "+-" to subtract)
else if (op == '*') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f); } // Multiply
else if (op == '/') { if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }// Divide
else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign constant (read as integer so big values are not lossy)
return (old_v != *v);
else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign integer constant
}
else if (data_type == ImGuiDataType_Float)
{
// For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
scalar_format = "%f";
float* v = (float*)data_ptr;
const float old_v = *v;
float arg0f = *v;
float arg0f = *v, arg1f = 0.0f;
if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
return false;
float arg1f = 0.0f;
if (sscanf(buf, scalar_format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
else if (op == '*') { *v = arg0f * arg1f; } // Multiply
else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
else { *v = arg1f; } // Assign constant
return (old_v != *v);
}
return false;
else if (data_type == ImGuiDataType_Double)
{
scalar_format = "%lf";
double* v = (double*)data_ptr;
double arg0f = *v, arg1f = 0.0f;
if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
return false;
if (sscanf(buf, scalar_format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
else if (op == '*') { *v = arg0f * arg1f; } // Multiply
else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
else { *v = arg1f; } // Assign constant
}
return memcmp(data_backup, data_ptr, GDataTypeSize[data_type]) != 0;
}
// Create text input in place of a slider (when CTRL+Clicking on slider)
@ -10123,7 +10190,7 @@ struct ImGuiPlotArrayGetterData
static float Plot_ArrayGetter(void* data, int idx)
{
ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
return v;
}
@ -10532,12 +10599,16 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys.
return false;
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank))
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))
{
if (flags & ImGuiInputTextFlags_CharsDecimal)
if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
return false;
if (flags & ImGuiInputTextFlags_CharsScientific)
if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))
return false;
if (flags & ImGuiInputTextFlags_CharsHexadecimal)
if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
return false;
@ -11199,7 +11270,7 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data
DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
bool value_changed = false;
if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal))
if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
extra_flags |= ImGuiInputTextFlags_CharsDecimal;
extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
@ -11212,13 +11283,13 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data
SameLine(0, style.ItemInnerSpacing.x);
if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
{
DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
value_changed = true;
}
SameLine(0, style.ItemInnerSpacing.x);
if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
{
DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
value_changed = true;
}
}
@ -11237,19 +11308,31 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data
bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
{
char display_format[16];
extra_flags |= ImGuiInputTextFlags_CharsScientific;
if (decimal_precision < 0)
strcpy(display_format, "%f"); // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
{
// Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), "%f", extra_flags);
}
else
{
char display_format[16];
ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
}
}
bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* display_format, ImGuiInputTextFlags extra_flags)
{
extra_flags |= ImGuiInputTextFlags_CharsScientific;
return InputScalarEx(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), display_format, extra_flags);
}
bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
{
// Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags);
return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), scalar_format, extra_flags);
}
bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
@ -13617,14 +13700,14 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s
// Store in heap
g.DragDropPayloadBufHeap.resize((int)data_size);
payload.Data = g.DragDropPayloadBufHeap.Data;
memcpy((void*)payload.Data, data, data_size);
memcpy((void*)(intptr_t)payload.Data, data, data_size);
}
else if (data_size > 0)
{
// Store locally
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
payload.Data = g.DragDropPayloadBufLocal;
memcpy((void*)payload.Data, data, data_size);
memcpy((void*)(intptr_t)payload.Data, data, data_size);
}
else
{