ref: cebd07a93936b7c64945802d216c57287bdced2c
parent: ab82f014aea39a8b12df8ad8759933664308c17c
author: Clownacy <Clownacy@users.noreply.github.com>
date: Wed Jul 8 12:05:58 EDT 2020
Update imgui to latest table branch commit
--- a/DoConfig/imgui/imgui.cpp
+++ b/DoConfig/imgui/imgui.cpp
@@ -1,10 +1,10 @@
-// dear imgui, v1.76 WIP
+// dear imgui, v1.78 WIP
// (main code and documentation)
// Help:
// - Read FAQ at http://dearimgui.org/faq
// - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
-// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. All applications in examples/ are doing that.
+// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
// Read imgui.cpp for details, links and comments.
// Resources:
@@ -18,7 +18,7 @@
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// See LICENSE.txt for copyright and licensing details (standard MIT License).
-// This library is free but I need your support to sustain development and maintenance.
+// This library is free but needs your support to sustain development and maintenance.
// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
// Individuals: you can support continued development via donations. See docs/README or web page.
@@ -40,7 +40,7 @@
- READ FIRST
- HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
- GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
- - HOW A SIMPLE APPLICATION MAY LOOK LIKE (2 variations)
+ - HOW A SIMPLE APPLICATION MAY LOOK LIKE
- HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
- USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
- API BREAKING CHANGES (read me when you update!)
@@ -139,7 +139,7 @@
- Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
- The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
- Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
- You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
+ You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ.
- Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI,
where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
@@ -153,6 +153,7 @@
However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
- C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
+
HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
----------------------------------------------
- Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
@@ -163,11 +164,12 @@
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 in imgui_demo.cpp to get acquainted with the library.
- In the majority of cases you should be able to use unmodified back-ends files available in the examples/ folder.
- - Add the Dear ImGui source files to your projects or using your preferred build system.
+ - Add the Dear ImGui source files + selected back-end source files to your projects or using your preferred build system.
It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
- You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.
- When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
@@ -177,9 +179,11 @@
- Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
- If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.
+
HOW A SIMPLE APPLICATION MAY LOOK LIKE
--------------------------------------
- EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
+ EXHIBIT 1: USING THE EXAMPLE BINDINGS (= imgui_impl_XXX.cpp files from the examples/ folder).
+ The sub-folders in examples/ contains examples applications following this structure.
// Application init: create a dear imgui context, setup some options, load fonts
ImGui::CreateContext();
@@ -268,8 +272,15 @@
// Shutdown
ImGui::DestroyContext();
+ To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application,
+ you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
+ Please read the FAQ and example applications for details about this!
+
+
HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
---------------------------------------------
+ The bindings in impl_impl_XXX.cpp files contains many working implementations of a rendering function.
+
void void MyImGuiRenderFunction(ImDrawData* draw_data)
{
// TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
@@ -314,11 +325,6 @@
}
}
- - The examples/ folders contains many actual implementation of the pseudo-codes above.
- - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
- They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the
- rest of your application. In every cases you need to pass on the inputs to Dear ImGui.
- - Refer to the FAQ for more information. Amusingly, it is called a FAQ because people frequently run into the same issues!
USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
------------------------------------------
@@ -366,8 +372,12 @@
When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
+ - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
+ - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete).
+ - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
+ - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
- 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
- - 2019/12/17 (1.75) - made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
+ - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
- 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
- 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
- ShowTestWindow() -> use ShowDemoWindow()
@@ -498,7 +508,7 @@
- 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
- 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
- changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
- - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))'
+ - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0))'
- 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
- 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
- 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
@@ -591,7 +601,10 @@
FREQUENTLY ASKED QUESTIONS (FAQ)
================================
- Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
+ Read all answers online:
+ https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
+ Read all answers locally (with a text editor or ideally a Markdown viewer):
+ docs/FAQ.md
Some answers are copied down here to facilitate searching in code.
Q&A: Basics
@@ -613,11 +626,14 @@
Q: What is this library called?
Q: Which version should I get?
>> This library is called "Dear ImGui", please don't call it "ImGui" :)
- >> See https://www.dearimgui.org/faq
+ >> See https://www.dearimgui.org/faq for details.
Q&A: Integration
================
+ Q: How to get started?
+ A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
+
Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
>> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
@@ -624,132 +640,18 @@
Q. How can I enable keyboard controls?
Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
- Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
+ Q: I integrated Dear ImGui in my engine and little squares are showing instead of text..
Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
+ Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries..
>> See https://www.dearimgui.org/faq
Q&A: Usage
----------
- Q: Why are multiple widgets reacting when I interact with a single one?
- Q: How can I have multiple widgets with the same label or with an empty label?
- A: A primer on labels and the ID Stack...
-
- 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.
-
- - Unique ID are often derived from a string label:
-
- 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();
- Begin("MyOtherWindow");
- Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK")
- End();
-
- - If you have a same ID twice in the same location, you'll have a conflict:
-
- Button("OK");
- 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!
-
- - 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 e.g. at compilation time which items
- are going to be created:
-
- 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 = hash of (..., "##On") // No visible label, just a checkbox!
-
- - 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, even though the label looks different
-
- sprintf(buf, "My game (%f FPS)###MyGame", fps);
- Begin(buf); // Variable title, ID = hash of "MyGame"
-
- - 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_ pushed into the ID stack.
- At each level of the stack we store the seed used for items at this level of the ID stack.
-
- Begin("Window");
- for (int i = 0; i < 100; i++)
- {
- 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 = 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 = hash of ("Window", obj->Name, "Click")
- PopID();
- }
- End();
-
- - You can stack multiple prefixes into the ID stack:
-
- Button("Click"); // Label = "Click", ID = hash of (..., "Click")
- PushID("node");
- Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
- PushID(my_ptr);
- 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 = hash of (..., "Click")
- if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
- {
- Button("Click"); // Label = "Click", ID = hash of (..., "node", "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 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 have widgets with an empty label?
+ Q: How can I have multiple widgets with the same label?
+ Q: Why are multiple widgets reacting when I interact with one?
Q: How can I display an image? What is ImTextureID, how does it works?
- >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
-
Q: How can I use my own math types instead of ImVec2/ImVec4?
Q: How can I interact with standard C++ types (such as std::string and std::vector)?
Q: How can I display custom shapes? (using low-level ImDrawList API)
@@ -758,11 +660,12 @@
Q&A: Fonts, Text
================
+ Q: How should I handle DPI in my application?
Q: How can I load a different font than the default?
Q: How can I easily use icons in my application?
Q: How can I load multiple fonts?
Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
- >> See https://www.dearimgui.org/faq and docs/FONTS.txt
+ >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
Q&A: Concerns
=============
@@ -853,21 +756,21 @@
// Clang/GCC warnings with -Weverything
#if defined(__clang__)
-#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
-#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
-#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
-#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
-#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
-#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is.
-#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'
-#if __has_warning("-Wzero-as-null-pointer-constant")
-#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
#endif
-#if __has_warning("-Wdouble-promotion")
-#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
-#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
+#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
+#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
+#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
+#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning: declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
+#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
+#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 "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
+#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
+#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#elif defined(__GNUC__)
// We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
@@ -893,7 +796,7 @@
// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
-static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certaint time, unless mouse moved.
+static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
//-------------------------------------------------------------------------
// [SECTION] FORWARD DECLARATIONS
@@ -901,7 +804,7 @@
static void SetCurrentWindow(ImGuiWindow* window);
static void FindHoveredWindow();
-static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
+static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
@@ -910,8 +813,10 @@
static ImRect GetViewportRect();
// Settings
+static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
+static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
// Platform Dependents default implementation for IO functions
@@ -928,6 +833,7 @@
static void NavUpdateMoveResult();
static float NavUpdatePageUpPageDown();
static inline void NavUpdateAnyRequestFlag();
+static void NavEndFrame();
static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
static ImVec2 NavCalcPreferredRefPos();
@@ -946,7 +852,7 @@
static void UpdateMouseWheel();
static void UpdateTabFocus();
static void UpdateDebugToolItemPicker();
-static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
+static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
static void RenderWindowOuterBorders(ImGuiWindow* window);
static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
@@ -998,7 +904,7 @@
{
Alpha = 1.0f; // Global alpha applies to everything in ImGui
WindowPadding = ImVec2(8,8); // Padding within a window
- WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
+ WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
WindowMinSize = ImVec2(32,32); // Minimum window size
WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
@@ -1022,6 +928,7 @@
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
TabBorderSize = 0.0f; // Thickness of border around tabs.
+ TabMinWidthForUnselectedCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
@@ -1059,6 +966,8 @@
GrabMinSize = ImFloor(GrabMinSize * scale_factor);
GrabRounding = ImFloor(GrabRounding * scale_factor);
TabRounding = ImFloor(TabRounding * scale_factor);
+ if (TabMinWidthForUnselectedCloseButton != FLT_MAX)
+ TabMinWidthForUnselectedCloseButton = ImFloor(TabMinWidthForUnselectedCloseButton * scale_factor);
DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
@@ -1074,7 +983,7 @@
ConfigFlags = ImGuiConfigFlags_None;
BackendFlags = ImGuiBackendFlags_None;
DisplaySize = ImVec2(-1.0f, -1.0f);
- DeltaTime = 1.0f/60.0f;
+ DeltaTime = 1.0f / 60.0f;
IniSavingRate = 5.0f;
IniFilename = "imgui.ini";
LogFilename = "imgui_log.txt";
@@ -1131,7 +1040,8 @@
// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
void ImGuiIO::AddInputCharacter(unsigned int c)
{
- InputQueueCharacters.push_back(c > 0 && c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
+ if (c != 0)
+ InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
}
// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
@@ -1138,10 +1048,13 @@
// we should save the high surrogate.
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
{
+ if (c == 0 && InputQueueSurrogate == 0)
+ return;
+
if ((c & 0xFC00) == 0xD800) // High surrogate, must save
{
if (InputQueueSurrogate != 0)
- InputQueueCharacters.push_back(0xFFFD);
+ InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
InputQueueSurrogate = c;
return;
}
@@ -1166,7 +1079,7 @@
{
unsigned int c = 0;
utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
- if (c > 0)
+ if (c != 0)
InputQueueCharacters.push_back((ImWchar)c);
}
}
@@ -1211,7 +1124,7 @@
float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
d2 = (d2 >= 0) ? d2 : -d2;
d3 = (d3 >= 0) ? d3 : -d3;
- if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy))
+ if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
{
ImVec2 p_current(x4, y4);
ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
@@ -1225,12 +1138,12 @@
}
else if (level < 10)
{
- float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f;
- float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f;
- float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f;
- float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f;
- float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f;
- float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f;
+ float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
+ float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
+ float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
+ float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
+ float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
+ float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
}
@@ -1684,7 +1597,7 @@
{
ImWchar* buf_out = buf;
ImWchar* buf_end = buf + buf_size;
- while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
+ while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
{
unsigned int c;
in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
@@ -1731,7 +1644,7 @@
{
if (buf_size < 3) return 0;
buf[0] = (char)(0xe0 + (c >> 12));
- buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
+ buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
buf[2] = (char)(0x80 + ((c ) & 0x3f));
return 3;
}
@@ -1768,13 +1681,13 @@
{
char* buf_out = buf;
const char* buf_end = buf + buf_size;
- while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
+ while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
{
unsigned int c = (unsigned int)(*in_text++);
if (c < 0x80)
*buf_out++ = (char)c;
else
- buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
+ buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);
}
*buf_out = 0;
return (int)(buf_out - buf);
@@ -1810,7 +1723,7 @@
ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
{
- float s = 1.0f/255.0f;
+ float s = 1.0f / 255.0f;
return ImVec4(
((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
@@ -1861,7 +1774,7 @@
return;
}
- h = ImFmod(h, 1.0f) / (60.0f/360.0f);
+ h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
int i = (int)h;
float f = h - (float)i;
float p = v * (1.0f - s);
@@ -2078,7 +1991,7 @@
void ImGuiTextFilter::Build()
{
Filters.resize(0);
- ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
+ ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
input_range.split(',', &Filters);
CountGrep = 0;
@@ -2236,7 +2149,9 @@
// 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);
+ unclipped_rect.Add(g.NavScoringRect);
+ if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
+ unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
const ImVec2 pos = window->DC.CursorPos;
int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
@@ -2793,7 +2708,7 @@
const float border_size = g.Style.FrameBorderSize;
if (border && border_size > 0.0f)
{
- window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
+ window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
}
}
@@ -2805,7 +2720,7 @@
const float border_size = g.Style.FrameBorderSize;
if (border_size > 0.0f)
{
- window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
+ window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
}
}
@@ -2828,11 +2743,11 @@
{
const float THICKNESS = 2.0f;
const float DISTANCE = 3.0f + THICKNESS * 0.5f;
- display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
+ display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
bool fully_visible = window->ClipRect.Contains(display_rect);
if (!fully_visible)
window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
- window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
+ window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
if (!fully_visible)
window->DrawList->PopClipRect();
}
@@ -2926,6 +2841,10 @@
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
ImGui::KeepAliveID(id);
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ ImGuiContext& g = *GImGui;
+ IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
+#endif
return id;
}
@@ -2934,6 +2853,10 @@
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
ImGui::KeepAliveID(id);
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ ImGuiContext& g = *GImGui;
+ IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
+#endif
return id;
}
@@ -2942,6 +2865,10 @@
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&n, sizeof(n), seed);
ImGui::KeepAliveID(id);
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ ImGuiContext& g = *GImGui;
+ IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
+#endif
return id;
}
@@ -2948,19 +2875,34 @@
ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
{
ImGuiID seed = IDStack.back();
- return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
+ ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ ImGuiContext& g = *GImGui;
+ IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
+#endif
+ return id;
}
ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
{
ImGuiID seed = IDStack.back();
- return ImHashData(&ptr, sizeof(void*), seed);
+ ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ ImGuiContext& g = *GImGui;
+ IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
+#endif
+ return id;
}
ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
{
ImGuiID seed = IDStack.back();
- return ImHashData(&n, sizeof(n), seed);
+ ImGuiID id = ImHashData(&n, sizeof(n), seed);
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ ImGuiContext& g = *GImGui;
+ IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
+#endif
+ return id;
}
// This is only used in rare/specific situations to manufacture an ID out of nowhere.
@@ -2993,7 +2935,7 @@
window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
window->IDStack.clear();
- window->DrawList->ClearFreeMemory();
+ window->DrawList->_ClearFreeMemory();
window->DC.ChildWindows.clear();
window->DC.ItemFlagsStack.clear();
window->DC.ItemWidthStack.clear();
@@ -3167,17 +3109,22 @@
if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
return false;
- SetHoveredID(id);
+ // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
+ // hover test in widgets code. We could also decide to split this function is two.
+ if (id != 0)
+ {
+ SetHoveredID(id);
- // [DEBUG] Item Picker tool!
- // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
- // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
- // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
- // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
- if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
- GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
- if (g.DebugItemPickerBreakId == id)
- IM_DEBUG_BREAK();
+ // [DEBUG] Item Picker tool!
+ // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
+ // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
+ // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
+ // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
+ if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
+ GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
+ if (g.DebugItemPickerBreakId == id)
+ IM_DEBUG_BREAK();
+ }
return true;
}
@@ -3187,7 +3134,7 @@
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (!bb.Overlaps(window->ClipRect))
- if (id == 0 || id != g.ActiveId)
+ if (id == 0 || (id != g.ActiveId && id != g.NavId))
if (clip_even_when_logged || !g.LogEnabled)
return true;
return false;
@@ -3242,11 +3189,21 @@
if (wrap_pos_x < 0.0f)
return 0.0f;
- ImGuiWindow* window = GImGui->CurrentWindow;
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
if (wrap_pos_x == 0.0f)
+ {
+ // We could decide to setup a default wrapping max point for auto-resizing windows,
+ // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
+ //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
+ // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
+ //else
wrap_pos_x = window->WorkRect.Max.x;
+ }
else if (wrap_pos_x > 0.0f)
+ {
wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
+ }
return ImMax(wrap_pos_x - pos.x, 1.0f);
}
@@ -3439,17 +3396,22 @@
if (g.NavWindow && g.NavWindow->Appearing)
return;
- // Click to focus window and start moving (after we're done with all our widgets)
+ // Click on empty space to focus window and start moving (after we're done with all our widgets)
if (g.IO.MouseClicked[0])
{
- if (g.HoveredRootWindow != NULL)
+ // Handle the edge case of a popup being closed while clicking in its empty space.
+ // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
+ ImGuiWindow* root_window = g.HoveredRootWindow;
+ const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
+
+ if (root_window != NULL && !is_closed_popup)
{
StartMouseMovingWindow(g.HoveredWindow);
- if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
- if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
+ if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
+ if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
g.MovingWindow = NULL;
}
- else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
+ else if (root_window != NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
{
// Clicking on void disable focus
FocusWindow(NULL);
@@ -3515,7 +3477,7 @@
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] = -DBL_MAX; // so the third click isn't turned into a double-click
+ g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
}
else
{
@@ -3690,7 +3652,7 @@
for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
{
if (g.IO.MouseClicked[i])
- g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
+ g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
mouse_any_down |= g.IO.MouseDown[i];
if (g.IO.MouseDown[i])
if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
@@ -3708,7 +3670,7 @@
if (g.WantCaptureMouseNextFrame != -1)
g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
else
- g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
+ g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
if (g.WantCaptureKeyboardNextFrame != -1)
@@ -3722,7 +3684,7 @@
g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
}
-static ImGuiKeyModFlags GetMergedKeyModFlags()
+ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
{
ImGuiContext& g = *GImGui;
ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
@@ -3776,11 +3738,11 @@
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
- g.BackgroundDrawList.Clear();
+ g.BackgroundDrawList._ResetForNewFrame();
g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
g.BackgroundDrawList.PushClipRectFullScreen();
- g.ForegroundDrawList.Clear();
+ g.ForegroundDrawList._ResetForNewFrame();
g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
g.ForegroundDrawList.PushClipRectFullScreen();
@@ -3832,6 +3794,7 @@
g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
g.DragDropWithinSource = false;
g.DragDropWithinTarget = false;
+ g.DragDropHoldJustPressedId = 0;
// Update keyboard input state
// Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
@@ -3840,7 +3803,7 @@
for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
- // Update gamepad/keyboard directional navigation
+ // Update gamepad/keyboard navigation
NavUpdate();
// Update mouse input state
@@ -3902,7 +3865,7 @@
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
// This fallback is particularly important as it avoid ImGui:: calls from crashing.
g.WithinFrameScopeWithImplicitWindow = true;
- SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
+ SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
Begin("Debug##Default");
IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
@@ -3946,8 +3909,10 @@
ImGuiSettingsHandler ini_handler;
ini_handler.TypeName = "Window";
ini_handler.TypeHash = ImHashStr("Window");
+ ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
+ ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
g.SettingsHandlers.push_back(ini_handler);
}
@@ -3954,15 +3919,7 @@
#ifdef IMGUI_HAS_TABLE
// Add .ini handle for ImGuiTable type
- {
- ImGuiSettingsHandler ini_handler;
- ini_handler.TypeName = "Table";
- ini_handler.TypeHash = ImHashStr("Table");
- ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
- ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
- ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
- g.SettingsHandlers.push_back(ini_handler);
- }
+ TableSettingsInstallHandler(context);
#endif // #ifdef IMGUI_HAS_TABLE
#ifdef IMGUI_HAS_DOCK
@@ -3996,6 +3953,11 @@
SetCurrentContext(backup_context);
}
+ // Notify hooked test engine, if any
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ ImGuiTestEngineHook_Shutdown(context);
+#endif
+
// Clear everything else
for (int i = 0; i < g.Windows.Size; i++)
IM_DELETE(g.Windows[i]);
@@ -4015,8 +3977,8 @@
g.OpenPopupStack.clear();
g.BeginPopupStack.clear();
g.DrawDataBuilder.ClearFreeMemory();
- g.BackgroundDrawList.ClearFreeMemory();
- g.ForegroundDrawList.ClearFreeMemory();
+ g.BackgroundDrawList._ClearFreeMemory();
+ g.ForegroundDrawList._ClearFreeMemory();
g.TabBars.Clear();
g.CurrentTabBarStack.clear();
@@ -4026,7 +3988,7 @@
g.CurrentTableStack.clear();
g.DrawChannelsTempMergeBuffer.clear();
- g.PrivateClipboard.clear();
+ g.ClipboardHandlerData.clear();
g.MenusIdSubmittedThisFrame.clear();
g.InputTextState.ClearFreeMemory();
@@ -4077,18 +4039,12 @@
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
{
- if (draw_list->CmdBuffer.empty())
+ // Remove trailing command if unused.
+ // Technically we could return directly instead of popping, but this make things looks neat in Metrics window as well.
+ draw_list->_PopUnusedDrawCmd();
+ if (draw_list->CmdBuffer.Size == 0)
return;
- // Remove trailing command if unused
- ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
- if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
- {
- draw_list->CmdBuffer.pop_back();
- if (draw_list->CmdBuffer.empty())
- return;
- }
-
// Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
// May trigger for you if you are using PrimXXX functions incorrectly.
IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
@@ -4173,7 +4129,12 @@
}
}
-// When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
+// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
+// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
+// so that e.g. (int)(max.x-min.x) in user's render produce correct result.
+// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
+// some frequently called functions which to modify both channels and clipping simultaneously tend to use the
+// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
{
ImGuiWindow* window = GetCurrentWindow();
@@ -4193,7 +4154,7 @@
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.Initialized);
-
+
// Don't process EndFrame() multiple times.
if (g.FrameCountEnded == g.FrameCount)
return;
@@ -4214,9 +4175,8 @@
g.CurrentWindow->Active = false;
End();
- // Show CTRL+TAB list window
- if (g.NavWindowingTarget != NULL)
- NavUpdateWindowingOverlay();
+ // Update navigation: CTRL+Tab, wrap-around requests
+ NavEndFrame();
// Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
if (g.DragDropActive)
@@ -4228,7 +4188,7 @@
}
// Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
- if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
+ if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
{
g.DragDropWithinSource = true;
SetTooltip("...");
@@ -4243,7 +4203,7 @@
UpdateMouseMovingWindowEndFrame();
// Sort the window list so that all child windows are after their parent
- // We cannot do that on FocusWindow() because childs may not exist yet
+ // We cannot do that on FocusWindow() because children may not exist yet
g.WindowsTempSortBuffer.resize(0);
g.WindowsTempSortBuffer.reserve(g.Windows.Size);
for (int i = 0; i != g.Windows.Size; i++)
@@ -4286,7 +4246,7 @@
// Add ImDrawList to render
ImGuiWindow* windows_to_render_top_most[2];
windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
- windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingList : NULL);
+ windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
for (int n = 0; n != g.Windows.Size; n++)
{
ImGuiWindow* window = g.Windows[n];
@@ -4343,7 +4303,7 @@
}
// Find window given position, search front-to-back
-// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
+// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
// called, aka before the next Begin(). Moving window isn't affected.
static void FindHoveredWindow()
@@ -4541,7 +4501,7 @@
{
ImGuiContext& g = *GImGui;
if (g.BeginPopupStack.Size > 0)
- return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
+ return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
return g.IO.MousePos;
}
@@ -4743,7 +4703,7 @@
ImGuiContext& g = *GImGui;
ImGuiWindow* parent_window = g.CurrentWindow;
- flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
+ flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
// Size
@@ -4783,7 +4743,7 @@
{
FocusWindow(child_window);
NavInitWindow(child_window, false);
- SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
+ SetActiveID(id + 1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
g.ActiveIdSource = ImGuiInputSource_Nav;
}
return ret;
@@ -4833,7 +4793,7 @@
// When browsing a window that has no activable items (scroll only) we keep a highlight on the child
if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
- RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
+ RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
}
else
{
@@ -4883,8 +4843,16 @@
return FindWindowByID(id);
}
-static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
+static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
{
+ window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
+ if (settings->Size.x > 0 && settings->Size.y > 0)
+ window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
+ window->Collapsed = settings->Collapsed;
+}
+
+static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
+{
ImGuiContext& g = *GImGui;
//IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
@@ -4903,12 +4871,8 @@
// Retrieve settings from .ini file
window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
- window->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
- window->Collapsed = settings->Collapsed;
- if (settings->Size.x > 0 && settings->Size.y > 0)
- size = ImVec2(settings->Size.x, settings->Size.y);
+ ApplyWindowSettings(window, settings);
}
- window->Size = window->SizeFull = ImFloor(size);
window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
@@ -5055,20 +5019,35 @@
static const ImGuiResizeGripDef resize_grip_def[4] =
{
- { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower-right
- { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower-left
- { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper-left (Unused)
- { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper-right (Unused)
+ { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
+ { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
+ { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
+ { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused)
};
+struct ImGuiResizeBorderDef
+{
+ ImVec2 InnerDir;
+ ImVec2 CornerPosN1, CornerPosN2;
+ float OuterAngle;
+};
+
+static const ImGuiResizeBorderDef resize_border_def[4] =
+{
+ { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top
+ { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
+ { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom
+ { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left
+};
+
static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
{
ImRect rect = window->Rect();
- if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
- if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); // Top
- if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); // Right
- if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); // Bottom
- if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); // Left
+ if (thickness == 0.0f) rect.Max -= ImVec2(1, 1);
+ if (border_n == 0) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } // Top
+ if (border_n == 1) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } // Right
+ if (border_n == 2) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } // Bottom
+ if (border_n == 3) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } // Left
IM_ASSERT(0);
return ImRect();
}
@@ -5086,7 +5065,7 @@
// Handle resize for: Resize Grips, Borders, Gamepad
// Return true when using auto-fit (double click on resize grip)
-static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
+static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
{
ImGuiContext& g = *GImGui;
ImGuiWindowFlags flags = window->Flags;
@@ -5138,6 +5117,9 @@
// Resize from any of the four corners
// We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip
+ ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
+ ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
+ corner_target = ImClamp(corner_target, clamp_min, clamp_max);
CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
}
if (resize_grip_n == 0 || held || hovered)
@@ -5163,6 +5145,9 @@
if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right
if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom
if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left
+ ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX);
+ ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX);
+ border_target = ImClamp(border_target, clamp_min, clamp_max);
CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
}
}
@@ -5184,6 +5169,7 @@
{
const float NAV_RESIZE_SPEED = 600.0f;
nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
+ nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
g.NavWindowingToggleLayer = false;
g.NavDisableMouseHover = true;
resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
@@ -5208,11 +5194,13 @@
return ret_auto_fit;
}
-static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
+static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
{
ImGuiContext& g = *GImGui;
- ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
- window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping);
+ ImVec2 size_for_clamping = window->Size;
+ if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
+ size_for_clamping.y = window->TitleBarHeight();
+ window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
}
static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
@@ -5226,23 +5214,10 @@
int border_held = window->ResizeBorderHeld;
if (border_held != -1)
{
- struct ImGuiResizeBorderDef
- {
- ImVec2 InnerDir;
- ImVec2 CornerPosN1, CornerPosN2;
- float OuterAngle;
- };
- static const ImGuiResizeBorderDef resize_border_def[4] =
- {
- { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
- { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
- { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
- { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left
- };
const ImGuiResizeBorderDef& def = resize_border_def[border_held];
ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
- window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle);
- window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f);
+ window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
+ window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
}
if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
@@ -5455,10 +5430,7 @@
ImGuiWindow* window = FindWindowByName(name);
const bool window_just_created = (window == NULL);
if (window_just_created)
- {
- ImVec2 size_on_first_use = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) ? 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);
- }
+ window = CreateNewWindow(name, flags);
// Automatically disable manual moving/resizing when NoInputs is set
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
@@ -5528,6 +5500,7 @@
UpdateWindowParentAndRootLinks(window, flags, parent_window);
// Process SetNextWindow***() calls
+ // (FIXME: Consider splitting the HasXXX flags into X/Y components
bool window_pos_set_by_api = false;
bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
@@ -5552,6 +5525,19 @@
window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
}
+ if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
+ {
+ if (g.NextWindowData.ScrollVal.x >= 0.0f)
+ {
+ window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
+ window->ScrollTargetCenterRatio.x = 0.0f;
+ }
+ if (g.NextWindowData.ScrollVal.y >= 0.0f)
+ {
+ window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
+ window->ScrollTargetCenterRatio.y = 0.0f;
+ }
+ }
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
else if (first_begin_of_the_frame)
@@ -5570,8 +5556,9 @@
const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
window->Active = true;
window->HasCloseButton = (p_open != NULL);
- window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
+ window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
window->IDStack.resize(1);
+ window->DrawList->_ResetForNewFrame();
// Restore buffer capacity when woken from a compacted state, to avoid
if (window->MemoryCompacted)
@@ -5580,7 +5567,7 @@
// Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
// The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.
bool window_title_visible_elsewhere = false;
- if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
+ if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
window_title_visible_elsewhere = true;
if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
{
@@ -5703,7 +5690,7 @@
if (window_just_activated_by_user)
{
window->AutoPosLastDirection = ImGuiDir_None;
- if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
+ if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
window->Pos = g.BeginPopupStack.back().OpenPopupPos;
}
@@ -5727,22 +5714,27 @@
else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
window->Pos = FindBestWindowPosForPopup(window);
+ // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
+ // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
+ ImRect viewport_rect(GetViewportRect());
+ ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
+ ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding);
+
// Clamp position/size so window stays visible within its viewport or monitor
// Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
- ImRect viewport_rect(GetViewportRect());
if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
- {
- ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
- if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
- {
- ClampWindowRect(window, viewport_rect, clamp_padding);
- }
- }
+ if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
+ ClampWindowRect(window, visibility_rect);
window->Pos = ImFloor(window->Pos);
// Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
+ // Large values tend to lead to variety of artifacts and are not recommended.
window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
+ // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
+ //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
+ // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
+
// Apply window focus (new and reactivated windows are moved to front)
bool want_focus = false;
if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
@@ -5759,7 +5751,7 @@
const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
if (!window->Collapsed)
- if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]))
+ if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
window->ResizeBorderHeld = (signed char)border_held;
@@ -5844,7 +5836,7 @@
// DRAWING
// Setup draw list and outer clipping rectangle
- window->DrawList->Clear();
+ IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
PushClipRect(host_rect.Min, host_rect.Max, false);
@@ -5943,7 +5935,7 @@
window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
window->DC.NavLayerActiveMaskNext = 0x00;
- window->DC.NavFocusScopeIdCurrent = parent_window ? parent_window->DC.NavFocusScopeIdCurrent : 0;
+ window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // -V595
window->DC.NavHideHighlightOneFrame = false;
window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
@@ -6151,20 +6143,19 @@
// Close popups if any
ClosePopupsOverWindow(window, false);
- // Passing NULL allow to disable keyboard focus
- if (!window)
- return;
-
// Move the root window to the top of the pile
- IM_ASSERT(window->RootWindow != NULL);
- ImGuiWindow* focus_front_window = window->RootWindow; // NB: In docking branch this is window->RootWindowDockStop
- ImGuiWindow* display_front_window = window->RootWindow;
+ IM_ASSERT(window == NULL || window->RootWindow != NULL);
+ ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
+ ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
// Steal focus on active widgets
- if (focus_front_window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement may be unnecessary? Need further testing before removing it..
- if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
- ClearActiveID();
+ if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
+ ClearActiveID();
+ // Passing NULL allow to disable keyboard focus
+ if (!window)
+ return;
+
// Bring to front
BringWindowToFocusFront(focus_front_window);
if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
@@ -6358,7 +6349,7 @@
}
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
-// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
+// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
// If you want a window to never be focused, you may use the e.g. NoInputs flag.
bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
{
@@ -6553,6 +6544,13 @@
g.NextWindowData.ContentSizeVal = size;
}
+void ImGui::SetNextWindowScroll(const ImVec2& scroll)
+{
+ ImGuiContext& g = *GImGui;
+ g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
+ g.NextWindowData.ScrollVal = scroll;
+}
+
void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
{
ImGuiContext& g = *GImGui;
@@ -6598,6 +6596,7 @@
void ImGui::SetWindowFontScale(float scale)
{
+ IM_ASSERT(scale > 0.0f);
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
window->FontWindowScale = scale;
@@ -6610,6 +6609,7 @@
g.NavNextActivateId = id;
}
+// Note: this is storing in same stack as IDStack, so Push/Pop mismatch will be reported there.
void ImGui::PushFocusScope(ImGuiID id)
{
ImGuiContext& g = *GImGui;
@@ -6667,32 +6667,41 @@
void ImGui::PushID(const char* str_id)
{
- ImGuiWindow* window = GImGui->CurrentWindow;
- window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiID id = window->GetIDNoKeepAlive(str_id);
+ window->IDStack.push_back(id);
}
void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
{
- ImGuiWindow* window = GImGui->CurrentWindow;
- window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
+ window->IDStack.push_back(id);
}
void ImGui::PushID(const void* ptr_id)
{
- ImGuiWindow* window = GImGui->CurrentWindow;
- window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
+ window->IDStack.push_back(id);
}
void ImGui::PushID(int int_id)
{
- ImGuiWindow* window = GImGui->CurrentWindow;
- window->IDStack.push_back(window->GetIDNoKeepAlive(int_id));
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiID id = window->GetIDNoKeepAlive(int_id);
+ window->IDStack.push_back(id);
}
// Push a given id value ignoring the ID stack as a seed.
void ImGui::PushOverrideID(ImGuiID id)
{
- ImGuiWindow* window = GImGui->CurrentWindow;
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
window->IDStack.push_back(id);
}
@@ -6759,6 +6768,14 @@
{
ImGuiContext& g = *GImGui;
+ // Check user IM_ASSERT macro
+ // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined!
+ // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
+ // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
+ // #define IM_ASSERT(EXPR) SomeCode(EXPR); SomeMoreCode(); // Wrong!
+ // #define IM_ASSERT(EXPR) do { SomeCode(EXPR); SomeMoreCode(); } while (0) // Correct!
+ if (true) IM_ASSERT(1); else IM_ASSERT(0);
+
// Check user data
// (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)
IM_ASSERT(g.Initialized);
@@ -6788,7 +6805,8 @@
{
ImGuiContext& g = *GImGui;
- // Verify that io.KeyXXX fields haven't been tampered with. Key mods shoudl not be modified between NewFrame() and EndFrame()
+ // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
+ // One possible reason leading to this assert is that your back-ends update inputs _AFTER_ NewFrame().
const ImGuiKeyModFlags expected_key_mod_flags = GetMergedKeyModFlags();
IM_ASSERT(g.IO.KeyMods == expected_key_mod_flags && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
IM_UNUSED(expected_key_mod_flags);
@@ -7090,10 +7108,10 @@
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiStyle& style = g.Style;
- const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
- const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
+ const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
+ const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
window->DC.ItemWidthStack.push_back(w_item_last);
- for (int i = 0; i < components-1; i++)
+ for (int i = 0; i < components - 1; i++)
window->DC.ItemWidthStack.push_back(w_item_one);
window->DC.ItemWidth = window->DC.ItemWidthStack.back();
g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
@@ -7514,7 +7532,7 @@
window->HiddenFramesCanSkipItems = 1;
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
}
- ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
+ ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
Begin(window_name, NULL, flags | extra_flags);
}
@@ -7543,22 +7561,51 @@
// [SECTION] POPUPS
//-----------------------------------------------------------------------------
-bool ImGui::IsPopupOpen(ImGuiID id)
+// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
+bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
{
ImGuiContext& g = *GImGui;
- return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
+ if (popup_flags & ImGuiPopupFlags_AnyPopupId)
+ {
+ // Return true if any popup is open at the current BeginPopup() level of the popup stack
+ // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
+ IM_ASSERT(id == 0);
+ if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
+ return g.OpenPopupStack.Size > 0;
+ else
+ return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
+ }
+ else
+ {
+ if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
+ {
+ // Return true if the popup is open anywhere in the popup stack
+ for (int n = 0; n < g.OpenPopupStack.Size; n++)
+ if (g.OpenPopupStack[n].PopupId == id)
+ return true;
+ return false;
+ }
+ else
+ {
+ // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
+ return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
+ }
+ }
}
-bool ImGui::IsPopupOpen(const char* str_id)
+bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
{
ImGuiContext& g = *GImGui;
- return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
+ ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
+ if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
+ IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
+ return IsPopupOpen(id, popup_flags);
}
ImGuiWindow* ImGui::GetTopMostPopupModal()
{
ImGuiContext& g = *GImGui;
- for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
+ for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
if (popup->Flags & ImGuiWindowFlags_Modal)
return popup;
@@ -7565,10 +7612,10 @@
return NULL;
}
-void ImGui::OpenPopup(const char* str_id)
+void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
{
ImGuiContext& g = *GImGui;
- OpenPopupEx(g.CurrentWindow->GetID(str_id));
+ OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
}
// Mark popup as open (toggle toward open state).
@@ -7575,11 +7622,16 @@
// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
-void ImGui::OpenPopupEx(ImGuiID id)
+void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* parent_window = g.CurrentWindow;
- int current_stack_size = g.BeginPopupStack.Size;
+ const int current_stack_size = g.BeginPopupStack.Size;
+
+ if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
+ if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
+ return;
+
ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
popup_ref.PopupId = id;
popup_ref.Window = NULL;
@@ -7606,8 +7658,8 @@
else
{
// Close child popups if any, then flag popup for open/reopen
- g.OpenPopupStack.resize(current_stack_size + 1);
- g.OpenPopupStack[current_stack_size] = popup_ref;
+ ClosePopupToLevel(current_stack_size, false);
+ g.OpenPopupStack.push_back(popup_ref);
}
// When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
@@ -7620,7 +7672,7 @@
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
{
ImGuiContext& g = *GImGui;
- if (g.OpenPopupStack.empty())
+ if (g.OpenPopupStack.Size == 0)
return;
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
@@ -7659,6 +7711,8 @@
{
ImGuiContext& g = *GImGui;
IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
+
+ // Trim open popup stack
ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
g.OpenPopupStack.resize(remaining);
@@ -7672,7 +7726,7 @@
}
else
{
- if (g.NavLayer == 0 && focus_window)
+ if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
focus_window = NavRestoreLastChildNavWindow(focus_window);
FocusWindow(focus_window);
}
@@ -7710,10 +7764,11 @@
window->DC.NavHideHighlightOneFrame = true;
}
+// Attention! BeginPopup() adds default flags which BeginPopupEx()!
bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
{
ImGuiContext& g = *GImGui;
- if (!IsPopupOpen(id))
+ if (!IsPopupOpen(id, ImGuiPopupFlags_None))
{
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
return false;
@@ -7752,18 +7807,19 @@
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiID id = window->GetID(name);
- if (!IsPopupOpen(id))
+ if (!IsPopupOpen(id, ImGuiPopupFlags_None))
{
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
return false;
}
- // Center modal windows by default
+ // Center modal windows by default for increased visibility
+ // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
// FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
- SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
+ SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
- flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
+ flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
const bool is_open = Begin(name, p_open, flags);
if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
{
@@ -7783,9 +7839,10 @@
IM_ASSERT(g.BeginPopupStack.Size > 0);
// Make all menus and popups wrap around for now, may need to expose that policy.
- NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
+ if (g.NavWindow == window)
+ NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
- // Child-popups don't need to be layed out
+ // Child-popups don't need to be laid out
IM_ASSERT(g.WithinEndChild == false);
if (window->Flags & ImGuiWindowFlags_ChildWindow)
g.WithinEndChild = true;
@@ -7793,14 +7850,15 @@
g.WithinEndChild = false;
}
-bool ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiMouseButton mouse_button)
+bool ImGui::OpenPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
{
ImGuiWindow* window = GImGui->CurrentWindow;
+ int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
{
ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
- OpenPopupEx(id);
+ OpenPopupEx(id, popup_flags);
return true;
}
return false;
@@ -7807,9 +7865,11 @@
}
// This is a helper to handle the simplest case of associating one named popup to one given widget.
-// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
-// You can pass a NULL str_id to use the identifier of the last item.
-bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiMouseButton mouse_button)
+// - You can pass a NULL str_id to use the identifier of the last item.
+// - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
+// - This is essentially the same as calling OpenPopupContextItem() + BeginPopup() but written to avoid
+// computing the ID twice because BeginPopupContextXXX functions are called very frequently.
+bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
{
ImGuiWindow* window = GImGui->CurrentWindow;
if (window->SkipItems)
@@ -7816,30 +7876,36 @@
return false;
ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
+ int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
- OpenPopupEx(id);
- return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
+ OpenPopupEx(id, popup_flags);
+ return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
}
-bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mouse_button, bool also_over_items)
+bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
{
+ ImGuiWindow* window = GImGui->CurrentWindow;
if (!str_id)
str_id = "window_context";
- ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
+ ImGuiID id = window->GetID(str_id);
+ int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
- if (also_over_items || !IsAnyItemHovered())
- OpenPopupEx(id);
- return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
+ if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
+ OpenPopupEx(id, popup_flags);
+ return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
}
-bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiMouseButton mouse_button)
+bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
{
+ ImGuiWindow* window = GImGui->CurrentWindow;
if (!str_id)
str_id = "void_context";
- ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
+ ImGuiID id = window->GetID(str_id);
+ int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
- OpenPopupEx(id);
- return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
+ if (GetTopMostPopupModal() == NULL)
+ OpenPopupEx(id, popup_flags);
+ return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
}
// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
@@ -8029,7 +8095,7 @@
}
}
-// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
+// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
{
ImGuiContext& g = *GImGui;
@@ -8037,7 +8103,7 @@
if (g.NavLayer != window->DC.NavLayerCurrent)
return false;
- 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)
+ const ImRect& curr = g.NavScoringRect; // 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++;
// When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
@@ -8058,7 +8124,7 @@
float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
if (dby != 0.0f && dbx != 0.0f)
- dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
+ dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
float dist_box = ImFabs(dbx) + ImFabs(dby);
// Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
@@ -8099,7 +8165,7 @@
ImDrawList* draw_list = GetForegroundDrawList(window);
draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
- draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150));
+ draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
}
else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
@@ -8113,7 +8179,7 @@
draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
}
}
- #endif
+#endif
// Is it in the quadrant we're interesting in moving to?
bool new_best = false;
@@ -8151,7 +8217,7 @@
// 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
// Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
- if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
+ if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
{
result->DistAxial = dist_axial;
@@ -8189,7 +8255,7 @@
// 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 || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav)))
+ if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
{
ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
#if IMGUI_DEBUG_NAV_SCORING
@@ -8262,36 +8328,11 @@
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
{
ImGuiContext& g = *GImGui;
- if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
- return;
- IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
- ImRect bb_rel = window->NavRectRel[0];
- ImGuiDir clip_dir = g.NavMoveDir;
- if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
- {
- bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
- if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
- }
- if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
- {
- bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
- if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
- }
- if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
- {
- bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
- if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
- }
- if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
- {
- bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
- if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
- NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
- }
+ // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
+ // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
+ g.NavWrapRequestWindow = window;
+ g.NavWrapRequestFlags = move_flags;
}
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
@@ -8421,6 +8462,8 @@
{
ImGuiContext& g = *GImGui;
g.IO.WantSetMousePos = false;
+ g.NavWrapRequestWindow = NULL;
+ g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
#if 0
if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("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
@@ -8507,7 +8550,7 @@
// Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
if (g.NavWindow)
NavSaveLastChildNavWindowIntoParent(g.NavWindow);
- if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
+ if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
g.NavWindow->NavLastChildNavWindow = NULL;
// Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
@@ -8544,7 +8587,7 @@
if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
}
- else if (g.NavLayer != 0)
+ else if (g.NavLayer != ImGuiNavLayer_Main)
{
// Leave the "menu" layer
NavRestoreLayer(ImGuiNavLayer_Main);
@@ -8618,6 +8661,7 @@
if (g.NavMoveDir != ImGuiDir_None)
{
g.NavMoveRequest = true;
+ g.NavMoveRequestKeyMods = g.IO.KeyMods;
g.NavMoveDirLast = g.NavMoveDir;
}
if (g.NavMoveRequest && g.NavId == 0)
@@ -8646,7 +8690,7 @@
// *Normal* Manual scroll with NavScrollXXX keys
// Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
- ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
+ ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
if (scroll_dir.x != 0.0f && window->ScrollbarX)
{
SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
@@ -8665,10 +8709,10 @@
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
- if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
+ if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == ImGuiNavLayer_Main)
{
ImGuiWindow* window = g.NavWindow;
- ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
+ ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
{
float pad = window->CalcFontSize() * 0.5f;
@@ -8680,12 +8724,12 @@
}
// For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
- ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
- g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
- g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
- g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
- g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
- IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
+ ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
+ g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
+ g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
+ g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
+ g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
+ IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
//GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
g.NavScoringCount = 0;
#if IMGUI_DEBUG_NAV_RECTS
@@ -8728,7 +8772,7 @@
IM_ASSERT(g.NavWindow && result->Window);
// Scroll to keep newly navigated item fully into view.
- if (g.NavLayer == 0)
+ if (g.NavLayer == ImGuiNavLayer_Main)
{
ImVec2 delta_scroll;
if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
@@ -8755,7 +8799,7 @@
// Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
g.NavJustMovedToId = result->ID;
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
-
+ g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
}
SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
g.NavMoveFromClampedRefRect = false;
@@ -8767,7 +8811,7 @@
ImGuiContext& g = *GImGui;
if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
return 0.0f;
- if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != 0)
+ if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
return 0.0f;
ImGuiWindow* window = g.NavWindow;
@@ -8833,10 +8877,72 @@
return 0.0f;
}
+static void ImGui::NavEndFrame()
+{
+ ImGuiContext& g = *GImGui;
+
+ // Show CTRL+TAB list window
+ if (g.NavWindowingTarget != NULL)
+ NavUpdateWindowingOverlay();
+
+ // Perform wrap-around in menus
+ ImGuiWindow* window = g.NavWrapRequestWindow;
+ ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
+ if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
+ {
+ IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
+ ImRect bb_rel = window->NavRectRel[0];
+
+ ImGuiDir clip_dir = g.NavMoveDir;
+ if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
+ {
+ bb_rel.Min.x = bb_rel.Max.x =
+ ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
+ if (move_flags & ImGuiNavMoveFlags_WrapX)
+ {
+ bb_rel.TranslateY(-bb_rel.GetHeight());
+ clip_dir = ImGuiDir_Up;
+ }
+ NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ }
+ if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
+ {
+ bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
+ if (move_flags & ImGuiNavMoveFlags_WrapX)
+ {
+ bb_rel.TranslateY(+bb_rel.GetHeight());
+ clip_dir = ImGuiDir_Down;
+ }
+ NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ }
+ if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
+ {
+ bb_rel.Min.y = bb_rel.Max.y =
+ ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
+ if (move_flags & ImGuiNavMoveFlags_WrapY)
+ {
+ bb_rel.TranslateX(-bb_rel.GetWidth());
+ clip_dir = ImGuiDir_Left;
+ }
+ NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ }
+ if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
+ {
+ bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
+ if (move_flags & ImGuiNavMoveFlags_WrapY)
+ {
+ bb_rel.TranslateX(+bb_rel.GetWidth());
+ clip_dir = ImGuiDir_Right;
+ }
+ NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
+ }
+ }
+}
+
static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
{
ImGuiContext& g = *GImGui;
- for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
+ for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
if (g.WindowsFocusOrder[i] == window)
return i;
return -1;
@@ -9030,8 +9136,8 @@
if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
return;
- if (g.NavWindowingList == NULL)
- g.NavWindowingList = FindWindowByName("###NavWindowingList");
+ if (g.NavWindowingListWindow == NULL)
+ g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
@@ -9320,7 +9426,7 @@
// FIXME-DRAG: Settle on a proper default visuals for drop target.
r.Expand(3.5f);
bool push_clip_rect = !window->ClipRect.Contains(r);
- if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
+ if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1));
window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
if (push_clip_rect) window->DrawList->PopClipRect();
}
@@ -9565,6 +9671,19 @@
//-----------------------------------------------------------------------------
// [SECTION] SETTINGS
//-----------------------------------------------------------------------------
+// - UpdateSettings() [Internal]
+// - MarkIniSettingsDirty() [Internal]
+// - CreateNewWindowSettings() [Internal]
+// - FindWindowSettings() [Internal]
+// - FindOrCreateWindowSettings() [Internal]
+// - FindSettingsHandler() [Internal]
+// - ClearIniSettings() [Internal]
+// - LoadIniSettingsFromDisk()
+// - LoadIniSettingsFromMemory()
+// - SaveIniSettingsToDisk()
+// - SaveIniSettingsToMemory()
+// - WindowSettingsHandler_***() [Internal]
+//-----------------------------------------------------------------------------
// Called by NewFrame()
void ImGui::UpdateSettings()
@@ -9647,16 +9766,6 @@
return CreateNewWindowSettings(name);
}
-void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
-{
- size_t file_data_size = 0;
- char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
- if (!file_data)
- return;
- LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
- IM_FREE(file_data);
-}
-
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
{
ImGuiContext& g = *GImGui;
@@ -9667,22 +9776,49 @@
return NULL;
}
+void ImGui::ClearIniSettings()
+{
+ ImGuiContext& g = *GImGui;
+ g.SettingsIniData.clear();
+ for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
+ if (g.SettingsHandlers[handler_n].ClearAllFn)
+ g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
+}
+
+void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
+{
+ size_t file_data_size = 0;
+ char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
+ if (!file_data)
+ return;
+ LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
+ IM_FREE(file_data);
+}
+
// Zero-tolerance, no error reporting, cheap .ini parsing
void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.Initialized);
- IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
+ //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
+ //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
// For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
// For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
if (ini_size == 0)
ini_size = strlen(ini_data);
- char* buf = (char*)IM_ALLOC(ini_size + 1);
- char* buf_end = buf + ini_size;
+ g.SettingsIniData.Buf.resize((int)ini_size + 1);
+ char* const buf = g.SettingsIniData.Buf.Data;
+ char* const buf_end = buf + ini_size;
memcpy(buf, ini_data, ini_size);
- buf[ini_size] = 0;
+ buf_end[0] = 0;
+ // Call pre-read handlers
+ // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
+ for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
+ if (g.SettingsHandlers[handler_n].ReadInitFn)
+ g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
+
void* entry_data = NULL;
ImGuiSettingsHandler* entry_handler = NULL;
@@ -9719,8 +9855,15 @@
entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
}
}
- IM_FREE(buf);
g.SettingsLoaded = true;
+
+ // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
+ memcpy(buf, ini_data, ini_size);
+
+ // Call post-read handlers
+ for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
+ if (g.SettingsHandlers[handler_n].ApplyAllFn)
+ g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
}
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
@@ -9756,11 +9899,21 @@
return g.SettingsIniData.c_str();
}
+static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
+{
+ ImGuiContext& g = *ctx;
+ for (int i = 0; i != g.Windows.Size; i++)
+ g.Windows[i]->SettingsOffset = -1;
+ g.SettingsWindows.clear();
+}
+
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
{
- ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name));
- if (!settings)
- settings = ImGui::CreateNewWindowSettings(name);
+ ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
+ ImGuiID id = settings->ID;
+ *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
+ settings->ID = id;
+ settings->WantApply = true;
return (void*)settings;
}
@@ -9774,6 +9927,19 @@
else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
}
+// Apply to existing windows (if any)
+static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
+{
+ ImGuiContext& g = *ctx;
+ for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
+ if (settings->WantApply)
+ {
+ if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
+ ApplyWindowSettings(window, settings);
+ settings->WantApply = false;
+ }
+}
+
static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
{
// Gather data from windows that were active during this session
@@ -9837,10 +10003,11 @@
#endif
// Win32 clipboard implementation
+// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
static const char* GetClipboardTextFn_DefaultImpl(void*)
{
- static ImVector<char> buf_local;
- buf_local.clear();
+ ImGuiContext& g = *GImGui;
+ g.ClipboardHandlerData.clear();
if (!::OpenClipboard(NULL))
return NULL;
HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
@@ -9852,12 +10019,12 @@
if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
{
int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
- buf_local.resize(buf_len);
- ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, buf_local.Data, buf_len, NULL, NULL);
+ g.ClipboardHandlerData.resize(buf_len);
+ ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
}
::GlobalUnlock(wbuf_handle);
::CloseClipboard();
- return buf_local.Data;
+ return g.ClipboardHandlerData.Data;
}
static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
@@ -9919,13 +10086,14 @@
CFDataRef cf_data;
if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
{
- static ImVector<char> clipboard_text;
+ ImGuiContext& g = *GImGui;
+ g.ClipboardHandlerData.clear();
int length = (int)CFDataGetLength(cf_data);
- clipboard_text.resize(length + 1);
- CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data);
- clipboard_text[length] = 0;
+ g.ClipboardHandlerData.resize(length + 1);
+ CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
+ g.ClipboardHandlerData[length] = 0;
CFRelease(cf_data);
- return clipboard_text.Data;
+ return g.ClipboardHandlerData.Data;
}
}
}
@@ -9938,17 +10106,17 @@
static const char* GetClipboardTextFn_DefaultImpl(void*)
{
ImGuiContext& g = *GImGui;
- return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
+ return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
}
static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
{
ImGuiContext& g = *GImGui;
- g.PrivateClipboard.clear();
+ g.ClipboardHandlerData.clear();
const char* text_end = text + strlen(text);
- g.PrivateClipboard.resize((int)(text_end - text) + 1);
- memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
- g.PrivateClipboard[(int)(text_end - text)] = 0;
+ g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
+ memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
+ g.ClipboardHandlerData[(int)(text_end - text)] = 0;
}
#endif
@@ -10013,8 +10181,8 @@
// Debugging enums
enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
- enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersDesired, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type
- const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersDesired", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" };
+ enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type
+ const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" };
// State
static bool show_windows_rects = false;
@@ -10022,7 +10190,8 @@
static bool show_windows_begin_order = false;
static bool show_tables_rects = false;
static int show_tables_rect_type = TRT_WorkRect;
- static bool show_drawcmd_details = true;
+ static bool show_drawcmd_mesh = true;
+ static bool show_drawcmd_aabb = true;
// Basic info
ImGuiContext& g = *GImGui;
@@ -10053,7 +10222,7 @@
else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table->LastOuterHeight); }
else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
else if (rect_type == TRT_ColumnsContentHeadersUsed) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MinX + c->ContentWidthHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate
- else if (rect_type == TRT_ColumnsContentHeadersDesired) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MinX + c->ContentWidthHeadersDesired, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // "
+ else if (rect_type == TRT_ColumnsContentHeadersIdeal) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MinX + c->ContentWidthHeadersIdeal, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // "
else if (rect_type == TRT_ColumnsContentRowsFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MinX + c->ContentWidthRowsFrozen, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // "
else if (rect_type == TRT_ColumnsContentRowsUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y + table->LastFirstRowHeight, c->MinX + c->ContentWidthRowsUnfrozen, table->InnerClipRect.Max.y); } // "
IM_ASSERT(0);
@@ -10073,6 +10242,38 @@
return ImRect();
}
+ static void NodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb)
+ {
+ IM_ASSERT(show_mesh || show_aabb);
+ ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
+ ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
+
+ // Draw wire-frame version of all triangles
+ ImRect clip_rect = draw_cmd->ClipRect;
+ ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
+ ImDrawListFlags backup_flags = fg_draw_list->Flags;
+ fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
+ for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + draw_cmd->ElemCount); base_idx += 3)
+ {
+ ImVec2 triangle[3];
+ for (int n = 0; n < 3; n++)
+ {
+ ImVec2 p = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
+ triangle[n] = p;
+ vtxs_rect.Add(p);
+ }
+ if (show_mesh)
+ fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
+ }
+ // Draw bounding boxes
+ if (show_aabb)
+ {
+ fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
+ fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
+ }
+ fg_draw_list->Flags = backup_flags;
+ }
+
static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
{
bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
@@ -10079,7 +10280,7 @@
if (draw_list == ImGui::GetWindowDrawList())
{
ImGui::SameLine();
- ImGui::TextColored(ImVec4(1.0f,0.4f,0.4f,1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
+ ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
if (node_open) ImGui::TreePop();
return;
}
@@ -10106,19 +10307,12 @@
ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
char buf[300];
- ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd: %4d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
- pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId,
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
+ pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
- if (show_drawcmd_details && fg_draw_list && ImGui::IsItemHovered())
- {
- ImRect clip_rect = pcmd->ClipRect;
- ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
- for (unsigned int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
- vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
- fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255,0,255,255));
- fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(255,255,0,255));
- }
+ if (ImGui::IsItemHovered() && (show_drawcmd_mesh || show_drawcmd_aabb) && fg_draw_list)
+ NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb);
if (!pcmd_node_open)
continue;
@@ -10136,29 +10330,15 @@
// Display vertex information summary. Hover to get all triangles drawn in wire-frame
ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
ImGui::Selectable(buf);
- if (fg_draw_list && ImGui::IsItemHovered() && show_drawcmd_details)
- {
- // Draw wire-frame version of everything
- ImDrawListFlags backup_flags = fg_draw_list->Flags;
- fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
- ImRect clip_rect = pcmd->ClipRect;
- fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255));
- for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
- {
- ImVec2 triangle[3];
- for (int n = 0; n < 3; n++)
- triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
- fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
- }
- fg_draw_list->Flags = backup_flags;
- }
+ if (ImGui::IsItemHovered() && fg_draw_list)
+ NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false);
// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
- ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
+ ImGuiListClipper clipper(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
while (clipper.Step())
- for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
+ for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
{
- char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
+ char* buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
ImVec2 triangle[3];
for (int n = 0; n < 3; n++, idx_i++)
{
@@ -10217,6 +10397,12 @@
ImGui::GetForegroundDrawList()->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
if (!open)
return;
+
+ if (!window->WasActive)
+ ImGui::TextDisabled("Note: window is not currently visible.");
+ if (window->MemoryCompacted)
+ ImGui::TextDisabled("Note: some memory buffers have been compacted/freed.");
+
ImGuiWindowFlags flags = window->Flags;
NodeDrawList(window, window->DrawList, "DrawList");
ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y);
@@ -10246,6 +10432,12 @@
ImGui::TreePop();
}
+ static void NodeWindowSettings(ImGuiWindowSettings* settings)
+ {
+ ImGui::Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
+ settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
+ }
+
static void NodeTabBar(ImGuiTabBar* tab_bar)
{
// Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
@@ -10253,6 +10445,7 @@
char* p = buf;
const char* buf_end = buf + IM_ARRAYSIZE(buf);
p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
+ IM_UNUSED(p);
if (ImGui::TreeNode(tab_bar, "%s", buf))
{
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
@@ -10280,6 +10473,24 @@
ImGui::TreePop();
}
+ static void NodeTableSettings(ImGuiTableSettings* settings)
+ {
+ if (!ImGui::TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount))
+ return;
+ ImGui::BulletText("SaveFlags: 0x%08X", settings->SaveFlags);
+ ImGui::BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax);
+ for (int n = 0; n < settings->ColumnsCount; n++)
+ {
+ ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n];
+ ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None;
+ ImGui::BulletText("Column %d Order %d SortOrder %2d %s Visible %d UserID 0x%08X WidthOrWeight %.3f",
+ n, column_settings->DisplayOrder, column_settings->SortOrder,
+ (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---",
+ column_settings->IsVisible, column_settings->UserID, column_settings->WidthOrWeight);
+ }
+ ImGui::TreePop();
+ }
+
static void NodeTable(ImGuiTable* table)
{
char buf[256];
@@ -10291,20 +10502,24 @@
ImGui::GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
if (open)
{
+ ImGui::BulletText("OuterWidth: %.1f, InnerWidth: %.1f%s", table->OuterRect.GetWidth(), table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : "");
+ ImGui::BulletText("ColumnsWidth: %.1f, AutoFitWidth: %.1f", table->ColumnsTotalWidth, table->ColumnsAutoFitWidth);
+ ImGui::BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder);
+ ImGui::BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn);
for (int n = 0; n < table->ColumnsCount; n++)
{
- ImGuiTableColumn* column = &table->Columns[n];
+ const ImGuiTableColumn* column = &table->Columns[n];
const char* name = TableGetColumnName(table, n);
ImGui::BulletText("Column %d order %d name '%s': +%.1f to +%.1f\n"
- "Active: %d, Clipped: %d, DrawChannels: %d,%d\n"
- "WidthGiven/Requested: %.1f/%.1f, Weight: %.2f\n"
- "ContentWidth: RowsFrozen %d, RowsUnfrozen %d, HeadersUsed/Desired %d/%d\n"
+ "Visible: %d, Clipped: %d, DrawChannels: %d,%d\n"
+ "WidthGiven/Request: %.2f/%.2f, WidthWeight: %.3f\n"
+ "ContentWidth: RowsFrozen %d, RowsUnfrozen %d, HeadersUsed/Ideal %d/%d\n"
"SortOrder: %d, SortDir: %s\n"
"UserID: 0x%08X, Flags: 0x%04X: %s%s%s%s..",
n, column->DisplayOrder, name ? name : "NULL", column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x,
- column->IsActive, column->IsClipped, column->DrawChannelRowsBeforeFreeze, column->DrawChannelRowsAfterFreeze,
- column->WidthGiven, column->WidthRequested, column->ResizeWeight,
- column->ContentWidthRowsFrozen, column->ContentWidthRowsUnfrozen, column->ContentWidthHeadersUsed, column->ContentWidthHeadersDesired,
+ column->IsVisible, column->IsClipped, column->DrawChannelRowsBeforeFreeze, column->DrawChannelRowsAfterFreeze,
+ column->WidthGiven, column->WidthRequest, column->WidthStretchWeight,
+ column->ContentWidthRowsFrozen, column->ContentWidthRowsUnfrozen, column->ContentWidthHeadersUsed, column->ContentWidthHeadersIdeal,
column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? "Ascending" : (column->SortDirection == ImGuiSortDirection_Descending) ? "Descending" : "None",
column->UserID, column->Flags,
(column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "",
@@ -10312,94 +10527,13 @@
(column->Flags & ImGuiTableColumnFlags_WidthAlwaysAutoResize) ? "WidthAlwaysAutoResize " : "",
(column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : "");
}
- ImGuiTableSettings* settings = TableFindSettings(table);
- if (settings && ImGui::TreeNode("Settings"))
- {
- ImGui::BulletText("SaveFlags: 0x%08X", settings->SaveFlags);
- ImGui::BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax);
- for (int n = 0; n < settings->ColumnsCount; n++)
- {
- ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n];
- ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None;
- ImGui::BulletText("Column %d Order %d SortOrder %d %s Visible %d UserID 0x%08X WidthOrWeight %.3f",
- n, column_settings->DisplayOrder, column_settings->SortOrder,
- (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---",
- column_settings->Visible, column_settings->UserID, column_settings->WidthOrWeight);
- }
- ImGui::TreePop();
- }
+ if (ImGuiTableSettings* settings = TableGetBoundSettings(table))
+ NodeTableSettings(settings);
ImGui::TreePop();
}
}
};
- Funcs::NodeWindows(g.Windows, "Windows");
- //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder");
- if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
- {
- for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
- Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
- ImGui::TreePop();
- }
-
- // Details for Popups
- if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
- {
- for (int i = 0; i < g.OpenPopupStack.Size; i++)
- {
- ImGuiWindow* window = g.OpenPopupStack[i].Window;
- ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
- }
- ImGui::TreePop();
- }
-
- // Details for TabBars
- if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
- {
- for (int n = 0; n < g.TabBars.GetSize(); n++)
- Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
- ImGui::TreePop();
- }
-
- // Details for Tables
-#ifdef IMGUI_HAS_TABLE
- if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
- {
- for (int n = 0; n < g.Tables.GetSize(); n++)
- Funcs::NodeTable(g.Tables.GetByIndex(n));
- ImGui::TreePop();
- }
-#endif // #define IMGUI_HAS_TABLE
-
- // Details for Docking
-#ifdef IMGUI_HAS_DOCK
- if (ImGui::TreeNode("Docking"))
- {
- ImGui::TreePop();
- }
-#endif // #define IMGUI_HAS_DOCK
-
- // Misc Details
- if (ImGui::TreeNode("Internal state"))
- {
- const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
- ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
- ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
- ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
- ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
- ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
- ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
- ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
- ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
- ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
- ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
- ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
- ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
- ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
- ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
- ImGui::TreePop();
- }
-
// Tools
if (ImGui::TreeNode("Tools"))
{
@@ -10435,7 +10569,7 @@
for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
{
ImGuiTable* table = g.Tables.GetByIndex(table_n);
- if (table->LastFrameActive < g.FrameCount - 1 || table->OuterWindow != g.NavWindow)
+ if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
continue;
ImGui::BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
@@ -10468,10 +10602,127 @@
}
}
- ImGui::Checkbox("Show details when hovering ImDrawCmd node", &show_drawcmd_details);
+ ImGui::Checkbox("Show mesh when hovering ImDrawCmd", &show_drawcmd_mesh);
+ ImGui::Checkbox("Show bounding boxes when hovering ImDrawCmd", &show_drawcmd_aabb);
ImGui::TreePop();
}
+ // Contents
+ Funcs::NodeWindows(g.Windows, "Windows");
+ //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder");
+ if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
+ {
+ for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
+ Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
+ ImGui::TreePop();
+ }
+
+ // Details for Popups
+ if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
+ {
+ for (int i = 0; i < g.OpenPopupStack.Size; i++)
+ {
+ ImGuiWindow* window = g.OpenPopupStack[i].Window;
+ ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
+ }
+ ImGui::TreePop();
+ }
+
+ // Details for TabBars
+ if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
+ {
+ for (int n = 0; n < g.TabBars.GetSize(); n++)
+ Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
+ ImGui::TreePop();
+ }
+
+ // Details for Tables
+#ifdef IMGUI_HAS_TABLE
+ if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
+ {
+ for (int n = 0; n < g.Tables.GetSize(); n++)
+ Funcs::NodeTable(g.Tables.GetByIndex(n));
+ ImGui::TreePop();
+ }
+#endif // #ifdef IMGUI_HAS_TABLE
+
+ // Details for Docking
+#ifdef IMGUI_HAS_DOCK
+ if (ImGui::TreeNode("Docking"))
+ {
+ ImGui::TreePop();
+ }
+#endif // #ifdef IMGUI_HAS_DOCK
+
+ // Settings
+ if (ImGui::TreeNode("Settings"))
+ {
+ if (ImGui::SmallButton("Clear"))
+ ImGui::ClearIniSettings();
+ ImGui::SameLine();
+ if (ImGui::SmallButton("Save to disk"))
+ ImGui::SaveIniSettingsToDisk(g.IO.IniFilename);
+ ImGui::SameLine();
+ if (g.IO.IniFilename)
+ ImGui::Text("\"%s\"", g.IO.IniFilename);
+ else
+ ImGui::TextUnformatted("<NULL>");
+ ImGui::Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
+ if (ImGui::TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
+ {
+ for (int n = 0; n < g.SettingsHandlers.Size; n++)
+ ImGui::TextUnformatted(g.SettingsHandlers[n].TypeName);
+ ImGui::TreePop();
+ }
+ if (ImGui::TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
+ {
+ for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
+ Funcs::NodeWindowSettings(settings);
+ ImGui::TreePop();
+ }
+
+#ifdef IMGUI_HAS_TABLE
+ if (ImGui::TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
+ {
+ for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
+ Funcs::NodeTableSettings(settings);
+ ImGui::TreePop();
+ }
+#endif // #ifdef IMGUI_HAS_TABLE
+
+#ifdef IMGUI_HAS_DOCK
+#endif // #ifdef IMGUI_HAS_DOCK
+
+ if (ImGui::TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
+ {
+ char* buf = (char*)(void*)(g.SettingsIniData.Buf.Data ? g.SettingsIniData.Buf.Data : "");
+ ImGui::InputTextMultiline("##Ini", buf, g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, 0.0f), ImGuiInputTextFlags_ReadOnly);
+ ImGui::TreePop();
+ }
+ ImGui::TreePop();
+ }
+
+ // Misc Details
+ if (ImGui::TreeNode("Internal state"))
+ {
+ const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
+ ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
+ ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
+ ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
+ ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
+ ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
+ ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
+ ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
+ ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
+ ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
+ ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
+ ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
+ ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
+ ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
+ ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
+ ImGui::TreePop();
+ }
+
// Overlay: Display windows Rectangles and Begin Order
if (show_windows_rects || show_windows_begin_order)
{
@@ -10524,7 +10775,7 @@
}
}
}
-#endif // #define IMGUI_HAS_TABLE
+#endif // #ifdef IMGUI_HAS_TABLE
#ifdef IMGUI_HAS_DOCK
// Overlay: Display Docking info
@@ -10531,7 +10782,7 @@
if (show_docking_nodes && g.IO.KeyCtrl)
{
}
-#endif // #define IMGUI_HAS_DOCK
+#endif // #ifdef IMGUI_HAS_DOCK
ImGui::End();
}
--- a/DoConfig/imgui/imgui.h
+++ b/DoConfig/imgui/imgui.h
@@ -1,10 +1,10 @@
-// dear imgui, v1.76 WIP
+// dear imgui, v1.78 WIP
// (headers)
// Help:
// - Read FAQ at http://dearimgui.org/faq
// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
-// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. All applications in examples/ are doing that.
+// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
// Read imgui.cpp for details, links and comments.
// Resources:
@@ -61,8 +61,8 @@
// Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
-#define IMGUI_VERSION "1.76 WIP"
-#define IMGUI_VERSION_NUM 17502
+#define IMGUI_VERSION "1.78 WIP"
+#define IMGUI_VERSION_NUM 17702
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
// Define attributes of all API symbols declarations (e.g. for DLL under Windows)
@@ -87,8 +87,8 @@
#define IM_FMTARGS(FMT)
#define IM_FMTLIST(FMT)
#endif
-#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*_ARR))) // Size of a static C-style array. Don't use on pointers!
-#define IM_UNUSED(_VAR) ((void)_VAR) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
+#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers!
+#define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
#if (__cplusplus >= 201100)
#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11
#else
@@ -166,11 +166,12 @@
typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc.
typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline()
typedef int ImGuiKeyModFlags; // -> enum ImGuiKeyModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super)
+typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar()
typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem()
typedef int ImGuiTableFlags; // -> enum ImGuiTableFlags_ // Flags: For BeginTable()
-typedef int ImGuiTableColumnFlags; // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn()
+typedef int ImGuiTableColumnFlags; // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn()
typedef int ImGuiTableRowFlags; // -> enum ImGuiTableRowFlags_ // Flags: For TableNextRow()
typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader()
typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild()
@@ -180,7 +181,7 @@
typedef void* ImTextureID; // User data for rendering back-end to identify a texture. This is whatever to you want it to be! read the FAQ about ImTextureID for details.
#endif
typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string.
-typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data);
+typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data);
typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);
// Decoded character types
@@ -276,9 +277,10 @@
// Windows
// - Begin() = push window to the stack and start appending to it. End() = pop window from the stack.
- // - You may append multiple times to the same window during the same frame.
// - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window,
// which clicking will set the boolean to false when clicked.
+ // - You may append multiple times to the same window during the same frame by calling Begin()/End() pairs multiple times.
+ // Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin().
// - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting
// anything to the window. Always call a matching End() for each Begin() call, regardless of its return value!
// [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu,
@@ -293,8 +295,8 @@
// - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400).
// - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window.
// Always call a matching EndChild() for each BeginChild() call, regardless of its return value [as with Begin: this is due to legacy reason and inconsistent with most BeginXXX functions apart from the regular Begin() which behaves like BeginChild().]
- IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0);
- IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0);
+ IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0);
+ IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0);
IMGUI_API void EndChild();
// Windows Utilities
@@ -310,7 +312,7 @@
IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y)
// Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin).
- IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0,0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc.
+ IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc.
IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin()
IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints.
IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin()
@@ -318,7 +320,7 @@
IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin()
IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground.
IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects.
- IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects.
+ IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects.
IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed().
IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus().
IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes().
@@ -379,7 +381,7 @@
// Cursor / Layout
// - By "cursor" we mean the current output position.
// - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down.
- // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceeding widget.
+ // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget.
// - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API:
// Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos()
// Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions.
@@ -441,17 +443,17 @@
// Widgets: Main
// - Most widgets return true when the value has been changed or when pressed/selected
// - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.
- IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button
+ IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)); // button
IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text
IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape
- IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0));
- IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
+ IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0));
+ IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
IMGUI_API bool Checkbox(const char* label, bool* v);
IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value);
IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; }
IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer
- IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL);
+ IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1, 0), const char* overlay = NULL);
IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses
// Widgets: Combo Box
@@ -506,7 +508,7 @@
// - If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.
// - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc.
IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
- IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
+ IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0);
IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0);
@@ -527,7 +529,7 @@
IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);
IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);
- IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0)); // display a colored square/button, hover for details, return true when pressed.
+ IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0)); // display a colored square/button, hover for details, return true when pressed.
IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls.
// Widgets: Trees
@@ -553,14 +555,14 @@
// Widgets: Selectables
// - A selectable highlights when hovered, and can display another color when selected.
// - Neighbors selectable extend their highlight bounds in order to leave no gap between them. This is so a series of selected Selectable appear contiguous.
- IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height
- IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper.
+ IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height
+ IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper.
// Widgets: List Boxes
// - FIXME: To be consistent with all the newer API, ListBoxHeader/ListBoxFooter should in reality be called BeginListBox/EndListBox. Will rename them.
IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1);
IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1);
- IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. if the function return true, you can output elements then call ListBoxFooter() afterwards.
+ IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)); // use if you want to reimplement ListBox() will custom data or interactions. if the function return true, you can output elements then call ListBoxFooter() afterwards.
IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // "
IMGUI_API void ListBoxFooter(); // terminate the scrolling region. only call ListBoxFooter() if ListBoxHeader() returned true!
@@ -598,24 +600,41 @@
IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);
// Popups, Modals
- // The properties of popups windows are:
- // - They block normal mouse hovering detection outside them. (*)
- // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
- // - Their visibility state (~bool) is held internally by imgui instead of being held by the programmer as we are used to with regular Begin() calls.
- // User can manipulate the visibility state by calling OpenPopup().
- // - We default to use the right mouse (ImGuiMouseButton_Right=1) for the Popup Context functions.
- // (*) You can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even when normally blocked by a popup.
- // Those three properties are connected. The library needs to hold their visibility state because it can close popups at any time.
- IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
- IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returns true!
- IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mouse_button = 1); // helper to open and begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!
- IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiMouseButton mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window.
- IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiMouseButton mouse_button = 1); // helper to open and begin popup when clicked in void (where there are no imgui windows).
- IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // modal dialog (regular window with title bar, block interactions behind the modal window, can't close the modal window by clicking outside)
- IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true!
- IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, ImGuiMouseButton mouse_button = 1); // helper to open popup when clicked on last item (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors). return true when just opened.
- IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open at the current begin-ed level of the popup stack.
- IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup.
+ // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them.
+ // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
+ // - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls.
+ // - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time.
+ // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered().
+ // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack.
+ // This is sometimes leading to confusing mistakes. May rework this in the future.
+ // Popups: begin/end functions
+ // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window.
+ // - BeginPopupModal(): block every interactions behind the window, cannot be closed by user, add a dimming background, has a title bar.
+ IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it.
+ IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it.
+ IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true!
+ // Popups: open/close functions
+ // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options.
+ // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
+ // - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually.
+ // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options).
+ // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup().
+ IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!).
+ IMGUI_API bool OpenPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. return true when just opened. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors)
+ IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into.
+ // Popups: open+begin combined functions helpers
+ // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking.
+ // - They are convenient to easily create context menus, hence the name.
+ // - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future.
+ // - We exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter. Passing a mouse button to ImGuiPopupFlags is guaranteed to be legal.
+ IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!
+ IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window.
+ IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows).
+ // Popups: test function
+ // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack.
+ // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack.
+ // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open.
+ IMGUI_API bool IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0); // return true if the popup is open.
// Columns
// - You can also use SameLine(pos_x) to mimic simplified columns.
@@ -638,7 +657,7 @@
// - If you are using tables as a sort of grid, populating every columns with the same type of contents,
// you may prefer using TableNextCell() instead of TableNextRow() + TableSetColumnIndex().
// - See Demo->Tables for details.
- // - See ImGuiTableFlags_ enums for a description of available flags.
+ // - See ImGuiTableFlags_ and ImGuiTableColumnsFlags_ enums for a description of available flags.
#define IMGUI_HAS_TABLE 1
IMGUI_API bool BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true!
@@ -649,11 +668,12 @@
IMGUI_API const char* TableGetColumnName(int column_n = -1); // return NULL if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.
IMGUI_API bool TableGetColumnIsVisible(int column_n = -1); // return true if column is visible. Same value is also returned by TableNextCell() and TableSetColumnIndex(). Pass -1 to use current column.
IMGUI_API bool TableGetColumnIsSorted(int column_n = -1); // return true if column is included in the sort specs. Rarely used, can be useful to tell if a data change should trigger resort. Equivalent to test ImGuiTableSortSpecs's ->ColumnsMask & (1 << column_n). Pass -1 to use current column.
+ IMGUI_API int TableGetHoveredColumn(); // return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
// Tables: Headers & Columns declaration
// - Use TableSetupColumn() to specify label, resizing policy, default width, id, various other flags etc.
// - The name passed to TableSetupColumn() is used by TableAutoHeaders() and by the context-menu
// - Use TableAutoHeaders() to submit the whole header row, otherwise you may treat the header row as a regular row, manually call TableHeader() and other widgets.
- // - Headers are required to perform some interactions: reordering, sorting, context menu // FIXME-TABLE: remove context from this list!
+ // - Headers are required to perform some interactions: reordering, sorting, context menu (FIXME-TABLE: context menu should work without!)
IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = -1.0f, ImU32 user_id = 0);
IMGUI_API void TableAutoHeaders(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu
IMGUI_API void TableHeader(const char* label); // submit one header cell manually.
@@ -681,6 +701,7 @@
// Drag and Drop
// - [BETA API] API may evolve!
+ // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip as replacement)
IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource()
IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui.
IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true!
@@ -760,7 +781,7 @@
IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held?
IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down)
IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down)
- IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime.
+ IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true)
IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block.
IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available
IMGUI_API bool IsAnyMouseDown(); // is any mouse button held?
@@ -893,6 +914,26 @@
ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog
};
+// Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions.
+// - To be backward compatible with older API which took an 'int mouse_button = 1' argument, we need to treat
+// small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags.
+// It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags.
+// - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0.
+enum ImGuiPopupFlags_
+{
+ ImGuiPopupFlags_None = 0,
+ ImGuiPopupFlags_MouseButtonLeft = 0, // For BeginPopupContext*(): open on Left Mouse release. Guaranted to always be == 0 (same as ImGuiMouseButton_Left)
+ ImGuiPopupFlags_MouseButtonRight = 1, // For BeginPopupContext*(): open on Right Mouse release. Guaranted to always be == 1 (same as ImGuiMouseButton_Right)
+ ImGuiPopupFlags_MouseButtonMiddle = 2, // For BeginPopupContext*(): open on Middle Mouse release. Guaranted to always be == 2 (same as ImGuiMouseButton_Middle)
+ ImGuiPopupFlags_MouseButtonMask_ = 0x1F,
+ ImGuiPopupFlags_MouseButtonDefault_ = 1,
+ ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack
+ ImGuiPopupFlags_NoOpenOverItems = 1 << 6, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space
+ ImGuiPopupFlags_AnyPopupId = 1 << 7, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup.
+ ImGuiPopupFlags_AnyPopupLevel = 1 << 8, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level)
+ ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel
+};
+
// Flags for ImGui::Selectable()
enum ImGuiSelectableFlags_
{
@@ -941,7 +982,8 @@
ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker.
ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem()
ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
- ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()
+ ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()
+ ImGuiTabItemFlags_NoTooltip = 1 << 4 // Disable tooltip for the given tab
};
// Flags for ImGui::BeginTable()
@@ -973,8 +1015,8 @@
ImGuiTableFlags_BordersVFullHeight = 1 << 11, // Borders covers all rows even when Headers are being used. Allow resizing from any rows.
// Padding, Sizing
ImGuiTableFlags_NoClipX = 1 << 12, // Disable pushing clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow)
- ImGuiTableFlags_SizingPolicyFixedX = 1 << 13, // Default if ScrollX is on. Columns will default to use WidthFixed or WidthAlwaysAutoResize policy. Read description above for more details.
- ImGuiTableFlags_SizingPolicyStretchX = 1 << 14, // Default if ScrollX is off. Columns will default to use WidthStretch policy. Read description above for more details.
+ ImGuiTableFlags_SizingPolicyFixedX = 1 << 13, // Default if ScrollX is on. Columns will default to use _WidthFixed or _WidthAlwaysAutoResize policy. Read description above for more details.
+ ImGuiTableFlags_SizingPolicyStretchX = 1 << 14, // Default if ScrollX is off. Columns will default to use _WidthStretch policy. Read description above for more details.
ImGuiTableFlags_NoHeadersWidth = 1 << 15, // Disable header width contribution to automatic width calculation.
ImGuiTableFlags_NoHostExtendY = 1 << 16, // (FIXME-TABLE: Reword as SizingPolicy?) Disable extending past the limit set by outer_size.y, only meaningful when neither of ScrollX|ScrollY are set (data below the limit will be clipped and not visible)
ImGuiTableFlags_NoKeepColumnsVisible = 1 << 17, // (FIXME-TABLE) Disable code that keeps column always minimally visible when table width gets too small.
@@ -1039,12 +1081,12 @@
ImGuiFocusedFlags_None = 0,
ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused
ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy)
- ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use ImGui::GetIO().WantCaptureMouse instead.
+ ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!
ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows
};
// Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered()
-// Note: if you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that. Please read the FAQ!
+// Note: if you are trying to check whether your mouse should be dispatched to Dear ImGui or to your app, you should use 'io.WantCaptureMouse' instead! Please read the FAQ!
// Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls.
enum ImGuiHoveredFlags_
{
@@ -1156,7 +1198,7 @@
ImGuiKeyModFlags_Super = 1 << 3
};
-// Gamepad/Keyboard directional navigation
+// Gamepad/Keyboard navigation
// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Back-end: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame().
// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://goo.gl/9LgVZW.
@@ -1283,7 +1325,7 @@
};
// Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure.
-// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.
+// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.
// During initialization or between frames, feel free to just poke into ImGuiStyle directly.
// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description.
// In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
@@ -1356,13 +1398,13 @@
// Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to
// override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup.
- ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_PickerHueBar,
+ ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar,
// [Internal] Masks
- ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_DisplayHSV|ImGuiColorEditFlags_DisplayHex,
- ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_Float,
- ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel|ImGuiColorEditFlags_PickerHueBar,
- ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_InputHSV
+ ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex,
+ ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float,
+ ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar,
+ ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -1407,8 +1449,9 @@
// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always.
enum ImGuiCond_
{
- ImGuiCond_Always = 1 << 0, // Set the variable
- ImGuiCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call with succeed)
+ ImGuiCond_None = 0, // No condition (always set the variable), same as _Always
+ ImGuiCond_Always = 1 << 0, // No condition (always set the variable)
+ ImGuiCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call will succeed)
ImGuiCond_FirstUseEver = 1 << 2, // Set the variable if the object/window has no persistently saved data (no entry in .ini file)
ImGuiCond_Appearing = 1 << 3 // Set the variable if the object/window is appearing after being hidden/inactive (or the first time)
};
@@ -1476,7 +1519,7 @@
inline const T& back() const { IM_ASSERT(Size > 0); return Data[Size - 1]; }
inline void swap(ImVector<T>& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }
- inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > sz ? new_capacity : sz; }
+ inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
inline void resize(int new_size, const T& v) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }
inline void shrink(int new_size) { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation
@@ -1486,10 +1529,10 @@
inline void push_back(const T& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
inline void pop_back() { IM_ASSERT(Size > 0); Size--; }
inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); }
- inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }
- inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data+Size && it_last > it && it_last <= Data+Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; }
- inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; if (it < Data+Size-1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; }
- inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; }
+ inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }
+ inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; }
+ inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; }
+ inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; }
inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
inline T* find(const T& v) { T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
inline const T* find(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
@@ -1509,7 +1552,7 @@
{
float Alpha; // Global alpha applies to everything in Dear ImGui.
ImVec2 WindowPadding; // Padding within a window.
- float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows.
+ float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).
ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constraint individual windows, use SetNextWindowSizeConstraints().
ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered.
@@ -1533,6 +1576,7 @@
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
float TabBorderSize; // Thickness of border around tabs.
+ float TabMinWidthForUnselectedCloseButton; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).
ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
@@ -1626,8 +1670,8 @@
// Input - Fill before calling NewFrame()
//------------------------------------------------------------------
- ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX,-FLT_MAX) if mouse is unavailable (on another screen, etc.)
- bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
+ ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)
+ bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text.
float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends.
bool KeyCtrl; // Keyboard modifier pressed: Control
@@ -1644,17 +1688,19 @@
IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually
//------------------------------------------------------------------
- // Output - Retrieve after calling NewFrame()
+ // Output - Updated by NewFrame() or EndFrame()/Render()
+ // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is
+ // generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!)
//------------------------------------------------------------------
- bool WantCaptureMouse; // When io.WantCaptureMouse is true, imgui will use the mouse inputs, do not dispatch them to your main game/application (in both cases, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
- bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, imgui will use the keyboard inputs, do not dispatch them to your main game/application (in both cases, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
- bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
- bool WantSetMousePos; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.
- bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. IMPORTANT: You need to clear io.WantSaveIniSettings yourself.
- bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
- bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
- float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames
+ bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
+ bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
+ bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
+ bool WantSetMousePos; // MousePos has been altered, back-end should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.
+ bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
+ bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
+ bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
+ float Framerate; // Application framerate estimate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames.
int MetricsRenderVertices; // Vertices output during last call to Render()
int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3
int MetricsRenderWindows; // Number of visible windows
@@ -1683,6 +1729,7 @@
float KeysDownDurationPrev[512]; // Previous duration the key has been down
float NavInputsDownDuration[ImGuiNavInput_COUNT];
float NavInputsDownDurationPrev[ImGuiNavInput_COUNT];
+ float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16
ImVector<ImWchar> InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper.
@@ -1749,7 +1796,7 @@
ImGuiID SourceId; // Source item id
ImGuiID SourceParentId; // Source parent id (if available)
int DataFrameCount; // Data timestamp
- char DataType[32+1]; // Data type tag (short user-supplied string, 32 characters max)
+ char DataType[32 + 1]; // Data type tag (short user-supplied string, 32 characters max)
bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets)
bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item.
@@ -1791,6 +1838,9 @@
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui
{
+ // OBSOLETED in 1.77 (from June 2020)
+ static inline bool OpenPopupOnItemClick(const char* str_id = NULL, ImGuiMouseButton mb = 1) { return OpenPopupContextItem(str_id, mb); } // Passing a mouse button to ImGuiPopupFlags is legal
+ static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); }
// OBSOLETED in 1.72 (from July 2019)
static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); }
// OBSOLETED in 1.71 (from June 2019)
@@ -1811,7 +1861,6 @@
// OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)
static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); }
static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }
- static inline ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = 0.f) { IM_UNUSED(on_edge); IM_UNUSED(outward); IM_ASSERT(0); return pos; }
}
typedef ImGuiInputTextCallback ImGuiTextEditCallback; // OBSOLETED in 1.63 (from Aug 2018): made the names consistent
typedef ImGuiInputTextCallbackData ImGuiTextEditCallbackData;
@@ -1998,8 +2047,8 @@
ImVec4 Value;
ImColor() { Value.x = Value.y = Value.z = Value.w = 0.0f; }
- ImColor(int r, int g, int b, int a = 255) { float sc = 1.0f/255.0f; Value.x = (float)r * sc; Value.y = (float)g * sc; Value.z = (float)b * sc; Value.w = (float)a * sc; }
- ImColor(ImU32 rgba) { float sc = 1.0f/255.0f; Value.x = (float)((rgba>>IM_COL32_R_SHIFT)&0xFF) * sc; Value.y = (float)((rgba>>IM_COL32_G_SHIFT)&0xFF) * sc; Value.z = (float)((rgba>>IM_COL32_B_SHIFT)&0xFF) * sc; Value.w = (float)((rgba>>IM_COL32_A_SHIFT)&0xFF) * sc; }
+ ImColor(int r, int g, int b, int a = 255) { float sc = 1.0f / 255.0f; Value.x = (float)r * sc; Value.y = (float)g * sc; Value.z = (float)b * sc; Value.w = (float)a * sc; }
+ ImColor(ImU32 rgba) { float sc = 1.0f / 255.0f; Value.x = (float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * sc; Value.y = (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * sc; Value.z = (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * sc; Value.w = (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * sc; }
ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; }
ImColor(const ImVec4& col) { Value = col; }
inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); }
@@ -2007,7 +2056,7 @@
// FIXME-OBSOLETE: May need to obsolete/cleanup those helpers.
inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; }
- static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); }
+ static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r, g, b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r, g, b, a); }
};
//-----------------------------------------------------------------------------
@@ -2033,19 +2082,21 @@
#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1)
// Typically, 1 command = 1 GPU draw call (unless command is a callback)
-// Pre 1.71 back-ends will typically ignore the VtxOffset/IdxOffset fields. When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset'
-// is enabled, those fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.
+// - VtxOffset/IdxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,
+// those fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.
+// Pre-1.71 back-ends will typically ignore the VtxOffset/IdxOffset fields.
+// - The ClipRect/TextureId/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for).
struct ImDrawCmd
{
- unsigned int ElemCount; // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].
- ImVec4 ClipRect; // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates
- ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas.
- unsigned int VtxOffset; // Start offset in vertex buffer. Pre-1.71 or without ImGuiBackendFlags_RendererHasVtxOffset: always 0. With ImGuiBackendFlags_RendererHasVtxOffset: may be >0 to support meshes larger than 64K vertices with 16-bit indices.
- unsigned int IdxOffset; // Start offset in index buffer. Always equal to sum of ElemCount drawn so far.
- ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.
- void* UserCallbackData; // The draw callback code can access this.
+ ImVec4 ClipRect; // 4*4 // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates
+ ImTextureID TextureId; // 4-8 // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas.
+ unsigned int VtxOffset; // 4 // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices.
+ unsigned int IdxOffset; // 4 // Start offset in index buffer. Always equal to sum of ElemCount drawn so far.
+ unsigned int ElemCount; // 4 // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].
+ ImDrawCallback UserCallback; // 4-8 // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.
+ void* UserCallbackData; // 4-8 // The draw callback code can access this.
- ImDrawCmd() { ElemCount = 0; TextureId = (ImTextureID)NULL; VtxOffset = IdxOffset = 0; UserCallback = NULL; UserCallbackData = NULL; }
+ ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed
};
// Vertex index, default to 16-bit
@@ -2136,7 +2187,6 @@
// [Internal, used while building lists]
const ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)
const char* _OwnerName; // Pointer to owner window's name for debugging
- unsigned int _VtxCurrentOffset; // [Internal] Always 0 unless 'Flags & ImDrawListFlags_AllowVtxOffset'.
unsigned int _VtxCurrentIdx; // [Internal] Generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
@@ -2143,11 +2193,13 @@
ImVector<ImVec4> _ClipRectStack; // [Internal]
ImVector<ImTextureID> _TextureIdStack; // [Internal]
ImVector<ImVec2> _Path; // [Internal] current path building
- ImDrawListSplitter _Splitter; // [Internal] for channels api
+ ImDrawCmd _CmdHeader; // [Internal] Template of active commands. Fields should match those of CmdBuffer.back().
+ ImDrawListSplitter _Splitter; // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)
// If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui)
- ImDrawList(const ImDrawListSharedData* shared_data) { _Data = shared_data; _OwnerName = NULL; Clear(); }
- ~ImDrawList() { ClearFreeMemory(); }
+ ImDrawList(const ImDrawListSharedData* shared_data) { _Data = shared_data; Flags = ImDrawListFlags_None; _VtxCurrentIdx = 0; _VtxWritePtr = NULL; _IdxWritePtr = NULL; _OwnerName = NULL; }
+
+ ~ImDrawList() { _ClearFreeMemory(); }
IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
IMGUI_API void PushClipRectFullScreen();
IMGUI_API void PopClipRect();
@@ -2190,7 +2242,7 @@
// Stateful path API, add points then finish with PathFillConvex() or PathStroke()
inline void PathClear() { _Path.Size = 0; }
inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); }
- inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); }
+ inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }
inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order.
inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; }
IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 10);
@@ -2207,26 +2259,31 @@
// - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives)
// - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end)
// - FIXME-OBSOLETE: This API shouldn't have been in ImDrawList in the first place!
- // Prefer using your own persistent copy of ImDrawListSplitter as you can stack them.
+ // Prefer using your own persistent instance of ImDrawListSplitter as you can stack them.
// Using the ImDrawList::ChannelsXXXX you cannot stack a split over another.
inline void ChannelsSplit(int count) { _Splitter.Split(this, count); }
inline void ChannelsMerge() { _Splitter.Merge(this); }
inline void ChannelsSetCurrent(int n) { _Splitter.SetCurrentChannel(this, n); }
- // Internal helpers
- // NB: all primitives needs to be reserved via PrimReserve() beforehand!
- IMGUI_API void Clear();
- IMGUI_API void ClearFreeMemory();
+ // Advanced: Primitives allocations
+ // - We render triangles (three vertices)
+ // - All primitives needs to be reserved via PrimReserve() beforehand.
IMGUI_API void PrimReserve(int idx_count, int vtx_count);
IMGUI_API void PrimUnreserve(int idx_count, int vtx_count);
IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); // Axis aligned rectangle (composed of two triangles)
IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col);
IMGUI_API void PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col);
- inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col){ _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; }
- inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; }
- inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); }
- IMGUI_API void UpdateClipRect();
- IMGUI_API void UpdateTextureID();
+ inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; }
+ inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; }
+ inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index
+
+ // [Internal helpers]
+ IMGUI_API void _ResetForNewFrame();
+ IMGUI_API void _ClearFreeMemory();
+ IMGUI_API void _PopUnusedDrawCmd();
+ IMGUI_API void _OnChangedClipRect();
+ IMGUI_API void _OnChangedTextureID();
+ IMGUI_API void _OnChangedVtxOffset();
};
// All draw data to render a Dear ImGui frame
@@ -2312,13 +2369,13 @@
// See ImFontAtlas::AddCustomRectXXX functions.
struct ImFontAtlasCustomRect
{
- unsigned int ID; // Input // User ID. Use < 0x110000 to map into a font glyph, >= 0x110000 for other/internal/custom texture data.
unsigned short Width, Height; // Input // Desired rectangle dimension
unsigned short X, Y; // Output // Packed position in Atlas
- float GlyphAdvanceX; // Input // For custom font glyphs only (ID < 0x110000): glyph xadvance
- ImVec2 GlyphOffset; // Input // For custom font glyphs only (ID < 0x110000): glyph display offset
- ImFont* Font; // Input // For custom font glyphs only (ID < 0x110000): target font
- ImFontAtlasCustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0,0); Font = NULL; }
+ unsigned int GlyphID; // Input // For custom font glyphs only (ID < 0x110000)
+ float GlyphAdvanceX; // Input // For custom font glyphs only: glyph xadvance
+ ImVec2 GlyphOffset; // Input // For custom font glyphs only: glyph display offset
+ ImFont* Font; // Input // For custom font glyphs only: target font
+ ImFontAtlasCustomRect() { Width = Height = 0; X = Y = 0xFFFF; GlyphID = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }
bool IsPacked() const { return X != 0xFFFF; }
};
@@ -2396,9 +2453,10 @@
// After calling Build(), you can query the rectangle position and render your pixels.
// You can also request your rectangles to be mapped as font glyph (given a font + Unicode point),
// so you can render e.g. custom colorful icons and use them as regular glyphs.
- // Read docs/FONTS.txt for more details about using colorful icons.
- IMGUI_API int AddCustomRectRegular(unsigned int id, int width, int height); // Id needs to be >= 0x110000. Id >= 0x80000000 are reserved for ImGui and ImDrawList
- IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0,0)); // Id needs to be < 0x110000 to register a rectangle to map into a specific font.
+ // Read docs/FONTS.md for more details about using colorful icons.
+ // Note: this API may be redesigned later in order to support multi-monitor varying DPI settings.
+ IMGUI_API int AddCustomRectRegular(int width, int height);
+ IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0, 0));
const ImFontAtlasCustomRect*GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; }
// [Internal]
@@ -2459,7 +2517,7 @@
float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale()
float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize]
int MetricsTotalSurface;// 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
- ImU8 Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations accross all used codepoints.
+ ImU8 Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints.
// Methods
IMGUI_API ImFont();
--- a/DoConfig/imgui/imgui_demo.cpp
+++ b/DoConfig/imgui/imgui_demo.cpp
@@ -1,41 +1,42 @@
-// dear imgui, v1.76 WIP
+// dear imgui, v1.78 WIP
// (demo code)
// Help:
// - Read FAQ at http://dearimgui.org/faq
// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
-// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. All applications in examples/ are doing that.
+// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
// Read imgui.cpp for more details, documentation and comments.
// Get latest version at https://github.com/ocornut/imgui
// Message to the person tempted to delete this file when integrating Dear ImGui into their code base:
-// Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other coders
-// will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of
-// your game/app! Removing this file from your project is hindering access to documentation for everyone in your team,
-// likely leading you to poorer usage of the library.
+// Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other
+// coders will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available
+// debug menu of your game/app! Removing this file from your project is hindering access to documentation for everyone
+// in your team, likely leading you to poorer usage of the library.
// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().
-// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be linked,
-// you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
+// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be
+// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
// In other situation, whenever you have Dear ImGui available you probably want this to be available for reference.
// Thank you,
// -Your beloved friend, imgui_demo.cpp (which you won't delete)
// Message to beginner C/C++ programmers about the meaning of the 'static' keyword:
-// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls, so it is
-// essentially like a global variable but declared inside the scope of the function. We do this as a way to gather code and data
-// in the same place, to make the demo source code faster to read, faster to write, and smaller in size.
-// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be
-// reentrant or used in multiple threads. This might be a pattern you will want to use in your code, but most of the real data
-// you would be editing is likely going to be stored outside your functions.
+// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls,
+// so it is essentially like a global variable but declared inside the scope of the function. We do this as a way to
+// gather code and data in the same place, to make the demo source code faster to read, faster to write, and smaller
+// in size. It also happens to be a convenient way of storing simple UI related information as long as your function
+// doesn't need to be reentrant or used in multiple threads. This might be a pattern you will want to use in your code,
+// but most of the real data you would be editing is likely going to be stored outside your functions.
// The Demo code in this file is designed to be easy to copy-and-paste in into your application!
// Because of this:
-// - We never omit the ImGui:: namespace when calling functions, even though most of our code is already in the same namespace.
+// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.
// - We try to declare static variables in the local scope, as close as possible to the code using them.
-// - We never use any of the helpers/facilities used internally by Dear ImGui, unless it has been exposed in the public API (imgui.h).
-// - We never use maths operators on ImVec2/ImVec4. For other of our sources files, they are provided by imgui_internal.h w/ IMGUI_DEFINE_MATH_OPERATORS.
-// For your own sources file they are optional and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
-// Because we don't want to assume anything about your support of maths operators, we don't use them in imgui_demo.cpp.
+// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.
+// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided
+// by imgui_internal.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional
+// and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
+// Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
/*
@@ -78,35 +79,37 @@
#include <stdint.h> // intptr_t
#endif
+// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#endif
+
+// Clang/GCC warnings with -Weverything
#if defined(__clang__)
-#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
-#pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning : 'xx' is deprecated: The POSIX name for this item.. // for strdup used in demo code (so user can copy & paste the code)
-#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
-#pragma clang diagnostic ignored "-Wformat-security" // warning : warning: format string is not a string literal
-#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
-#pragma clang diagnostic ignored "-Wunused-macros" // warning : warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used.
-#if __has_warning("-Wzero-as-null-pointer-constant")
-#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
#endif
-#if __has_warning("-Wdouble-promotion")
-#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
-#endif
-#if __has_warning("-Wreserved-id-macro")
-#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier //
-#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
+#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
+#pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning: 'xx' is deprecated: The POSIX name for this.. // for strdup used in demo code (so user can copy & paste the code)
+#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type
+#pragma clang diagnostic ignored "-Wformat-security" // warning: format string is not a string literal
+#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning: declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
+#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used.
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
+#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
+#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
+#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#elif defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
-#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
-#pragma GCC diagnostic ignored "-Wformat-security" // warning : format string is not a string literal (potentially insecure)
-#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 "-Wmisleading-indentation" // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub.
+#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
+#pragma GCC diagnostic ignored "-Wformat-security" // warning: format string is not a string literal (potentially insecure)
+#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 "-Wmisleading-indentation" // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub.
#endif
-// Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!)
+// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
#ifdef _WIN32
#define IM_NEWLINE "\r\n"
#else
@@ -113,6 +116,7 @@
#define IM_NEWLINE "\n"
#endif
+// Helpers
#if defined(_MSC_VER) && !defined(snprintf)
#define snprintf _snprintf
#endif
@@ -120,6 +124,14 @@
#define vsnprintf _vsnprintf
#endif
+// Helpers macros
+// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
+// but making an exception here as those are largely simplifying code...
+// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
+#define IM_MIN(A, B) (((A) < (B)) ? (A) : (B))
+#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
+#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
+
// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
#ifndef IMGUI_CDECL
#ifdef _MSC_VER
@@ -151,7 +163,7 @@
static void ShowExampleMenuFile();
// Helper to display a little (?) mark which shows a tooltip when hovered.
-// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.txt)
+// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
static void HelpMarker(const char* desc)
{
ImGui::TextDisabled("(?)");
@@ -170,7 +182,9 @@
{
ImGuiIO& io = ImGui::GetIO();
ImGui::BulletText("Double-click on title bar to collapse window.");
- ImGui::BulletText("Click and drag on lower corner to resize window\n(double-click to auto fit window to its contents).");
+ ImGui::BulletText(
+ "Click and drag on lower corner to resize window\n"
+ "(double-click to auto fit window to its contents).");
ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text.");
ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
if (io.FontAllowUserScaling)
@@ -206,7 +220,8 @@
// - ShowDemoWindowMisc()
//-----------------------------------------------------------------------------
-// We split the contents of the big ShowDemoWindow() function into smaller functions (because the link time of very large functions grow non-linearly)
+// We split the contents of the big ShowDemoWindow() function into smaller functions
+// (because the link time of very large functions grow non-linearly)
static void ShowDemoWindowWidgets();
static void ShowDemoWindowLayout();
static void ShowDemoWindowPopups();
@@ -215,10 +230,13 @@
static void ShowDemoWindowMisc();
// Demonstrate most Dear ImGui features (this is big function!)
-// You may execute this function to experiment with the UI and understand what it does. You may then search for keywords in the code when you are interested by a specific feature.
+// You may execute this function to experiment with the UI and understand what it does.
+// You may then search for keywords in the code when you are interested by a specific feature.
void ImGui::ShowDemoWindow(bool* p_open)
{
- IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!"); // Exceptionally add an extra assert here for people confused with initial dear imgui setup
+ // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
+ // Most ImGui functions would normally just crash if the context is missing.
+ IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!");
// Examples Apps (accessible from the "Examples" menu)
static bool show_app_documents = false;
@@ -252,9 +270,14 @@
static bool show_app_style_editor = false;
static bool show_app_about = false;
- if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); }
- if (show_app_style_editor) { ImGui::Begin("Style Editor", &show_app_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); }
- if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); }
+ if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); }
+ if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); }
+ if (show_app_style_editor)
+ {
+ ImGui::Begin("Dear ImGui Style Editor", &show_app_style_editor);
+ ImGui::ShowStyleEditor();
+ ImGui::End();
+ }
// Demonstrate the various window flags. Typically you would just use the default!
static bool no_titlebar = false;
@@ -280,7 +303,8 @@
if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
if (no_close) p_open = NULL; // Don't pass our bool* to Begin
- // We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming.
+ // We specify a default position/size in case there's no data in the .ini file.
+ // We only do it to make the demo applications a little more welcoming, but typically this isn't required.
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver);
@@ -292,10 +316,14 @@
return;
}
- // Most "big" widgets share a common width settings by default.
- //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // Use 2/3 of the space for widgets and 1/3 for labels (default)
- ImGui::PushItemWidth(ImGui::GetFontSize() * -12); // Use fixed width for labels (by passing a negative value), the rest goes to widgets. We choose a width proportional to our font size.
+ // Most "big" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details.
+ // e.g. Use 2/3 of the space for widgets and 1/3 for labels (default)
+ //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f);
+
+ // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets.
+ ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
+
// Menu Bar
if (ImGui::BeginMenuBar())
{
@@ -361,13 +389,15 @@
if (ImGui::TreeNode("Configuration##2"))
{
- ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
- ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
+ ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int*)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
+ ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int*)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
ImGui::SameLine(); HelpMarker("Required back-end to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
- ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos);
+ ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int*)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos);
ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos.");
- ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouse);
- if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) // Create a way to restore this flag otherwise we could be stuck completely!
+ ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", (unsigned int*)&io.ConfigFlags, ImGuiConfigFlags_NoMouse);
+
+ // The "NoMouse" option above can get us stuck with a disable mouse! Provide an alternative way to fix it:
+ if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
{
if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f)
{
@@ -377,7 +407,7 @@
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space)))
io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
}
- ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
+ ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int*)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
ImGui::SameLine(); HelpMarker("Instruct back-end to not alter mouse cursor shape and visibility.");
ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
ImGui::SameLine(); HelpMarker("Set to false to disable blinking cursor, for users who consider it distracting");
@@ -385,7 +415,7 @@
ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback.");
ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
- ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor for you. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).");
+ ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).");
ImGui::TreePop();
ImGui::Separator();
}
@@ -392,12 +422,16 @@
if (ImGui::TreeNode("Backend Flags"))
{
- HelpMarker("Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities.\nHere we expose then as read-only fields to avoid breaking interactions with your back-end.");
- ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying actual back-end flags.
- ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasGamepad);
- ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors);
- ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasSetMousePos);
- ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", (unsigned int *)&backend_flags, ImGuiBackendFlags_RendererHasVtxOffset);
+ HelpMarker(
+ "Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities.\n"
+ "Here we expose then as read-only fields to avoid breaking interactions with your back-end.");
+
+ // Make a local copy to avoid modifying actual back-end flags.
+ ImGuiBackendFlags backend_flags = io.BackendFlags;
+ ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int*)&backend_flags, ImGuiBackendFlags_HasGamepad);
+ ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int*)&backend_flags, ImGuiBackendFlags_HasMouseCursors);
+ ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", (unsigned int*)&backend_flags, ImGuiBackendFlags_HasSetMousePos);
+ ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", (unsigned int*)&backend_flags, ImGuiBackendFlags_RendererHasVtxOffset);
ImGui::TreePop();
ImGui::Separator();
}
@@ -412,10 +446,13 @@
if (ImGui::TreeNode("Capture/Logging"))
{
- ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded.");
- HelpMarker("Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
+ HelpMarker(
+ "The logging API redirects all text output so you can easily capture the content of "
+ "a window or a block. Tree nodes can be automatically expanded.\n"
+ "Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
ImGui::LogButtons();
- ImGui::TextWrapped("You can also call ImGui::LogText() to output directly to the log without a visual output.");
+
+ HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output.");
if (ImGui::Button("Copy \"Hello, world!\" to clipboard"))
{
ImGui::LogToClipboard();
@@ -481,15 +518,17 @@
if (i > 0)
ImGui::SameLine();
ImGui::PushID(i);
- ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.6f));
- ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.7f));
- ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i/7.0f, 0.8f, 0.8f));
+ ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));
ImGui::Button("Click");
ImGui::PopStyleColor(3);
ImGui::PopID();
}
- // Use AlignTextToFramePadding() to align text baseline to the baseline of framed elements (otherwise a Text+SameLine+Button sequence will have the text a little too high by default)
+ // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
+ // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
+ // See 'Demo->Layout->Text Baseline Alignment' for details.
ImGui::AlignTextToFramePadding();
ImGui::Text("Hold to repeat:");
ImGui::SameLine();
@@ -527,7 +566,7 @@
{
// Using the _simplified_ one-liner Combo() api here
// See "Combo" section for examples of how to use the more complete BeginCombo()/EndCombo() api.
- const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
+ const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" };
static int item_current = 0;
ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items));
ImGui::SameLine(); HelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n");
@@ -538,7 +577,18 @@
// see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
static char str0[128] = "Hello, world!";
ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0));
- ImGui::SameLine(); HelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp).");
+ ImGui::SameLine(); HelpMarker(
+ "USER:\n"
+ "Hold SHIFT or use mouse to select text.\n"
+ "CTRL+Left/Right to word jump.\n"
+ "CTRL+A or double-click to select all.\n"
+ "CTRL+X,CTRL+C,CTRL+V clipboard.\n"
+ "CTRL+Z,CTRL+Y undo/redo.\n"
+ "ESCAPE to revert.\n\n"
+ "PROGRAMMER:\n"
+ "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
+ "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
+ "in imgui_demo.cpp).");
static char str1[128] = "";
ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1));
@@ -545,7 +595,10 @@
static int i0 = 123;
ImGui::InputInt("input int", &i0);
- ImGui::SameLine(); HelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n");
+ ImGui::SameLine(); HelpMarker(
+ "You can apply arithmetic operators +,*,/ on numerical values.\n"
+ " e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\n"
+ "Use +- to subtract.");
static float f0 = 0.001f;
ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f");
@@ -555,7 +608,9 @@
static float f1 = 1.e10f;
ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e");
- ImGui::SameLine(); HelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n");
+ ImGui::SameLine(); HelpMarker(
+ "You can input value using the scientific notation,\n"
+ " e.g. \"1e+8\" becomes \"100000000\".");
static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
ImGui::InputFloat3("input float3", vec4a);
@@ -564,21 +619,24 @@
{
static int i1 = 50, i2 = 42;
ImGui::DragInt("drag int", &i1, 1);
- ImGui::SameLine(); HelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value.");
+ ImGui::SameLine(); HelpMarker(
+ "Click and drag to edit value.\n"
+ "Hold SHIFT/ALT for faster/slower edit.\n"
+ "Double-click or CTRL+click to input value.");
ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%");
- static float f1=1.00f, f2=0.0067f;
+ static float f1 = 1.00f, f2 = 0.0067f;
ImGui::DragFloat("drag float", &f1, 0.005f);
ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns");
}
{
- static int i1=0;
+ static int i1 = 0;
ImGui::SliderInt("slider int", &i1, -1, 3);
ImGui::SameLine(); HelpMarker("CTRL+click to input value.");
- static float f1=0.123f, f2=0.0f;
+ static float f1 = 0.123f, f2 = 0.0f;
ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
ImGui::SliderFloat("slider float (curve)", &f2, -10.0f, 10.0f, "%.4f", 2.0f);
@@ -589,18 +647,22 @@
// Here we completely omit '%d' from the format string, so it'll only display a name.
// This technique can also be used with DragInt().
enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };
- const char* element_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
- static int current_element = Element_Fire;
- const char* current_element_name = (current_element >= 0 && current_element < Element_COUNT) ? element_names[current_element] : "Unknown";
- ImGui::SliderInt("slider enum", ¤t_element, 0, Element_COUNT - 1, current_element_name);
+ static int elem = Element_Fire;
+ const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
+ const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
+ ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name);
ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer.");
}
{
- static float col1[3] = { 1.0f,0.0f,0.2f };
- static float col2[4] = { 0.4f,0.7f,0.0f,0.5f };
+ static float col1[3] = { 1.0f, 0.0f, 0.2f };
+ static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
ImGui::ColorEdit3("color 1", col1);
- ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n");
+ ImGui::SameLine(); HelpMarker(
+ "Click on the colored square to open a color picker.\n"
+ "Click and hold to use drag and drop.\n"
+ "Right-click on the colored square to show options.\n"
+ "CTRL+click on individual component to input value.\n");
ImGui::ColorEdit4("color 2", col2);
}
@@ -607,9 +669,9 @@
{
// List box
- const char* listbox_items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
- static int listbox_item_current = 1;
- ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4);
+ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
+ static int item_current = 1;
+ ImGui::ListBox("listbox\n(single select)", &item_current, items, IM_ARRAYSIZE(items), 4);
//static int listbox_item_current2 = 2;
//ImGui::SetNextItemWidth(-1);
@@ -631,8 +693,8 @@
{
for (int i = 0; i < 5; i++)
{
- // Use SetNextItemOpen() so set the default state of a node to be open.
- // We could also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
+ // Use SetNextItemOpen() so set the default state of a node to be open. We could
+ // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
if (i == 0)
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
@@ -649,23 +711,31 @@
if (ImGui::TreeNode("Advanced, with Selectable nodes"))
{
- HelpMarker("This is a more typical looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open.");
+ HelpMarker(
+ "This is a more typical looking tree with selectable nodes.\n"
+ "Click to select, CTRL+Click to toggle, click on arrows or double-click to open.");
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
static bool align_label_with_current_x_position = false;
- ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
+ static bool test_drag_and_drop = false;
+ ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);
- ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be layed out after the node.");
- ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_SpanFullWidth);
+ ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node.");
+ ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
+ ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop);
ImGui::Text("Hello!");
if (align_label_with_current_x_position)
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
- static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit.
- int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc.
+ // 'selection_mask' is dumb representation of what may be user-side selection state.
+ // You may retain selection state inside or outside your objects in whatever format you see fit.
+ // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end
+ /// of the loop. May be a pointer to your own node type, etc.
+ static int selection_mask = (1 << 2);
+ int node_clicked = -1;
for (int i = 0; i < 6; i++)
{
- // Disable the default open on single-click behavior and pass in Selected flag according to our selection state.
+ // Disable the default "open on single-click behavior" + set Selected flag according to our selection.
ImGuiTreeNodeFlags node_flags = base_flags;
const bool is_selected = (selection_mask & (1 << i)) != 0;
if (is_selected)
@@ -676,6 +746,12 @@
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i);
if (ImGui::IsItemClicked())
node_clicked = i;
+ if (test_drag_and_drop && ImGui::BeginDragDropSource())
+ {
+ ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
+ ImGui::Text("This is a drag and drop source");
+ ImGui::EndDragDropSource();
+ }
if (node_open)
{
ImGui::BulletText("Blah blah\nBlah Blah");
@@ -685,20 +761,27 @@
else
{
// Items 3..5 are Tree Leaves
- // The only reason we use TreeNode at all is to allow selection of the leaf.
- // Otherwise we can use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
+ // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
+ // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i);
if (ImGui::IsItemClicked())
node_clicked = i;
+ if (test_drag_and_drop && ImGui::BeginDragDropSource())
+ {
+ ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
+ ImGui::Text("This is a drag and drop source");
+ ImGui::EndDragDropSource();
+ }
}
}
if (node_clicked != -1)
{
- // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame.
+ // Update selection state
+ // (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
- else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection
+ else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked); // Click to single-select
}
if (align_label_with_current_x_position)
@@ -750,8 +833,8 @@
if (ImGui::TreeNode("Colored Text"))
{
// Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
- ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink");
- ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow");
+ ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Pink");
+ ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Yellow");
ImGui::TextDisabled("Disabled");
ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle.");
ImGui::TreePop();
@@ -766,21 +849,24 @@
static float wrap_width = 200.0f;
ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f");
- ImGui::Text("Test paragraph 1:");
- ImVec2 pos = ImGui::GetCursorScreenPos();
- ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255));
- ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
- ImGui::Text("The lazy dog is a good dog. This paragraph is made to fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width);
- ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255));
- ImGui::PopTextWrapPos();
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ for (int n = 0; n < 2; n++)
+ {
+ ImGui::Text("Test paragraph %d:", n);
+ ImVec2 pos = ImGui::GetCursorScreenPos();
+ ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);
+ ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());
+ ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
+ if (n == 0)
+ ImGui::Text("The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width);
+ if (n == 1)
+ ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh");
- ImGui::Text("Test paragraph 2:");
- pos = ImGui::GetCursorScreenPos();
- ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255));
- ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
- ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh");
- ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255));
- ImGui::PopTextWrapPos();
+ // Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
+ draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));
+ draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255));
+ ImGui::PopTextWrapPos();
+ }
ImGui::TreePop();
}
@@ -788,14 +874,19 @@
if (ImGui::TreeNode("UTF-8 Text"))
{
// UTF-8 test with Japanese characters
- // (Needs a suitable font, try Noto, or Arial Unicode, or M+ fonts. Read docs/FONTS.txt for details.)
+ // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
// - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
- // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. Visual Studio save your file as 'UTF-8 without signature')
- // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE.
- // Instead we are encoding a few strings with hexadecimal constants. Don't do this in your application!
- // Please use u8"text in any language" in your application!
- // Note that characters values are preserved even by InputText() if the font cannot be displayed, so you can safely copy & paste garbled characters into another application.
- ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->AddFontFromFileTTF() manually to load extra character ranges. Read docs/FONTS.txt for details.");
+ // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
+ // can save your source files as 'UTF-8 without signature').
+ // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
+ // CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
+ // Don't do this in your application! Please use u8"text in any language" in your application!
+ // Note that characters values are preserved even by InputText() if the font cannot be displayed,
+ // so you can safely copy & paste garbled characters into another application.
+ ImGui::TextWrapped(
+ "CJK text will only appears if the font was loaded with the appropriate CJK character ranges. "
+ "Call io.Font->AddFontFromFileTTF() manually to load extra character ranges. "
+ "Read docs/FONTS.md for details.");
ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we would use u8"blah blah" with the proper characters directly in the string.
ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
@@ -811,33 +902,50 @@
ImGuiIO& io = ImGui::GetIO();
ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!");
- // Here we are grabbing the font texture because that's the only one we have access to inside the demo code.
- // Remember that ImTextureID is just storage for whatever you want it to be, it is essentially a value that will be passed to the render function inside the ImDrawCmd structure.
- // If you use one of the default imgui_impl_XXXX.cpp renderer, they all have comments at the top of their file to specify what they expect to be stored in ImTextureID.
- // (for example, the imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier etc.)
- // If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers to ImGui::Image(), and gather width/height through your own functions, etc.
- // Using ShowMetricsWindow() as a "debugger" to inspect the draw data that are being passed to your render will help you debug issues if you are confused about this.
- // Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
+ // Below we are displaying the font texture because it is the only texture we have access to inside the demo!
+ // Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that
+ // will be passed to the rendering back-end via the ImDrawCmd structure.
+ // If you use one of the default imgui_impl_XXXX.cpp rendering back-end, they all have comments at the top
+ // of their respective source file to specify what they expect to be stored in ImTextureID, for example:
+ // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer
+ // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
+ // More:
+ // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
+ // to ImGui::Image(), and gather width/height through your own functions, etc.
+ // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
+ // it will help you debug issues if you are confused about it.
+ // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
+ // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
+ // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
ImTextureID my_tex_id = io.Fonts->TexID;
float my_tex_w = (float)io.Fonts->TexWidth;
float my_tex_h = (float)io.Fonts->TexHeight;
-
- ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
- ImVec2 pos = ImGui::GetCursorScreenPos();
- ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImVec4(1.0f,1.0f,1.0f,1.0f), ImVec4(1.0f,1.0f,1.0f,0.5f));
- if (ImGui::IsItemHovered())
{
- ImGui::BeginTooltip();
- float region_sz = 32.0f;
- float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz;
- float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz;
- float zoom = 4.0f;
- ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y);
- ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
- ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
- ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
- ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f));
- ImGui::EndTooltip();
+ ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
+ ImVec2 pos = ImGui::GetCursorScreenPos();
+ ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
+ ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
+ ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
+ ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white
+ ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col);
+ if (ImGui::IsItemHovered())
+ {
+ ImGui::BeginTooltip();
+ float region_sz = 32.0f;
+ float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
+ float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
+ float zoom = 4.0f;
+ if (region_x < 0.0f) { region_x = 0.0f; }
+ else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }
+ if (region_y < 0.0f) { region_y = 0.0f; }
+ else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }
+ ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y);
+ ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
+ ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
+ ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
+ ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, tint_col, border_col);
+ ImGui::EndTooltip();
+ }
}
ImGui::TextWrapped("And now some textured buttons..");
static int pressed_count = 0;
@@ -844,8 +952,13 @@
for (int i = 0; i < 8; i++)
{
ImGui::PushID(i);
- int frame_padding = -1 + i; // -1 = uses default padding
- if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImVec4(0.0f,0.0f,0.0f,1.0f)))
+ int frame_padding = -1 + i; // -1 == uses default padding (style.FramePadding)
+ ImVec2 size = ImVec2(32.0f, 32.0f); // Size of the image we want to make visible
+ ImVec2 uv0 = ImVec2(0.0f, 0.0f); // UV coordinates for lower-left
+ ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32 / my_tex_h); // UV coordinates for (32,32) in our texture
+ ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // Black background
+ ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint
+ if (ImGui::ImageButton(my_tex_id, size, uv0, uv1, frame_padding, bg_col, tint_col))
pressed_count += 1;
ImGui::PopID();
ImGui::SameLine();
@@ -866,19 +979,23 @@
if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", (unsigned int*)&flags, ImGuiComboFlags_NoPreview))
flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both
- // General BeginCombo() API, you have full control over your selection data and display type.
- // (your selection data could be an index, a pointer to the object, an id for the object, a flag stored in the object itself, etc.)
+ // Using the generic BeginCombo() API, you have full control over how to display the combo contents.
+ // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
+ // stored in the object itself, etc.)
const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
- static const char* item_current = items[0]; // Here our selection is a single pointer stored outside the object.
- if (ImGui::BeginCombo("combo 1", item_current, flags)) // The second parameter is the label previewed before opening the combo.
+ static int item_current_idx = 0; // Here our selection data is an index.
+ const char* combo_label = items[item_current_idx]; // Label to preview before opening the combo (technically could be anything)(
+ if (ImGui::BeginCombo("combo 1", combo_label, flags))
{
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
{
- bool is_selected = (item_current == items[n]);
+ const bool is_selected = (item_current_idx == n);
if (ImGui::Selectable(items[n], is_selected))
- item_current = items[n];
+ item_current_idx = n;
+
+ // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if (is_selected)
- ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
+ ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
@@ -902,9 +1019,11 @@
if (ImGui::TreeNode("Selectables"))
{
// Selectable() has 2 overloads:
- // - The one taking "bool selected" as a read-only selection information. When Selectable() has been clicked is returns true and you can alter selection state accordingly.
+ // - The one taking "bool selected" as a read-only selection information.
+ // When Selectable() has been clicked it returns true and you can alter selection state accordingly.
// - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
- // The earlier is more flexible, as in real application your selection may be stored in a different manner (in flags within objects, as an external list, etc).
+ // The earlier is more flexible, as in real application your selection may be stored in many different ways
+ // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
if (ImGui::TreeNode("Basic"))
{
static bool selection[5] = { false, true, false, false, false };
@@ -948,7 +1067,8 @@
}
if (ImGui::TreeNode("Rendering more text into the same line"))
{
- // Using the Selectable() override that takes "bool* p_selected" parameter and toggle your booleans automatically.
+ // Using the Selectable() override that takes "bool* p_selected" parameter,
+ // this function toggle your bool value automatically.
static bool selected[3] = { false, false, false };
ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes");
ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes");
@@ -970,13 +1090,17 @@
}
if (ImGui::TreeNode("Grid"))
{
- static bool selected[4*4] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true };
- for (int i = 0; i < 4*4; i++)
+ static int selected[4 * 4] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+ for (int i = 0; i < 4 * 4; i++)
{
ImGui::PushID(i);
- if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50)))
+ if (ImGui::Selectable("Sailor", selected[i] != 0, 0, ImVec2(50, 50)))
{
- // Note: We _unnecessarily_ test for both x/y and i here only to silence some static analyzer. The second part of each test is unnecessary.
+ // Toggle
+ selected[i] = !selected[i];
+
+ // Note: We _unnecessarily_ test for both x/y and i here only to silence some static analyzer.
+ // The second part of each test is unnecessary.
int x = i % 4;
int y = i / 4;
if (x > 0) { selected[i - 1] ^= 1; }
@@ -991,8 +1115,11 @@
}
if (ImGui::TreeNode("Alignment"))
{
- HelpMarker("By default, Selectables uses style.SelectableTextAlign but it can be overriden on a per-item basis using PushStyleVar(). You'll probably want to always keep your default situation to left-align otherwise it becomes difficult to layout multiple items on a same line");
- static bool selected[3*3] = { true, false, true, false, true, false, true, false, true };
+ HelpMarker(
+ "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item "
+ "basis using PushStyleVar(). You'll probably want to always keep your default situation to "
+ "left-align otherwise it becomes difficult to layout multiple items on a same line");
+ static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };
for (int y = 0; y < 3; y++)
{
for (int x = 0; x < 3; x++)
@@ -1002,7 +1129,7 @@
sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y);
if (x > 0) ImGui::SameLine();
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);
- ImGui::Selectable(name, &selected[3*y+x], ImGuiSelectableFlags_None, ImVec2(80,80));
+ ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80));
ImGui::PopStyleVar();
}
}
@@ -1042,12 +1169,22 @@
if (ImGui::TreeNode("Filtered Text Input"))
{
- static char buf1[64] = ""; ImGui::InputText("default", buf1, 64);
- static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal);
+ struct TextFilters
+ {
+ // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i'
+ static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)
+ {
+ if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar))
+ return 0;
+ return 1;
+ }
+ };
+
+ static char buf1[64] = ""; ImGui::InputText("default", buf1, 64);
+ static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal);
static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
- static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase);
- static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank);
- struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } };
+ static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase);
+ static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank);
static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters);
ImGui::Text("Password input");
@@ -1062,9 +1199,11 @@
if (ImGui::TreeNode("Resize Callback"))
{
// To wire InputText() with std::string or any other custom string type,
- // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper using your prefered type.
- // See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
- HelpMarker("Demonstrate using ImGuiInputTextFlags_CallbackResize to wire your resizable string type to InputText().\n\nSee misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
+ // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
+ // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
+ HelpMarker(
+ "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n"
+ "See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
struct Funcs
{
static int MyResizeCallback(ImGuiInputTextCallbackData* data)
@@ -1073,14 +1212,14 @@
{
ImVector<char>* my_str = (ImVector<char>*)data->UserData;
IM_ASSERT(my_str->begin() == data->Buf);
- my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
+ my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
data->Buf = my_str->begin();
}
return 0;
}
- // Tip: Because ImGui:: is a namespace you would typicall add your own function into the namespace in your own source files.
- // For example, you may add a function called ImGui::InputText(const char* label, MyString* my_str).
+ // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
+ // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
{
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
@@ -1089,7 +1228,8 @@
};
// For this demo we are using ImVector as a string container.
- // Note that because we need to store a terminating zero character, our size/capacity are 1 more than usually reported by a typical string class.
+ // Note that because we need to store a terminating zero character, our size/capacity are 1 more
+ // than usually reported by a typical string class.
static ImVector<char> my_str;
if (my_str.empty())
my_str.push_back(0);
@@ -1102,7 +1242,8 @@
}
// Plot/Graph widgets are currently fairly limited.
- // Consider writing your own plotting widget, or using a third-party one (see "Wiki->Useful Widgets", or github.com/ocornut/imgui/issues/2747)
+ // Consider writing your own plotting widget, or using a third-party one
+ // (for third-party Plot widgets, see 'Wiki->Useful Widgets' or https://github.com/ocornut/imgui/labels/plot%2Fgraph)
if (ImGui::TreeNode("Plots Widgets"))
{
static bool animate = true;
@@ -1112,19 +1253,20 @@
ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr));
// Create a dummy array of contiguous float values to plot
- // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter.
+ // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
+ // and the sizeof() of your structure in the "stride" parameter.
static float values[90] = {};
static int values_offset = 0;
static double refresh_time = 0.0;
if (!animate || refresh_time == 0.0)
refresh_time = ImGui::GetTime();
- while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo
+ while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 Hz rate for the demo
{
static float phase = 0.0f;
values[values_offset] = cosf(phase);
- values_offset = (values_offset+1) % IM_ARRAYSIZE(values);
- phase += 0.10f*values_offset;
- refresh_time += 1.0f/60.0f;
+ values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
+ phase += 0.10f * values_offset;
+ refresh_time += 1.0f / 60.0f;
}
// Plots can display overlay texts
@@ -1136,12 +1278,13 @@
average /= (float)IM_ARRAYSIZE(values);
char overlay[32];
sprintf(overlay, "avg %f", average);
- ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0,80));
+ ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f));
}
- ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80));
+ ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));
// Use functions to generate output
- // FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count.
+ // FIXME: This is rather awkward because current plot API only pass in indices.
+ // We probably want an API passing floats and user provide sample rate/count.
struct Funcs
{
static float Sin(void*, int i) { return sinf(i * 0.1f); }
@@ -1154,8 +1297,8 @@
ImGui::SameLine();
ImGui::SliderInt("Sample count", &display_count, 1, 400);
float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
- ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80));
- ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80));
+ ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
+ ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
ImGui::Separator();
// Animate a simple progress bar
@@ -1169,20 +1312,20 @@
// Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
// or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
- ImGui::ProgressBar(progress, ImVec2(0.0f,0.0f));
+ ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::Text("Progress Bar");
- float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress;
+ float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f);
char buf[32];
- sprintf(buf, "%d/%d", (int)(progress_saturated*1753), 1753);
- ImGui::ProgressBar(progress, ImVec2(0.f,0.f), buf);
+ sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753);
+ ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);
ImGui::TreePop();
}
if (ImGui::TreeNode("Color/Picker Widgets"))
{
- static ImVec4 color = ImVec4(114.0f/255.0f, 144.0f/255.0f, 154.0f/255.0f, 200.0f/255.0f);
+ static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);
static bool alpha_preview = true;
static bool alpha_half_preview = false;
@@ -1197,7 +1340,9 @@
ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions);
ImGui::Text("Color widget:");
- ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n");
+ ImGui::SameLine(); HelpMarker(
+ "Click on the colored square to open a color picker.\n"
+ "CTRL+click on individual component to input value.\n");
ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags);
ImGui::Text("Color widget HSV with Alpha:");
@@ -1207,7 +1352,10 @@
ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags);
ImGui::Text("Color button with Picker:");
- ImGui::SameLine(); HelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup.");
+ ImGui::SameLine(); HelpMarker(
+ "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n"
+ "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only "
+ "be used for the tooltip and picker popup.");
ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags);
ImGui::Text("Color button with Custom Picker Popup:");
@@ -1219,7 +1367,8 @@
{
for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
{
- ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);
+ ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f,
+ saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);
saved_palette[n].w = 1.0f; // Alpha
}
saved_palette_init = false;
@@ -1243,9 +1392,9 @@
ImGui::BeginGroup(); // Lock X position
ImGui::Text("Current");
- ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40));
+ ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40));
ImGui::Text("Previous");
- if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)))
+ if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)))
color = backup_color;
ImGui::Separator();
ImGui::Text("Palette");
@@ -1254,11 +1403,13 @@
ImGui::PushID(n);
if ((n % 8) != 0)
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
- if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20)))
+
+ ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
+ if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, ImVec2(20, 20)))
color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
- // Allow user to drop colors into each palette entry
- // (Note that ColorButton is already a drag source by default, unless using ImGuiColorEditFlags_NoDragDrop)
+ // Allow user to drop colors into each palette entry. Note that ColorButton() is already a
+ // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
@@ -1277,7 +1428,7 @@
ImGui::Text("Color button only:");
static bool no_border = false;
ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border);
- ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80,80));
+ ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80));
ImGui::Text("Color picker:");
static bool alpha = true;
@@ -1284,7 +1435,7 @@
static bool alpha_bar = true;
static bool side_preview = true;
static bool ref_color = false;
- static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f);
+ static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);
static int display_mode = 0;
static int picker_mode = 0;
ImGui::Checkbox("With Alpha", &alpha);
@@ -1301,7 +1452,10 @@
}
}
ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0");
- ImGui::SameLine(); HelpMarker("ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, but the user can change it with a right-click.\n\nColorPicker defaults to displaying RGB+HSV+Hex if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
+ ImGui::SameLine(); HelpMarker(
+ "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
+ "but the user can change it with a right-click.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
+ "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0");
ImGui::SameLine(); HelpMarker("User can right-click the picker to change mode.");
ImGuiColorEditFlags flags = misc_flags;
@@ -1316,8 +1470,12 @@
if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;
ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);
- ImGui::Text("Programmatically set defaults:");
- ImGui::SameLine(); HelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible.");
+ ImGui::Text("Set defaults in code:");
+ ImGui::SameLine(); HelpMarker(
+ "SetColorEditOptions() is designed to allow you to set boot-time default.\n"
+ "We don't have Push/Pop functions because you can force options on a per-widget basis if needed,"
+ "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid"
+ "encouraging you to persistently save values that aren't forward-compatible.");
if (ImGui::Button("Default: Uint8 + HSV + Hue Bar"))
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);
if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
@@ -1324,14 +1482,17 @@
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
// HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
- static ImVec4 color_stored_as_hsv(0.23f, 1.0f, 1.0f, 1.0f);
+ static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
ImGui::Spacing();
ImGui::Text("HSV encoded colors");
- ImGui::SameLine(); HelpMarker("By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
+ ImGui::SameLine(); HelpMarker(
+ "By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV"
+ "allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the"
+ "added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
ImGui::Text("Color widget with InputHSV:");
- ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
- ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
- ImGui::DragFloat4("Raw HSV values", (float*)&color_stored_as_hsv, 0.01f, 0.0f, 1.0f);
+ ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
+ ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
+ ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f);
ImGui::TreePop();
}
@@ -1347,20 +1508,23 @@
if (ImGui::TreeNode("Data Types"))
{
- // The DragScalar/InputScalar/SliderScalar functions allow various data types: signed/unsigned int/long long and float/double
- // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type,
- // and passing all arguments by address.
+ // DragScalar/InputScalar/SliderScalar functions allow various data types
+ // - signed/unsigned
+ // - 8/16/32/64-bits
+ // - integer/float/double
+ // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
+ // to pass the type, and passing all arguments by pointer.
// This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types.
- // In practice, if you frequently use a given type that is not covered by the normal API entry points, you can wrap it
- // yourself inside a 1 line function which can take typed argument as value instead of void*, and then pass their address
- // to the generic function. For example:
+ // In practice, if you frequently use a given type that is not covered by the normal API entry points,
+ // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
+ // and then pass their address to the generic function. For example:
// bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
// {
// return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
// }
- // Limits (as helper variables that we can take the address of)
- // Note that the SliderScalar function has a maximum usable range of half the natural type maximum, hence the /2 below.
+ // Setup limits (as helper variables so we can take their address, as explained above)
+ // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
#ifndef LLONG_MIN
ImS64 LLONG_MIN = -9223372036854775807LL - 1;
ImS64 LLONG_MAX = 9223372036854775807LL;
@@ -1486,7 +1650,7 @@
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
static int int_value = 0;
- ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5);
+ ImGui::VSliderInt("##int", ImVec2(18, 160), &int_value, 0, 5);
ImGui::SameLine();
static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };
@@ -1495,11 +1659,11 @@
{
if (i > 0) ImGui::SameLine();
ImGui::PushID(i);
- ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i/7.0f, 0.5f, 0.5f));
- ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.5f));
- ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.5f));
- ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i/7.0f, 0.9f, 0.9f));
- ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, "");
+ ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f));
+ ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f));
+ ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f));
+ ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f));
+ ImGui::VSliderFloat("##v", ImVec2(18, 160), &values[i], 0.0f, 1.0f, "");
if (ImGui::IsItemActive() || ImGui::IsItemHovered())
ImGui::SetTooltip("%.3f", values[i]);
ImGui::PopStyleColor(4);
@@ -1518,7 +1682,7 @@
ImGui::BeginGroup();
for (int ny = 0; ny < rows; ny++)
{
- ImGui::PushID(nx*rows+ny);
+ ImGui::PushID(nx * rows + ny);
ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, "");
if (ImGui::IsItemActive() || ImGui::IsItemHovered())
ImGui::SetTooltip("%.3f", values2[nx]);
@@ -1535,7 +1699,7 @@
if (i > 0) ImGui::SameLine();
ImGui::PushID(i);
ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40);
- ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f\nsec");
+ ImGui::VSliderFloat("##v", ImVec2(40, 160), &values[i], 0.0f, 1.0f, "%.2f\nsec");
ImGui::PopStyleVar();
ImGui::PopID();
}
@@ -1549,8 +1713,9 @@
if (ImGui::TreeNode("Drag and drop in standard widgets"))
{
// ColorEdit widgets automatically act as drag source and drag target.
- // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F to allow your own widgets
- // to use colors in their drag and drop interaction. Also see the demo in Color Picker -> Palette demo.
+ // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
+ // to allow your own widgets to use colors in their drag and drop interaction.
+ // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
HelpMarker("You can drag from the colored squares.");
static float col1[3] = { 1.0f, 0.0f, 0.2f };
static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
@@ -1571,19 +1736,28 @@
if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();
if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();
if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; }
- static const char* names[9] = { "Bobby", "Beatrice", "Betty", "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn" };
+ const char* names[9] =
+ {
+ "Bobby", "Beatrice", "Betty",
+ "Brianna", "Barry", "Bernard",
+ "Bibi", "Blaine", "Bryn"
+ };
for (int n = 0; n < IM_ARRAYSIZE(names); n++)
{
ImGui::PushID(n);
if ((n % 3) != 0)
ImGui::SameLine();
- ImGui::Button(names[n], ImVec2(60,60));
+ ImGui::Button(names[n], ImVec2(60, 60));
// Our buttons are both drag sources and drag targets here!
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
{
- ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int)); // Set payload to carry the index of our item (could be anything)
- if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); } // Display preview (could be anything, e.g. when dragging an image we could decide to display the filename and a small preview of the image, etc.)
+ // Set payload to carry the index of our item (could be anything)
+ ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int));
+
+ // Display preview (could be anything, e.g. when dragging an image we could decide to display
+ // the filename and a small preview of the image, etc.)
+ if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); }
if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); }
if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); }
ImGui::EndDragDropSource();
@@ -1620,7 +1794,9 @@
if (ImGui::TreeNode("Drag to reorder items (simple)"))
{
// Simple reordering
- HelpMarker("We don't use the drag and drop api at all here! Instead we query when the item is held but not hovered, and order items accordingly.");
+ HelpMarker(
+ "We don't use the drag and drop api at all here! "
+ "Instead we query when the item is held but not hovered, and order items accordingly.");
static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
for (int n = 0; n < IM_ARRAYSIZE(item_names); n++)
{
@@ -1646,11 +1822,18 @@
if (ImGui::TreeNode("Querying Status (Active/Focused/Hovered etc.)"))
{
- // Submit an item (various types available) so we can query their status in the following block.
+ // Select an item type
+ const char* item_names[] =
+ {
+ "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputFloat",
+ "InputFloat3", "ColorEdit4", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "ListBox"
+ };
static int item_type = 1;
- ImGui::Combo("Item Type", &item_type, "Text\0Button\0Button (w/ repeat)\0Checkbox\0SliderFloat\0InputText\0InputFloat\0InputFloat3\0ColorEdit4\0MenuItem\0TreeNode\0TreeNode (w/ double-click)\0ListBox\0", 20);
+ ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names));
ImGui::SameLine();
HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions.");
+
+ // Submit selected item item so we can query their status in the code following it.
bool ret = false;
static bool b = false;
static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
@@ -1669,10 +1852,10 @@
if (item_type == 11){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.
if (item_type == 12){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }
- // Display the value of IsItemHovered() and other common item state functions.
+ // Display the values of IsItemHovered() and other common item state functions.
// Note that the ImGuiHoveredFlags_XXX flags can be combined.
// Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
- // we query every state in a single call to avoid storing them and to simplify the code
+ // we query every state in a single call to avoid storing them and to simplify the code.
ImGui::BulletText(
"Return value = %d\n"
"IsItemFocused() = %d\n"
@@ -1715,7 +1898,7 @@
static bool embed_all_inside_a_child_window = false;
ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window);
if (embed_all_inside_a_child_window)
- ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20), true);
+ ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true);
// Testing IsWindowFocused() function with its various flags.
// Note that the ImGuiFocusedFlags_XXX flags can be combined.
@@ -1761,7 +1944,7 @@
ImGui::InputText("dummy", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
// Calling IsItemHovered() after begin returns the hovered status of the title bar.
- // This is useful in particular if you want to create a context menu (with BeginPopupContextItem) associated to the title bar of a window.
+ // This is useful in particular if you want to create a context menu associated to the title bar of a window.
static bool test_window = false;
ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window);
if (test_window)
@@ -1804,7 +1987,9 @@
// Child 1: no border, enable horizontal scrollbar
{
- ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0);
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
+ if (disable_mouse_wheel)
+ window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags);
for (int i = 0; i < 100; i++)
{
@@ -1821,7 +2006,11 @@
// Child 2: rounded border
{
- ImGuiWindowFlags window_flags = (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar);
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
+ if (disable_mouse_wheel)
+ window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
+ if (!disable_menu)
+ window_flags |= ImGuiWindowFlags_MenuBar;
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
ImGui::BeginChild("ChildR", ImVec2(0, 260), true, window_flags);
if (!disable_menu && ImGui::BeginMenuBar())
@@ -1849,10 +2038,11 @@
// Demonstrate a few extra things
// - Changing ImGuiCol_ChildBg (which is transparent black in default styles)
- // - Using SetCursorPos() to position the child window (because the child window is an item from the POV of the parent window)
- // You can also call SetNextWindowPos() to position the child window. The parent window will effectively layout from this position.
- // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from the POV of the parent window)
- // See "Widgets" -> "Querying Status (Active/Focused/Hovered etc.)" section for more details about this.
+ // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
+ // You can also call SetNextWindowPos() to position the child window. The parent window will effectively
+ // layout from this position.
+ // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
+ // the POV of the parent window). See 'Demo->Querying Status (Active/Focused/Hovered etc.)' for details.
{
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10);
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
@@ -1873,6 +2063,9 @@
{
// Use SetNextItemWidth() to set the width of a single upcoming item.
// Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
+ // In real code use you'll probably want to choose width values that are proportional to your font size
+ // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
+
static float f = 0.0f;
ImGui::Text("SetNextItemWidth/PushItemWidth(100)");
ImGui::SameLine(); HelpMarker("Fixed width.");
@@ -1894,7 +2087,8 @@
ImGui::SetNextItemWidth(-100);
ImGui::DragFloat("float##4", &f);
- // Demonstrate using PushItemWidth to surround three items. Calling SetNextItemWidth() before each of them would have the same effect.
+ // Demonstrate using PushItemWidth to surround three items.
+ // Calling SetNextItemWidth() before each of them would have the same effect.
ImGui::Text("SetNextItemWidth/PushItemWidth(-1)");
ImGui::SameLine(); HelpMarker("Align to right edge");
ImGui::PushItemWidth(-1);
@@ -1975,7 +2169,8 @@
ImGui::Dummy(button_sz); ImGui::SameLine();
ImGui::Button("B", button_sz);
- // Manually wrapping (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
+ // Manually wrapping
+ // (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
ImGui::Text("Manually wrapping:");
ImGuiStyle& style = ImGui::GetStyle();
int buttons_count = 20;
@@ -2046,7 +2241,8 @@
ImGui::Checkbox(names[n], &opened[n]);
}
- // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): the underlying bool will be set to false when the tab is closed.
+ // Passing a bool* to BeginTabItem() is similar to passing one to Begin():
+ // the underlying bool will be set to false when the tab is closed.
if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
{
for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
@@ -2067,7 +2263,10 @@
if (ImGui::TreeNode("Groups"))
{
- HelpMarker("BeginGroup() basically locks the horizontal position for new line. EndGroup() bundles the whole group so that you can use \"item\" functions such as IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
+ HelpMarker(
+ "BeginGroup() basically locks the horizontal position for new line. "
+ "EndGroup() bundles the whole group so that you can use \"item\" functions such as "
+ "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
ImGui::BeginGroup();
{
ImGui::BeginGroup();
@@ -2090,9 +2289,9 @@
const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };
ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size);
- ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f, size.y));
+ ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
ImGui::SameLine();
- ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f, size.y));
+ ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
ImGui::EndGroup();
ImGui::SameLine();
@@ -2113,8 +2312,9 @@
{
{
ImGui::BulletText("Text baseline:");
- ImGui::SameLine();
- HelpMarker("This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets.");
+ ImGui::SameLine(); HelpMarker(
+ "This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. "
+ "Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets.");
ImGui::Indent();
ImGui::Text("KO Blahblah"); ImGui::SameLine();
@@ -2122,7 +2322,8 @@
HelpMarker("Baseline of button will look misaligned with text..");
// If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
- // Because we don't know what's coming after the Text() statement, we need to move the text baseline down by FramePadding.y
+ // (because we don't know what's coming after the Text() statement, we need to move the text baseline
+ // down by FramePadding.y ahead of time)
ImGui::AlignTextToFramePadding();
ImGui::Text("OK Blahblah"); ImGui::SameLine();
ImGui::Button("Some framed item"); ImGui::SameLine();
@@ -2174,7 +2375,7 @@
ImGui::BulletText("Misc items:");
ImGui::Indent();
- // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button
+ // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
ImGui::Button("80x80", ImVec2(80, 80));
ImGui::SameLine();
ImGui::Button("50x50", ImVec2(50, 50));
@@ -2187,12 +2388,29 @@
const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
ImGui::Button("Button##1");
ImGui::SameLine(0.0f, spacing);
- if (ImGui::TreeNode("Node##1")) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data
+ if (ImGui::TreeNode("Node##1"))
+ {
+ // Dummy tree data
+ for (int i = 0; i < 6; i++)
+ ImGui::BulletText("Item %d..", i);
+ ImGui::TreePop();
+ }
- ImGui::AlignTextToFramePadding(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. Otherwise you can use SmallButton (smaller fit).
- bool node_open = ImGui::TreeNode("Node##2");// Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add child content.
+ // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
+ // Otherwise you can use SmallButton() (smaller fit).
+ ImGui::AlignTextToFramePadding();
+
+ // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
+ // other contents below the node.
+ bool node_open = ImGui::TreeNode("Node##2");
ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2");
- if (node_open) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data
+ if (node_open)
+ {
+ // Dummy tree data
+ for (int i = 0; i < 6; i++)
+ ImGui::BulletText("Item %d..", i);
+ ImGui::TreePop();
+ }
// Bullet
ImGui::Button("Button##3");
@@ -2249,8 +2467,9 @@
const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" };
ImGui::TextUnformatted(names[i]);
- ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
- bool window_visible = ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(child_w, 200.0f), true, child_flags);
+ const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
+ const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
+ const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), true, child_flags);
if (ImGui::BeginMenuBar())
{
ImGui::TextUnformatted("abc");
@@ -2260,7 +2479,7 @@
ImGui::SetScrollY(scroll_to_off_px);
if (scroll_to_pos)
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);
- if (window_visible) // Avoid calling SetScrollHereY when running with culled items
+ if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
{
for (int item = 0; item < 100; item++)
{
@@ -2285,18 +2504,25 @@
// Horizontal scroll functions
ImGui::Spacing();
- HelpMarker("Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\nUsing the \"Scroll To Pos\" button above will make the discontinuity at edges visible: scrolling to the top/bottom/left/right-most item will add an additional WindowPadding to reflect on reaching the edge of the list.\n\nBecause the clipping rectangle of most window hides half worth of WindowPadding on the left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the equivalent SetScrollFromPosY(+1) wouldn't.");
+ HelpMarker(
+ "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
+ "Using the \"Scroll To Pos\" button above will make the discontinuity at edges visible: "
+ "scrolling to the top/bottom/left/right-most item will add an additional WindowPadding to reflect "
+ "on reaching the edge of the list.\n\nBecause the clipping rectangle of most window hides half "
+ "worth of WindowPadding on the left/right, using SetScrollFromPosX(+1) will usually result in "
+ "clipped text whereas the equivalent SetScrollFromPosY(+1) wouldn't.");
ImGui::PushID("##HorizontalScrolling");
for (int i = 0; i < 5; i++)
{
float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
- bool window_visible = ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(-100, child_height), true, child_flags);
+ ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
+ bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), true, child_flags);
if (scroll_to_off)
ImGui::SetScrollX(scroll_to_off_px);
if (scroll_to_pos)
ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f);
- if (window_visible) // Avoid calling SetScrollHereY when running with culled items
+ if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
{
for (int item = 0; item < 100; item++)
{
@@ -2323,16 +2549,21 @@
ImGui::PopID();
// Miscellaneous Horizontal Scrolling Demo
- HelpMarker("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\nYou may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin().");
+ HelpMarker(
+ "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n"
+ "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().");
static int lines = 7;
ImGui::SliderInt("Lines", &lines, 1, 15);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
- ImGui::BeginChild("scrolling", ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30), true, ImGuiWindowFlags_HorizontalScrollbar);
+ ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
+ ImGui::BeginChild("scrolling", scrolling_child_size, true, ImGuiWindowFlags_HorizontalScrollbar);
for (int line = 0; line < lines; line++)
{
- // Display random stuff (for the sake of this trivial demo we are using basic Button+SameLine. If you want to create your own time line for a real application you may be better off
- // manipulating the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets yourself. You may also want to use the lower-level ImDrawList API)
+ // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
+ // If you want to create your own time line for a real application you may be better off manipulating
+ // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
+ // yourself. You may also want to use the lower-level ImDrawList API.
int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);
for (int n = 0; n < num_buttons; n++)
{
@@ -2340,8 +2571,8 @@
ImGui::PushID(n + line * 1000);
char num_buf[16];
sprintf(num_buf, "%d", n);
- const char* label = (!(n%15)) ? "FizzBuzz" : (!(n%3)) ? "Fizz" : (!(n%5)) ? "Buzz" : num_buf;
- float hue = n*0.05f;
+ const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf;
+ float hue = n * 0.05f;
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f));
@@ -2355,13 +2586,21 @@
ImGui::EndChild();
ImGui::PopStyleVar(2);
float scroll_x_delta = 0.0f;
- ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) { scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine();
+ ImGui::SmallButton("<<");
+ if (ImGui::IsItemActive())
+ scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;
+ ImGui::SameLine();
ImGui::Text("Scroll from code"); ImGui::SameLine();
- ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) { scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine();
+ ImGui::SmallButton(">>");
+ if (ImGui::IsItemActive())
+ scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;
+ ImGui::SameLine();
ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x);
if (scroll_x_delta != 0.0f)
{
- ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window)
+ // Demonstrate a trick: you can use Begin to set yourself in the context of another window
+ // (here we are already out of your child window)
+ ImGui::BeginChild("scrolling");
ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
ImGui::EndChild();
}
@@ -2450,7 +2689,7 @@
}
if (show_child)
{
- ImGui::BeginChild("child", ImVec2(0,0), true);
+ ImGui::BeginChild("child", ImVec2(0, 0), true);
ImGui::EndChild();
}
ImGui::End();
@@ -2462,15 +2701,22 @@
if (ImGui::TreeNode("Clipping"))
{
static ImVec2 size(100, 100), offset(50, 20);
- ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost.");
+ ImGui::TextWrapped(
+ "On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. "
+ "Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. "
+ "The system is designed to try minimizing both execution and CPU/GPU rendering cost.");
ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f");
ImGui::TextWrapped("(Click and drag)");
ImVec2 pos = ImGui::GetCursorScreenPos();
ImVec4 clip_rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
ImGui::InvisibleButton("##dummy", size);
- if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; }
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0))
+ {
+ offset.x += ImGui::GetIO().MouseDelta.x;
+ offset.y += ImGui::GetIO().MouseDelta.y;
+ }
ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(90, 90, 120, 255));
- ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x + offset.x, pos.y + offset.y), IM_COL32(255, 255, 255, 255), "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect);
+ ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x + offset.x, pos.y + offset.y), IM_COL32_WHITE, "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect);
ImGui::TreePop();
}
}
@@ -2483,10 +2729,12 @@
// The properties of popups windows are:
// - They block normal mouse hovering detection outside them. (*)
// - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
- // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as we are used to with regular Begin() calls.
- // User can manipulate the visibility state by calling OpenPopup().
- // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even when normally blocked by a popup.
- // Those three properties are connected. The library needs to hold their visibility state because it can close popups at any time.
+ // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
+ // we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
+ // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even
+ // when normally blocked by a popup.
+ // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
+ // popups at any time.
// Typical use for regular windows:
// bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End();
@@ -2498,14 +2746,16 @@
if (ImGui::TreeNode("Popups"))
{
- ImGui::TextWrapped("When a popup is active, it inhibits interacting with windows that are behind the popup. Clicking outside the popup closes it.");
+ ImGui::TextWrapped(
+ "When a popup is active, it inhibits interacting with windows that are behind the popup. "
+ "Clicking outside the popup closes it.");
static int selected_fish = -1;
const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" };
static bool toggles[] = { true, false, false, false, false };
- // Simple selection popup
- // (If you want to show the current selection inside the Button itself, you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
+ // Simple selection popup (if you want to show the current selection inside the Button itself,
+ // you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
if (ImGui::Button("Select.."))
ImGui::OpenPopup("my_select_popup");
ImGui::SameLine();
@@ -2576,10 +2826,11 @@
if (ImGui::TreeNode("Context menus"))
{
// BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
- // if (IsItemHovered() && IsMouseReleased(0))
+ // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
// OpenPopup(id);
// return BeginPopup(id);
- // For more advanced uses you may want to replicate and cuztomize this code. This the comments inside BeginPopupContextItem() implementation.
+ // For more advanced uses you may want to replicate and customize this code.
+ // See details in BeginPopupContextItem().
static float value = 0.5f;
ImGui::Text("Value = %.3f (<-- right-click here)", value);
if (ImGui::BeginPopupContextItem("item context menu"))
@@ -2591,16 +2842,19 @@
ImGui::EndPopup();
}
- // We can also use OpenPopupOnItemClick() which is the same as BeginPopupContextItem() but without the Begin call.
- // So here we will make it that clicking on the text field with the right mouse button (1) will toggle the visibility of the popup above.
+ // We can also use OpenPopupContextItem() which is the same as BeginPopupContextItem() but without the
+ // Begin() call. So here we will make it that clicking on the text field with the right mouse button (1)
+ // will toggle the visibility of the popup above.
ImGui::Text("(You can also right-click me to open the same popup as above.)");
- ImGui::OpenPopupOnItemClick("item context menu", 1);
+ ImGui::OpenPopupContextItem("item context menu", 1);
- // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem().
+ // When used after an item that has an ID (e.g.Button), we can skip providing an ID to BeginPopupContextItem().
// BeginPopupContextItem() will use the last item ID as the popup ID.
- // In addition here, we want to include your editable label inside the button label. We use the ### operator to override the ID (read FAQ about ID for details)
+ // In addition here, we want to include your editable label inside the button label.
+ // We use the ### operator to override the ID (read FAQ about ID for details)
static char name[32] = "Label1";
- char buf[64]; sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
+ char buf[64];
+ sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
ImGui::Button(buf);
if (ImGui::BeginPopupContextItem())
{
@@ -2617,11 +2871,15 @@
if (ImGui::TreeNode("Modals"))
{
- ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside the window.");
+ ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside.");
if (ImGui::Button("Delete.."))
ImGui::OpenPopup("Delete?");
+ // Always center this window when appearing
+ ImVec2 center(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f);
+ ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
+
if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n");
@@ -2659,7 +2917,7 @@
// Testing behavior of widgets stacking their own regular popups over the modal.
static int item = 1;
- static float color[4] = { 0.4f,0.7f,0.0f,0.5f };
+ static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
ImGui::ColorEdit4("color", color);
@@ -2666,8 +2924,9 @@
if (ImGui::Button("Add another modal.."))
ImGui::OpenPopup("Stacked 2");
- // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which will close the popup.
- // Note that the visibility state of popups is owned by imgui, so the input value of the bool actually doesn't matter here.
+ // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
+ // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
+ // of the bool actually doesn't matter here.
bool dummy_open = true;
if (ImGui::BeginPopupModal("Stacked 2", &dummy_open))
{
@@ -2689,9 +2948,12 @@
{
ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
ImGui::Separator();
- // NB: As a quirk in this very specific example, we want to differentiate the parent of this menu from the parent of the various popup menus above.
- // To do so we are encloding the items in a PushID()/PopID() block to make them two different menusets. If we don't, opening any popup above and hovering our menu here
- // would open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it, which is the desired behavior for regular menus.
+
+ // Note: As a quirk in this very specific example, we want to differentiate the parent of this menu from the
+ // parent of the various popup menus above. To do so we are encloding the items in a PushID()/PopID() block
+ // to make them two different menusets. If we don't, opening any popup above and hovering our menu here would
+ // open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it,
+ // which is the desired behavior for regular menus.
ImGui::PushID("foo");
ImGui::MenuItem("Menu item", "CTRL+M");
if (ImGui::BeginMenu("Menu inside a regular window"))
@@ -2729,7 +2991,7 @@
int Quantity;
// We have a problem which is affecting _only this demo_ and should not affect your code:
- // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
+ // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
// however qsort doesn't allow passing user data to comparing function.
// As a workaround, we are storing the sort specs in a static/global for the comparing function to access.
// In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.
@@ -2809,9 +3071,9 @@
if (ImGui::TreeNode("Basic"))
{
// Here we will showcase 4 different ways to output a table. They are very simple variations of a same thing!
-
+
// Basic use of tables using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
- // In many situations, this is the most flexible and easy to use pattern.
+ // In many situations, this is the most flexible and easy to use pattern.
HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
if (ImGui::BeginTable("##table1", 3))
{
@@ -2846,7 +3108,7 @@
}
// Another subtle variant, we call TableNextCell() _before_ each cell. At the end of a row, TableNextCell() will create a new row.
- // Note that we don't call TableNextRow() here!
+ // Note that we don't call TableNextRow() here!
// If we want to call TableNextRow(), then we don't need to call TableNextCell() for the first cell.
HelpMarker("Only using TableNextCell(), which tends to be convenient for tables where every cells contains the same type of contents.\nThis is also more similar to the old NextColumn() function of the Columns API, and provided to facilitate the Columns->Tables API transition.");
if (ImGui::BeginTable("##table4", 3))
@@ -2899,22 +3161,27 @@
for (int column = 0; column < 3; column++)
{
ImGui::TableSetColumnIndex(column);
+ char buf[32];
if (display_width)
{
+ // [DEBUG] Draw limits
ImVec2 p = ImGui::GetCursorScreenPos();
- ImDrawList* draw_list = ImGui::GetWindowDrawList();
- float x1 = p.x;
- float x2 = ImGui::GetWindowPos().x + ImGui::GetContentRegionMax().x;
- float x3 = draw_list->GetClipRectMax().x;
- float y2 = p.y + ImGui::GetTextLineHeight();
- draw_list->AddLine(ImVec2(x1, y2), ImVec2(x3, y2), IM_COL32(255, 255, 0, 255)); // Hard clipping limit
- draw_list->AddLine(ImVec2(x1, y2), ImVec2(x2, y2), IM_COL32(255, 0, 0, 255)); // Normal limit
- ImGui::Text("w=%.2f", x2 - x1);
+ float contents_x1 = p.x;
+ float contents_x2 = ImGui::GetWindowPos().x + ImGui::GetContentRegionMax().x;
+ float cliprect_x1 = ImGui::GetWindowDrawList()->GetClipRectMin().x;
+ float cliprect_x2 = ImGui::GetWindowDrawList()->GetClipRectMax().x;
+ float y1 = p.y;
+ float y2 = p.y + ImGui::GetTextLineHeight() + 2.0f;
+ ImDrawList* fg_draw_list = ImGui::GetForegroundDrawList();
+ fg_draw_list->AddRect(ImVec2(contents_x1, y1 + 0.0f), ImVec2(contents_x2, y2 + 0.0f), IM_COL32(255, 0, 0, 255)); // Contents limit (e.g. Cell + Padding)
+ fg_draw_list->AddLine(ImVec2(cliprect_x1, y2 + 1.0f), ImVec2(cliprect_x2, y2 + 1.0f), IM_COL32(255, 255, 0, 255)); // Hard clipping limit
+ sprintf(buf, "w=%.2f", contents_x2 - contents_x1);
}
else
{
- ImGui::Text("Hello %d,%d", row, column);
+ sprintf(buf, "Hello %d,%d", row, column);
}
+ ImGui::TextUnformatted(buf);
}
}
ImGui::EndTable();
@@ -2932,7 +3199,7 @@
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", (unsigned int*)&flags, ImGuiTableFlags_Resizable);
ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", (unsigned int*)&flags, ImGuiTableFlags_BordersV);
ImGui::SameLine(); HelpMarker("Using the _Resizable flag automatically enables the _BordersV flag as well.");
-
+
if (ImGui::BeginTable("##table1", 3, flags))
{
for (int row = 0; row < 5; row++)
@@ -3033,7 +3300,7 @@
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", (unsigned int*)&flags, ImGuiTableFlags_Resizable);
ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", (unsigned int*)&flags, ImGuiTableFlags_Reorderable);
ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", (unsigned int*)&flags, ImGuiTableFlags_Hideable);
-
+
if (ImGui::BeginTable("##table1", 3, flags))
{
// Submit columns name with TableSetupColumn() and call TableAutoHeaders() to create a row with a header in each column.
@@ -3053,6 +3320,24 @@
}
ImGui::EndTable();
}
+
+ if (ImGui::BeginTable("##table2", 3, flags | ImGuiTableFlags_SizingPolicyFixedX))
+ {
+ ImGui::TableSetupColumn("One");
+ ImGui::TableSetupColumn("Two");
+ ImGui::TableSetupColumn("Three");
+ ImGui::TableAutoHeaders();
+ for (int row = 0; row < 6; row++)
+ {
+ ImGui::TableNextRow();
+ for (int column = 0; column < 3; column++)
+ {
+ ImGui::TableSetColumnIndex(column);
+ ImGui::Text("Fixed %d,%d", row, column);
+ }
+ }
+ ImGui::EndTable();
+ }
ImGui::TreePop();
}
@@ -3065,7 +3350,7 @@
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollFreezeTopRow | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", (unsigned int*)&flags, ImGuiTableFlags_ScrollY);
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollFreezeTopRow", (unsigned int*)&flags, ImGuiTableFlags_ScrollFreezeTopRow);
-
+
if (ImGui::BeginTable("##table1", 3, flags, size))
{
ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None);
@@ -3163,8 +3448,8 @@
ImGui::CheckboxFlags("_NoSortDescending", (unsigned int*)&column_flags[column], ImGuiTableColumnFlags_NoSortDescending);
ImGui::CheckboxFlags("_PreferSortAscending", (unsigned int*)&column_flags[column], ImGuiTableColumnFlags_PreferSortAscending);
ImGui::CheckboxFlags("_PreferSortDescending", (unsigned int*)&column_flags[column], ImGuiTableColumnFlags_PreferSortDescending);
- ImGui::CheckboxFlags("_IndentEnable", (unsigned int*)&column_flags[column], ImGuiTableColumnFlags_IndentEnable);
- ImGui::CheckboxFlags("_IndentDisable", (unsigned int*)&column_flags[column], ImGuiTableColumnFlags_IndentDisable);
+ ImGui::CheckboxFlags("_IndentEnable", (unsigned int*)&column_flags[column], ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0");
+ ImGui::CheckboxFlags("_IndentDisable", (unsigned int*)&column_flags[column], ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0");
ImGui::PopID();
ImGui::PopStyleVar(2);
}
@@ -3200,7 +3485,7 @@
if (ImGui::TreeNode("Recursive"))
{
HelpMarker("This demonstrate embedding a table into another table cell.");
-
+
if (ImGui::BeginTable("recurse1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_BordersVFullHeight | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable))
{
ImGui::TableSetupColumn("A0");
@@ -3241,10 +3526,10 @@
if (ImGui::TreeNode("Sizing policies, cell contents"))
{
HelpMarker("This section allows you to interact and see the effect of StretchX vs FixedX sizing policies depending on whether Scroll is enabled and the contents of your columns.");
- enum ContentsType { CT_ShortText, CT_LongText, CT_Button, CT_StretchButton, CT_InputText };
- static int contents_type = CT_StretchButton;
+ enum ContentsType { CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };
+ static int contents_type = CT_FillButton;
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
- ImGui::Combo("Contents", &contents_type, "Short Text\0Long Text\0Button\0Stretch Button\0InputText\0");
+ ImGui::Combo("Contents", &contents_type, "Short Text\0Long Text\0Button\0Fill Button\0InputText\0");
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg;
ImGui::CheckboxFlags("ImGuiTableFlags_BordersHInner", (unsigned int*)&flags, ImGuiTableFlags_BordersHInner);
@@ -3255,10 +3540,10 @@
ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", (unsigned int*)&flags, ImGuiTableFlags_ScrollY);
if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyStretchX", (unsigned int*)&flags, ImGuiTableFlags_SizingPolicyStretchX))
flags &= ~(ImGuiTableFlags_SizingPolicyMaskX_ ^ ImGuiTableFlags_SizingPolicyStretchX); // Can't specify both sizing polices so we clear the other
- ImGui::SameLine(); HelpMarker("Default if _ScrollX if disabled.");
+ ImGui::SameLine(); HelpMarker("Default if _ScrollX if disabled. Makes columns use _WidthStretch policy by default.");
if (ImGui::CheckboxFlags("ImGuiTableFlags_SizingPolicyFixedX", (unsigned int*)&flags, ImGuiTableFlags_SizingPolicyFixedX))
flags &= ~(ImGuiTableFlags_SizingPolicyMaskX_ ^ ImGuiTableFlags_SizingPolicyFixedX); // Can't specify both sizing polices so we clear the other
- ImGui::SameLine(); HelpMarker("Default if _ScrollX if enabled.");
+ ImGui::SameLine(); HelpMarker("Default if _ScrollX if enabled. Makes columns use _WidthFixed by default, or _WidthAlwaysAutoResize if _Resizable is not set.");
ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", (unsigned int*)&flags, ImGuiTableFlags_Resizable);
ImGui::CheckboxFlags("ImGuiTableFlags_NoClipX", (unsigned int*)&flags, ImGuiTableFlags_NoClipX);
@@ -3275,11 +3560,11 @@
sprintf(label, "Hello %d,%d", row, column);
switch (contents_type)
{
- case CT_ShortText: ImGui::TextUnformatted(label); break;
- case CT_LongText: ImGui::Text("Some longer text %d,%d\nOver two lines..", row, column); break;
- case CT_Button: ImGui::Button(label); break;
- case CT_StretchButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break;
- case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break;
+ case CT_ShortText: ImGui::TextUnformatted(label); break;
+ case CT_LongText: ImGui::Text("Some longer text %d,%d\nOver two lines..", row, column); break;
+ case CT_Button: ImGui::Button(label); break;
+ case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break;
+ case CT_InputText: ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break;
}
}
}
@@ -3369,13 +3654,13 @@
ImGui::TableAutoHeaders();
// Simple storage to output a dummy file-system.
- struct MyTreeNode
- {
- const char* Name;
- const char* Type;
- int Size;
- int ChildIdx;
- int ChildCount;
+ struct MyTreeNode
+ {
+ const char* Name;
+ const char* Type;
+ int Size;
+ int ChildIdx;
+ int ChildCount;
static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
{
ImGui::TableNextRow();
@@ -3425,13 +3710,12 @@
}
// Demonstrate using TableHeader() calls instead of TableAutoHeaders()
- // FIXME-TABLE: Currently this doesn't get us feature-parity with TableAutoHeaders(), e.g. missing context menu. Tables API needs some work!
if (open_action != -1)
ImGui::SetNextItemOpen(open_action != 0);
if (ImGui::TreeNode("Custom headers"))
{
const int COLUMNS_COUNT = 3;
- if (ImGui::BeginTable("##table1", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable))
+ if (ImGui::BeginTable("##table1", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
{
ImGui::TableSetupColumn("Apricot");
ImGui::TableSetupColumn("Banana");
@@ -3472,6 +3756,70 @@
ImGui::TreePop();
}
+ // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeader()/TableAutoHeaders()
+ if (open_action != -1)
+ ImGui::SetNextItemOpen(open_action != 0);
+ if (ImGui::TreeNode("Context menus"))
+ {
+ HelpMarker("By default, TableHeader()/TableAutoHeaders() will open a context-menu on right-click.");
+ ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingPolicyFixedX | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
+ const int COLUMNS_COUNT = 3;
+ if (ImGui::BeginTable("##table1", COLUMNS_COUNT, flags))
+ {
+ ImGui::TableSetupColumn("One");
+ ImGui::TableSetupColumn("Two");
+ ImGui::TableSetupColumn("Three");
+
+ // Context Menu 1: right-click on header (including empty section after the third column!) should open Default Table Popup
+ ImGui::TableAutoHeaders();
+ for (int row = 0; row < 4; row++)
+ {
+ ImGui::TableNextRow();
+ for (int column = 0; column < COLUMNS_COUNT; column++)
+ {
+ ImGui::TableSetColumnIndex(column);
+ ImGui::PushID(row * COLUMNS_COUNT + column);
+ ImGui::Text("Cell %d,%d", column, row);
+ ImGui::SameLine();
+
+ // Context Menu 2: right-click on buttons open Custom Button Popup
+ ImGui::SmallButton("..");
+ if (ImGui::BeginPopupContextItem())
+ {
+ ImGui::Text("This is the popup for Button() On Cell %d,%d", column, row);
+ ImGui::Selectable("Close");
+ ImGui::EndPopup();
+ }
+ ImGui::PopID();
+ }
+ }
+
+ // Context Menu 3: Right-click anywhere in columns opens a custom popup
+ // We use the ImGuiPopupFlags_NoOpenOverExistingPopup flag to avoid displaying over either the standard TableHeader context-menu or the Button context-menu.
+ const int hovered_column = ImGui::TableGetHoveredColumn();
+ for (int column = 0; column < COLUMNS_COUNT + 1; column++)
+ {
+ ImGui::PushID(column);
+ if (hovered_column == column && ImGui::IsMouseReleased(1))
+ ImGui::OpenPopup("MyPopup", ImGuiPopupFlags_NoOpenOverExistingPopup);
+ if (ImGui::BeginPopup("MyPopup"))
+ {
+ if (column == COLUMNS_COUNT)
+ ImGui::Text("This is the popup for unused space after the last column.");
+ else
+ ImGui::Text("This is the popup for Column '%s'", ImGui::TableGetColumnName(column));
+ ImGui::Selectable("Close");
+ ImGui::EndPopup();
+ }
+ ImGui::PopID();
+ }
+
+ ImGui::EndTable();
+ ImGui::Text("TableGetHoveredColumn() returned: %d", hovered_column);
+ }
+ ImGui::TreePop();
+ }
+
static const char* template_items_names[] =
{
"Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango",
@@ -3565,14 +3913,13 @@
| ImGuiTableFlags_SizingPolicyFixedX
;
- enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_Selectable };
- static int contents_type = CT_Button;
- const char* contents_type_names[] = { "Text", "Button", "SmallButton", "Selectable" };
+ enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable };
+ static int contents_type = CT_FillButton;
+ const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable" };
static int items_count = IM_ARRAYSIZE(template_items_names);
static ImVec2 outer_size_value = ImVec2(0, 250);
static float row_min_height = 0.0f; // Auto
- static float inner_width_without_scroll = 0.0f; // Fill
static float inner_width_with_scroll = 0.0f; // Auto-extend
static bool outer_size_enabled = true;
static bool lock_first_column_visibility = false;
@@ -3654,10 +4001,7 @@
// From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.
// To facilitate experimentation we expose two values and will select the right one depending on active flags.
- if (flags & ImGuiTableFlags_ScrollX)
- ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);
- else
- ImGui::DragFloat("inner_width (when ScrollX inactive)", &inner_width_without_scroll, 1.0f, 0.0f, FLT_MAX);
+ ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);
ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX);
ImGui::SameLine(); HelpMarker("Specify height of the Selectable item.");
ImGui::DragInt("items_count", &items_count, 0.1f, 0, 5000);
@@ -3696,7 +4040,7 @@
ImVec2 table_scroll_cur, table_scroll_max; // For debug display
const ImDrawList* table_draw_list = NULL; // "
- const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : inner_width_without_scroll;
+ const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;
if (ImGui::BeginTable("##table", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use))
{
// Declare columns
@@ -3760,6 +4104,8 @@
ImGui::Button(label);
else if (contents_type == CT_SmallButton)
ImGui::SmallButton(label);
+ else if (contents_type == CT_FillButton)
+ ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));
else if (contents_type == CT_Selectable)
{
if (ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap, ImVec2(0, row_min_height)))
@@ -4004,7 +4350,8 @@
if (ImGui::TreeNode("Horizontal Scrolling"))
{
ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));
- ImGui::BeginChild("##ScrollingRegion", ImVec2(0, ImGui::GetFontSize() * 20), false, ImGuiWindowFlags_HorizontalScrollbar);
+ ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);
+ ImGui::BeginChild("##ScrollingRegion", child_size, false, ImGuiWindowFlags_HorizontalScrollbar);
ImGui::Columns(10);
int ITEMS_COUNT = 2000;
ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list
@@ -4101,7 +4448,7 @@
ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
- ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
+ ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
ImGui::Text("Mouse wheel: %.1f", io.MouseWheel);
@@ -4111,9 +4458,9 @@
ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
- ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); }
- ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); }
- ImGui::Text("NavInputs duration:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputsDownDuration[i]); }
+ ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); }
+ ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); }
+ ImGui::Text("NavInputs duration:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputsDownDuration[i]); }
ImGui::Button("Hovering me sets the\nkeyboard capture flag");
if (ImGui::IsItemHovered())
@@ -4135,7 +4482,7 @@
ImGui::InputText("3", buf, IM_ARRAYSIZE(buf));
ImGui::PushAllowKeyboardFocus(false);
ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf));
- //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets.");
+ //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool) to disable tabbing through certain widgets.");
ImGui::PopAllowKeyboardFocus();
ImGui::InputText("5", buf, IM_ARRAYSIZE(buf));
ImGui::TreePop();
@@ -4185,19 +4532,27 @@
{
ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");
for (int button = 0; button < 3; button++)
- ImGui::Text("IsMouseDragging(%d):\n w/ default threshold: %d,\n w/ zero threshold: %d\n w/ large threshold: %d",
- button, ImGui::IsMouseDragging(button), ImGui::IsMouseDragging(button, 0.0f), ImGui::IsMouseDragging(button, 20.0f));
+ {
+ ImGui::Text("IsMouseDragging(%d):", button);
+ ImGui::Text(" w/ default threshold: %d,", ImGui::IsMouseDragging(button));
+ ImGui::Text(" w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f));
+ ImGui::Text(" w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f));
+ }
ImGui::Button("Drag Me");
if (ImGui::IsItemActive())
ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor
- // Drag operations gets "unlocked" when the mouse has moved past a certain threshold (the default threshold is stored in io.MouseDragThreshold)
- // You can request a lower or higher threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta()
+ // Drag operations gets "unlocked" when the mouse has moved past a certain threshold
+ // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
+ // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);
ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0);
ImVec2 mouse_delta = io.MouseDelta;
- ImGui::Text("GetMouseDragDelta(0):\n w/ default threshold: (%.1f, %.1f),\n w/ zero threshold: (%.1f, %.1f)\nMouseDelta: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y, value_raw.x, value_raw.y, mouse_delta.x, mouse_delta.y);
+ ImGui::Text("GetMouseDragDelta(0):");
+ ImGui::Text(" w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y);
+ ImGui::Text(" w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y);
+ ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y);
ImGui::TreePop();
}
@@ -4206,9 +4561,13 @@
const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed" };
IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
- ImGui::Text("Current mouse cursor = %d: %s", ImGui::GetMouseCursor(), mouse_cursors_names[ImGui::GetMouseCursor()]);
+ ImGuiMouseCursor current = ImGui::GetMouseCursor();
+ ImGui::Text("Current mouse cursor = %d: %s", current, mouse_cursors_names[current]);
ImGui::Text("Hover to see mouse cursors:");
- ImGui::SameLine(); HelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it.");
+ ImGui::SameLine(); HelpMarker(
+ "Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. "
+ "If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, "
+ "otherwise your backend needs to handle it.");
for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)
{
char label[32];
@@ -4247,11 +4606,12 @@
ImGuiStyle& style = ImGui::GetStyle();
bool copy_to_clipboard = ImGui::Button("Copy to clipboard");
- ImGui::BeginChildFrame(ImGui::GetID("cfginfos"), ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18), ImGuiWindowFlags_NoMove);
+ ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);
+ ImGui::BeginChildFrame(ImGui::GetID("cfg_infos"), child_size, ImGuiWindowFlags_NoMove);
if (copy_to_clipboard)
{
ImGui::LogToClipboard();
- ImGui::LogText("```\n"); // Back quotes will make the text appears without formatting when pasting to GitHub
+ ImGui::LogText("```\n"); // Back quotes will make text appears without formatting when pasting on GitHub
}
ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
@@ -4368,7 +4728,8 @@
//-----------------------------------------------------------------------------
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
-// Here we use the simplified Combo() api that packs items into a single literal string. Useful for quick combo boxes where the choices are known locally.
+// Here we use the simplified Combo() api that packs items into a single literal string.
+// Useful for quick combo boxes where the choices are known locally.
bool ImGui::ShowStyleSelector(const char* label)
{
static int style_idx = -1;
@@ -4407,13 +4768,103 @@
HelpMarker(
"- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n"
"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
- "- Read FAQ and docs/FONTS.txt for more details.\n"
+ "- Read FAQ and docs/FONTS.md for more details.\n"
"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
}
+// [Internal] Display details for a single font, called by ShowStyleEditor().
+static void NodeFont(ImFont* font)
+{
+ ImGuiIO& io = ImGui::GetIO();
+ ImGuiStyle& style = ImGui::GetStyle();
+ bool font_details_opened = ImGui::TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
+ font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
+ ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { io.FontDefault = font; }
+ if (!font_details_opened)
+ return;
+
+ ImGui::PushFont(font);
+ ImGui::Text("The quick brown fox jumps over the lazy dog");
+ ImGui::PopFont();
+ ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font
+ ImGui::SameLine(); HelpMarker(
+ "Note than the default embedded font is NOT meant to be scaled.\n\n"
+ "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
+ "You may oversample them to get some flexibility with scaling. "
+ "You can also render at multiple sizes and select which one to use at runtime.\n\n"
+ "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
+ ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, "%.0f");
+ ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
+ ImGui::Text("Fallback character: '%c' (U+%04X)", font->FallbackChar, font->FallbackChar);
+ ImGui::Text("Ellipsis character: '%c' (U+%04X)", font->EllipsisChar, font->EllipsisChar);
+ const int surface_sqrt = (int)sqrtf((float)font->MetricsTotalSurface);
+ ImGui::Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
+ for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
+ if (font->ConfigData)
+ if (const ImFontConfig* cfg = &font->ConfigData[config_i])
+ ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d",
+ config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH);
+ if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
+ {
+ // Display all glyphs of the fonts in separate pages of 256 characters
+ const ImU32 glyph_col = ImGui::GetColorU32(ImGuiCol_Text);
+ for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
+ {
+ // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
+ // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
+ // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
+ if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
+ {
+ base += 4096 - 256;
+ continue;
+ }
+
+ int count = 0;
+ for (unsigned int n = 0; n < 256; n++)
+ if (font->FindGlyphNoFallback((ImWchar)(base + n)))
+ count++;
+ if (count <= 0)
+ continue;
+ if (!ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
+ continue;
+ float cell_size = font->FontSize * 1;
+ float cell_spacing = style.ItemSpacing.y;
+ ImVec2 base_pos = ImGui::GetCursorScreenPos();
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ for (unsigned int n = 0; n < 256; n++)
+ {
+ // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
+ // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
+ ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
+ ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
+ const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
+ draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
+ if (glyph)
+ font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
+ if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2))
+ {
+ ImGui::BeginTooltip();
+ ImGui::Text("Codepoint: U+%04X", base + n);
+ ImGui::Separator();
+ ImGui::Text("Visible: %d", glyph->Visible);
+ ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX);
+ ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
+ ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
+ ImGui::EndTooltip();
+ }
+ }
+ ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
+ ImGui::TreePop();
+ }
+ ImGui::TreePop();
+ }
+ ImGui::TreePop();
+}
+
void ImGui::ShowStyleEditor(ImGuiStyle* ref)
{
- // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it compares to an internally stored reference)
+ // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
+ // (without a reference style pointer, we will use one compared locally as a reference)
ImGuiStyle& style = ImGui::GetStyle();
static ImGuiStyle ref_saved_style;
@@ -4431,14 +4882,14 @@
ref_saved_style = style;
ImGui::ShowFontSelector("Fonts##Selector");
- // Simplified Settings
+ // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
- { bool window_border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &window_border)) style.WindowBorderSize = window_border ? 1.0f : 0.0f; }
+ { bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine();
- { bool frame_border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox("FrameBorder", &frame_border)) style.FrameBorderSize = frame_border ? 1.0f : 0.0f; }
+ { bool border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
ImGui::SameLine();
- { bool popup_border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox("PopupBorder", &popup_border)) style.PopupBorderSize = popup_border ? 1.0f : 0.0f; }
+ { bool border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
// Save/Revert button
if (ImGui::Button("Save Ref"))
@@ -4447,7 +4898,9 @@
if (ImGui::Button("Revert Ref"))
style = *ref;
ImGui::SameLine();
- HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export\" below to save them somewhere.");
+ HelpMarker(
+ "Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
+ "Use \"Export\" below to save them somewhere.");
ImGui::Separator();
@@ -4485,9 +4938,12 @@
if (ImGui::Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0"))
style.WindowMenuButtonPosition = window_menu_button_position - 1;
ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
- ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
- ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.");
- ImGui::Text("Safe Area Padding"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).");
+ ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
+ ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
+ ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
+ ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.");
+ ImGui::Text("Safe Area Padding");
+ ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).");
ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f");
ImGui::EndTabItem();
}
@@ -4508,7 +4964,8 @@
const ImVec4& col = style.Colors[i];
const char* name = ImGui::GetStyleColorName(i);
if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
- ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
+ ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
+ name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
}
ImGui::LogFinish();
}
@@ -4519,10 +4976,13 @@
filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
static ImGuiColorEditFlags alpha_flags = 0;
- if (ImGui::RadioButton("Opaque", alpha_flags == 0)) { alpha_flags = 0; } ImGui::SameLine();
- if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();
- if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();
- HelpMarker("In the color list:\nLeft-click on colored square to open color picker,\nRight-click to open edit options menu.");
+ if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();
+ if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();
+ if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();
+ HelpMarker(
+ "In the color list:\n"
+ "Left-click on colored square to open color picker,\n"
+ "Right-click to open edit options menu.");
ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened);
ImGui::PushItemWidth(-160);
@@ -4535,10 +4995,11 @@
ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);
if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)
{
- // Tips: in a real user application, you may want to merge and use an icon font into the main font, so instead of "Save"/"Revert" you'd use icons.
- // Read the FAQ and docs/FONTS.txt about using icon fonts. It's really easy and super convenient!
- ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) ref->Colors[i] = style.Colors[i];
- ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) style.Colors[i] = ref->Colors[i];
+ // Tips: in a real user application, you may want to merge and use an icon font into the main font,
+ // so instead of "Save"/"Revert" you'd use icons!
+ // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
+ ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; }
+ ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; }
}
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
ImGui::TextUnformatted(name);
@@ -4554,82 +5015,13 @@
{
ImGuiIO& io = ImGui::GetIO();
ImFontAtlas* atlas = io.Fonts;
- HelpMarker("Read FAQ and docs/FONTS.txt for details on font loading.");
+ HelpMarker("Read FAQ and docs/FONTS.md for details on font loading.");
ImGui::PushItemWidth(120);
for (int i = 0; i < atlas->Fonts.Size; i++)
{
ImFont* font = atlas->Fonts[i];
ImGui::PushID(font);
- bool font_details_opened = ImGui::TreeNode(font, "Font %d: \"%s\"\n%.2f px, %d glyphs, %d file(s)", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
- ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { io.FontDefault = font; }
- if (font_details_opened)
- {
- ImGui::PushFont(font);
- ImGui::Text("The quick brown fox jumps over the lazy dog");
- ImGui::PopFont();
- ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font
- ImGui::SameLine(); HelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)");
- ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, "%.0f");
- ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
- ImGui::Text("Fallback character: '%c' (U+%04X)", font->FallbackChar, font->FallbackChar);
- ImGui::Text("Ellipsis character: '%c' (U+%04X)", font->EllipsisChar, font->EllipsisChar);
- const float surface_sqrt = sqrtf((float)font->MetricsTotalSurface);
- ImGui::Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, (int)surface_sqrt, (int)surface_sqrt);
- for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
- if (font->ConfigData)
- if (const ImFontConfig* cfg = &font->ConfigData[config_i])
- ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH);
- if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
- {
- // Display all glyphs of the fonts in separate pages of 256 characters
- for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
- {
- // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
- // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT is large.
- // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
- if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
- {
- base += 4096 - 256;
- continue;
- }
-
- int count = 0;
- for (unsigned int n = 0; n < 256; n++)
- count += font->FindGlyphNoFallback((ImWchar)(base + n)) ? 1 : 0;
- if (count > 0 && ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
- {
- float cell_size = font->FontSize * 1;
- float cell_spacing = style.ItemSpacing.y;
- ImVec2 base_pos = ImGui::GetCursorScreenPos();
- ImDrawList* draw_list = ImGui::GetWindowDrawList();
- for (unsigned int n = 0; n < 256; n++)
- {
- ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
- ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
- const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
- draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
- if (glyph)
- font->RenderChar(draw_list, cell_size, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base + n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string.
- if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2))
- {
- ImGui::BeginTooltip();
- ImGui::Text("Codepoint: U+%04X", base + n);
- ImGui::Separator();
- ImGui::Text("Visible: %d", glyph->Visible);
- ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX);
- ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
- ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
- ImGui::EndTooltip();
- }
- }
- ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
- ImGui::TreePop();
- }
- }
- ImGui::TreePop();
- }
- ImGui::TreePop();
- }
+ NodeFont(font);
ImGui::PopID();
}
if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
@@ -4640,11 +5032,20 @@
ImGui::TreePop();
}
- HelpMarker("Those are old settings provided for convenience.\nHowever, the _correct_ way of scaling your UI is currently to reload your font at the designed size, rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.");
+ // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
+ // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
+ const float MIN_SCALE = 0.3f;
+ const float MAX_SCALE = 2.0f;
+ HelpMarker(
+ "Those are old settings provided for convenience.\n"
+ "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
+ "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
+ "Using those settings here will give you poor quality results.");
static float window_scale = 1.0f;
- if (ImGui::DragFloat("window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.2f")) // scale only this window
- ImGui::SetWindowFontScale(window_scale);
- ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.2f"); // scale everything
+ if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f")) // Scale only this window
+ ImGui::SetWindowFontScale(IM_MAX(window_scale, MIN_SCALE));
+ if (ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f")) // Scale everything
+ io.FontGlobalScale = IM_MAX(io.FontGlobalScale, MIN_SCALE);
ImGui::PopItemWidth();
ImGui::EndTabItem();
@@ -4652,7 +5053,8 @@
if (ImGui::BeginTabItem("Rendering"))
{
- ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
+ ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
+ ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
ImGui::PushItemWidth(100);
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
@@ -4679,7 +5081,7 @@
// Demonstrate creating a "main" fullscreen menu bar and populating it.
// Note the difference between BeginMainMenuBar() and BeginMenuBar():
-// - BeginMenuBar() = menu-bar inside current window we Begin()-ed into (the window needs the ImGuiWindowFlags_MenuBar flag)
+// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)
// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.
static void ShowExampleAppMainMenuBar()
{
@@ -4704,7 +5106,8 @@
}
}
-// Note that shortcuts are currently provided for display only (future version will add flags to BeginMenu to process shortcuts)
+// Note that shortcuts are currently provided for display only
+// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
static void ShowExampleMenuFile()
{
ImGui::MenuItem("(dummy menu)", NULL, false, false);
@@ -4755,7 +5158,7 @@
{
const char* name = ImGui::GetStyleColorName((ImGuiCol)i);
ImVec2 p = ImGui::GetCursorScreenPos();
- ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x+sz, p.y+sz), ImGui::GetColorU32((ImGuiCol)i));
+ ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i));
ImGui::Dummy(ImVec2(sz, sz));
ImGui::SameLine();
ImGui::MenuItem(name);
@@ -4786,7 +5189,7 @@
//-----------------------------------------------------------------------------
// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
-// For the console example, here we are using a more C++ like approach of declaring a class to hold the data and the functions.
+// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
struct ExampleAppConsole
{
char InputBuf[256];
@@ -4803,10 +5206,12 @@
ClearLog();
memset(InputBuf, 0, sizeof(InputBuf));
HistoryPos = -1;
+
+ // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
Commands.push_back("HELP");
Commands.push_back("HISTORY");
Commands.push_back("CLEAR");
- Commands.push_back("CLASSIFY"); // "classify" is only here to provide an example of "C"+[tab] completing to "CL" and displaying matches.
+ Commands.push_back("CLASSIFY");
AutoScroll = true;
ScrollToBottom = false;
AddLog("Welcome to Dear ImGui!");
@@ -4819,10 +5224,10 @@
}
// Portable helpers
- static int Stricmp(const char* str1, const char* str2) { int d; while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } return d; }
- static int Strnicmp(const char* str1, const char* str2, int n) { int d = 0; while (n > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; n--; } return d; }
- static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)str, len); }
- static void Strtrim(char* str) { char* str_end = str + strlen(str); while (str_end > str && str_end[-1] == ' ') str_end--; *str_end = 0; }
+ static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }
+ static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }
+ static char* Strdup(const char* s) { size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); }
+ static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
void ClearLog()
{
@@ -4845,7 +5250,7 @@
void Draw(const char* title, bool* p_open)
{
- ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
if (!ImGui::Begin(title, p_open))
{
ImGui::End();
@@ -4852,7 +5257,8 @@
return;
}
- // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar. So e.g. IsItemHovered() will return true when hovering the title bar.
+ // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
+ // So e.g. IsItemHovered() will return true when hovering the title bar.
// Here we create a context menu only available from the title bar.
if (ImGui::BeginPopupContextItem())
{
@@ -4861,7 +5267,9 @@
ImGui::EndPopup();
}
- ImGui::TextWrapped("This example implements a console with basic coloring, completion and history. A more elaborate implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
+ ImGui::TextWrapped(
+ "This example implements a console with basic coloring, completion and history. A more elaborate "
+ "implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
ImGui::TextWrapped("Enter 'HELP' for help, press TAB to use text completion.");
// TODO: display items starting from the bottom
@@ -4868,7 +5276,7 @@
if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine();
if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine();
- if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine();
+ if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine();
bool copy_to_clipboard = ImGui::SmallButton("Copy");
//static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
@@ -4888,8 +5296,9 @@
Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
ImGui::Separator();
- const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); // 1 separator, 1 input text
- ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar); // Leave room for 1 separator + 1 InputText
+ // Reserve enough left-over height for 1 separator + 1 input text
+ const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
+ ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar);
if (ImGui::BeginPopupContextWindow())
{
if (ImGui::Selectable("Clear")) ClearLog();
@@ -4896,18 +5305,30 @@
ImGui::EndPopup();
}
- // Display every line as a separate entry so we can change their color or add custom widgets. If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
- // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping to only process visible items.
- // You can seek and display only the lines that are visible using the ImGuiListClipper helper, if your elements are evenly spaced and you have cheap random access to the elements.
- // To use the clipper we could replace the 'for (int i = 0; i < Items.Size; i++)' loop with:
- // ImGuiListClipper clipper(Items.Size);
- // while (clipper.Step())
+ // Display every line as a separate entry so we can change their color or add custom widgets.
+ // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
+ // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
+ // to only process visible items. The clipper will automatically measure the height of your first item and then
+ // "seek" to display only items in the visible area.
+ // To use the clipper we can replace your standard loop:
+ // for (int i = 0; i < Items.Size; i++)
+ // With:
+ // ImGuiListClipper clipper(Items.Size);
+ // while (clipper.Step())
// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
- // However, note that you can not use this code as is if a filter is active because it breaks the 'cheap random-access' property. We would need random-access on the post-filtered list.
- // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices that passed the filtering test, recomputing this array when user changes the filter,
- // and appending newly elements as they are inserted. This is left as a task to the user until we can manage to improve this example code!
- // If your items are of variable size you may want to implement code similar to what ImGuiListClipper does. Or split your data into fixed height items to allow random-seeking into your list.
- ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing
+ // - That your items are evenly spaced (same height)
+ // - That you have cheap random access to your elements (you can access them given their index,
+ // without processing all the ones before)
+ // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
+ // We would need random-access on the post-filtered list.
+ // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
+ // or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
+ // and appending newly elements as they are inserted. This is left as a task to the user until we can manage
+ // to improve this example code!
+ // If your items are of variable height:
+ // - Split them into same height items would be simpler and facilitate random-seeking into your list.
+ // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing
if (copy_to_clipboard)
ImGui::LogToClipboard();
for (int i = 0; i < Items.Size; i++)
@@ -4916,12 +5337,16 @@
if (!Filter.PassFilter(item))
continue;
- // Normally you would store more information in your item (e.g. make Items[] an array of structure, store color/type etc.)
- bool pop_color = false;
- if (strstr(item, "[error]")) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); pop_color = true; }
- else if (strncmp(item, "# ", 2) == 0) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.6f, 1.0f)); pop_color = true; }
+ // Normally you would store more information in your item than just a string.
+ // (e.g. make Items[] an array of structure, store color/type etc.)
+ ImVec4 color;
+ bool has_color = false;
+ if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
+ else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
+ if (has_color)
+ ImGui::PushStyleColor(ImGuiCol_Text, color);
ImGui::TextUnformatted(item);
- if (pop_color)
+ if (has_color)
ImGui::PopStyleColor();
}
if (copy_to_clipboard)
@@ -4937,7 +5362,8 @@
// Command-line
bool reclaim_focus = false;
- if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this))
+ ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
+ if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))
{
char* s = InputBuf;
Strtrim(s);
@@ -4959,9 +5385,10 @@
{
AddLog("# %s\n", command_line);
- // Insert into history. First find match and delete it so it can be pushed to the back. This isn't trying to be smart or optimal.
+ // Insert into history. First find match and delete it so it can be pushed to the back.
+ // This isn't trying to be smart or optimal.
HistoryPos = -1;
- for (int i = History.Size-1; i >= 0; i--)
+ for (int i = History.Size - 1; i >= 0; i--)
if (Stricmp(History[i], command_line) == 0)
{
free(History[i]);
@@ -4992,11 +5419,12 @@
AddLog("Unknown command: '%s'\n", command_line);
}
- // On commad input, we scroll to bottom even if AutoScroll==false
+ // On command input, we scroll to bottom even if AutoScroll==false
ScrollToBottom = true;
}
- static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks
+ // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
+ static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)
{
ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;
return console->TextEditCallback(data);
@@ -5025,24 +5453,25 @@
// Build a list of candidates
ImVector<const char*> candidates;
for (int i = 0; i < Commands.Size; i++)
- if (Strnicmp(Commands[i], word_start, (int)(word_end-word_start)) == 0)
+ if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
candidates.push_back(Commands[i]);
if (candidates.Size == 0)
{
// No match
- AddLog("No match for \"%.*s\"!\n", (int)(word_end-word_start), word_start);
+ AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
}
else if (candidates.Size == 1)
{
- // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing
- data->DeleteChars((int)(word_start-data->Buf), (int)(word_end-word_start));
+ // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
+ data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
data->InsertChars(data->CursorPos, candidates[0]);
data->InsertChars(data->CursorPos, " ");
}
else
{
- // Multiple matches. Complete as much as we can, so inputing "C" will complete to "CL" and display "CLEAR" and "CLASSIFY"
+ // Multiple matches. Complete as much as we can..
+ // So inputing "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
int match_len = (int)(word_end - word_start);
for (;;)
{
@@ -5060,7 +5489,7 @@
if (match_len > 0)
{
- data->DeleteChars((int)(word_start - data->Buf), (int)(word_end-word_start));
+ data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
}
@@ -5121,8 +5550,8 @@
{
ImGuiTextBuffer Buf;
ImGuiTextFilter Filter;
- ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls, allowing us to have a random access on lines
- bool AutoScroll; // Keep scrolling if already at the bottom
+ ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
+ bool AutoScroll; // Keep scrolling if already at the bottom.
ExampleAppLog()
{
@@ -5175,7 +5604,7 @@
Filter.Draw("Filter", -100.0f);
ImGui::Separator();
- ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar);
+ ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
if (clear)
Clear();
@@ -5189,8 +5618,8 @@
{
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have a random access on the result on our filter.
- // A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
- // especially if the filtering function is not trivial (e.g. reg-exp).
+ // A real application processing logs with ten of thousands of entries may want to store the result of
+ // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
{
const char* line_start = buf + LineOffsets[line_no];
@@ -5203,13 +5632,17 @@
{
// The simplest and easy way to display the entire buffer:
// ImGui::TextUnformatted(buf_begin, buf_end);
- // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
- // Here we instead demonstrate using the clipper to only process lines that are within the visible area.
- // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
- // Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
+ // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
+ // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
+ // within the visible area.
+ // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
+ // on your side is recommended. Using ImGuiListClipper requires
+ // - A) random access into your data
+ // - B) items all being the same height,
// both of which we can handle since we an array pointing to the beginning of each line of text.
- // When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
- // Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
+ // When using the filter (in the block of code above) we don't have random access into the data to display
+ // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
+ // it possible (and would be recommended if you want to search through tens of thousands of entries).
ImGuiListClipper clipper;
clipper.Begin(LineOffsets.Size);
while (clipper.Step())
@@ -5246,12 +5679,14 @@
if (ImGui::SmallButton("[Debug] Add 5 entries"))
{
static int counter = 0;
+ const char* categories[3] = { "info", "warn", "error" };
+ const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
for (int n = 0; n < 5; n++)
{
- const char* categories[3] = { "info", "warn", "error" };
- const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
+ const char* category = categories[counter % IM_ARRAYSIZE(categories)];
+ const char* word = words[counter % IM_ARRAYSIZE(words)];
log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
- ImGui::GetFrameCount(), categories[counter % IM_ARRAYSIZE(categories)], ImGui::GetTime(), words[counter % IM_ARRAYSIZE(words)]);
+ ImGui::GetFrameCount(), category, ImGui::GetTime(), word);
counter++;
}
}
@@ -5281,43 +5716,47 @@
ImGui::EndMenuBar();
}
- // left
+ // Left
static int selected = 0;
- ImGui::BeginChild("left pane", ImVec2(150, 0), true);
- for (int i = 0; i < 100; i++)
{
- char label[128];
- sprintf(label, "MyObject %d", i);
- if (ImGui::Selectable(label, selected == i))
- selected = i;
+ ImGui::BeginChild("left pane", ImVec2(150, 0), true);
+ for (int i = 0; i < 100; i++)
+ {
+ char label[128];
+ sprintf(label, "MyObject %d", i);
+ if (ImGui::Selectable(label, selected == i))
+ selected = i;
+ }
+ ImGui::EndChild();
}
- ImGui::EndChild();
ImGui::SameLine();
- // right
- ImGui::BeginGroup();
+ // Right
+ {
+ ImGui::BeginGroup();
ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
- ImGui::Text("MyObject: %d", selected);
- ImGui::Separator();
- if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
+ ImGui::Text("MyObject: %d", selected);
+ ImGui::Separator();
+ if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
+ {
+ if (ImGui::BeginTabItem("Description"))
{
- if (ImGui::BeginTabItem("Description"))
- {
- ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ");
- ImGui::EndTabItem();
- }
- if (ImGui::BeginTabItem("Details"))
- {
- ImGui::Text("ID: 0123456789");
- ImGui::EndTabItem();
- }
- ImGui::EndTabBar();
+ ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ");
+ ImGui::EndTabItem();
}
+ if (ImGui::BeginTabItem("Details"))
+ {
+ ImGui::Text("ID: 0123456789");
+ ImGui::EndTabItem();
+ }
+ ImGui::EndTabBar();
+ }
ImGui::EndChild();
if (ImGui::Button("Revert")) {}
ImGui::SameLine();
if (ImGui::Button("Save")) {}
- ImGui::EndGroup();
+ ImGui::EndGroup();
+ }
}
ImGui::End();
}
@@ -5326,10 +5765,51 @@
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
//-----------------------------------------------------------------------------
+static void ShowDummyObject(const char* prefix, int uid)
+{
+ // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID.
+ ImGui::PushID(uid);
+ ImGui::AlignTextToFramePadding(); // Text and Tree nodes are less high than framed widgets, here we add vertical spacing to make the tree lines equal high.
+ bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid);
+ ImGui::NextColumn();
+ ImGui::AlignTextToFramePadding();
+ ImGui::Text("my sailor is rich");
+ ImGui::NextColumn();
+ if (node_open)
+ {
+ static float dummy_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f };
+ for (int i = 0; i < 8; i++)
+ {
+ ImGui::PushID(i); // Use field index as identifier.
+ if (i < 2)
+ {
+ ShowDummyObject("Child", 424242);
+ }
+ else
+ {
+ // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well)
+ ImGui::AlignTextToFramePadding();
+ ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet;
+ ImGui::TreeNodeEx("Field", flags, "Field_%d", i);
+ ImGui::NextColumn();
+ ImGui::SetNextItemWidth(-1);
+ if (i >= 5)
+ ImGui::InputFloat("##value", &dummy_members[i], 1.0f);
+ else
+ ImGui::DragFloat("##value", &dummy_members[i], 0.01f);
+ ImGui::NextColumn();
+ }
+ ImGui::PopID();
+ }
+ ImGui::TreePop();
+ }
+ ImGui::PopID();
+}
+
// Demonstrate create a simple property editor.
static void ShowExampleAppPropertyEditor(bool* p_open)
{
- ImGui::SetNextWindowSize(ImVec2(430,450), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Example: Property editor", p_open))
{
ImGui::End();
@@ -5336,57 +5816,19 @@
return;
}
- HelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API.");
+ HelpMarker(
+ "This example shows how you may implement a property editor using two columns.\n"
+ "All objects/fields data are dummies here.\n"
+ "Remember that in many simple cases, you can use ImGui::SameLine(xxx) to position\n"
+ "your cursor horizontally instead of using the Columns() API.");
- ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2,2));
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
ImGui::Columns(2);
ImGui::Separator();
- struct funcs
- {
- static void ShowDummyObject(const char* prefix, int uid)
- {
- ImGui::PushID(uid); // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID.
- ImGui::AlignTextToFramePadding(); // Text and Tree nodes are less high than regular widgets, here we add vertical spacing to make the tree lines equal high.
- bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid);
- ImGui::NextColumn();
- ImGui::AlignTextToFramePadding();
- ImGui::Text("my sailor is rich");
- ImGui::NextColumn();
- if (node_open)
- {
- static float dummy_members[8] = { 0.0f,0.0f,1.0f,3.1416f,100.0f,999.0f };
- for (int i = 0; i < 8; i++)
- {
- ImGui::PushID(i); // Use field index as identifier.
- if (i < 2)
- {
- ShowDummyObject("Child", 424242);
- }
- else
- {
- // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well)
- ImGui::AlignTextToFramePadding();
- ImGui::TreeNodeEx("Field", ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet, "Field_%d", i);
- ImGui::NextColumn();
- ImGui::SetNextItemWidth(-1);
- if (i >= 5)
- ImGui::InputFloat("##value", &dummy_members[i], 1.0f);
- else
- ImGui::DragFloat("##value", &dummy_members[i], 0.01f);
- ImGui::NextColumn();
- }
- ImGui::PopID();
- }
- ImGui::TreePop();
- }
- ImGui::PopID();
- }
- };
-
// Iterate dummy objects with dummy members (all the same data)
for (int obj_i = 0; obj_i < 3; obj_i++)
- funcs::ShowDummyObject("Object", obj_i);
+ ShowDummyObject("Object", obj_i);
ImGui::Columns(1);
ImGui::Separator();
@@ -5401,7 +5843,7 @@
// Demonstrate/test rendering huge amount of text, and the incidence of clipping.
static void ShowExampleAppLongText(bool* p_open)
{
- ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Example: Long text display", p_open))
{
ImGui::End();
@@ -5412,7 +5854,10 @@
static ImGuiTextBuffer log;
static int lines = 0;
ImGui::Text("Printing unusually long amount of text.");
- ImGui::Combo("Test type", &test_type, "Single call to TextUnformatted()\0Multiple calls to Text(), clipped\0Multiple calls to Text(), not clipped (slow)\0");
+ ImGui::Combo("Test type", &test_type,
+ "Single call to TextUnformatted()\0"
+ "Multiple calls to Text(), clipped\0"
+ "Multiple calls to Text(), not clipped (slow)\0");
ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size());
if (ImGui::Button("Clear")) { log.clear(); lines = 0; }
ImGui::SameLine();
@@ -5419,7 +5864,7 @@
if (ImGui::Button("Add 1000 lines"))
{
for (int i = 0; i < 1000; i++)
- log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines+i);
+ log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i);
lines += 1000;
}
ImGui::BeginChild("Log");
@@ -5432,7 +5877,7 @@
case 1:
{
// Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
- ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
ImGuiListClipper clipper(lines);
while (clipper.Step())
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
@@ -5442,7 +5887,7 @@
}
case 2:
// Multiple calls to Text(), not clipped (slow)
- ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0));
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
for (int i = 0; i < lines; i++)
ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
ImGui::PopStyleVar();
@@ -5466,7 +5911,10 @@
}
static int lines = 10;
- ImGui::Text("Window will resize every-frame to the size of its content.\nNote that you probably don't want to query the window size to\noutput your content because that would create a feedback loop.");
+ ImGui::TextUnformatted(
+ "Window will resize every-frame to the size of its content.\n"
+ "Note that you probably don't want to query the window size to\n"
+ "output your content because that would create a feedback loop.");
ImGui::SliderInt("Number of lines", &lines, 1, 20);
for (int i = 0; i < lines; i++)
ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally
@@ -5480,12 +5928,24 @@
// Demonstrate creating a window with custom resize constraints.
static void ShowExampleAppConstrainedResize(bool* p_open)
{
- struct CustomConstraints // Helper functions to demonstrate programmatic constraints
+ struct CustomConstraints
{
- static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize.x = data->DesiredSize.y = (data->DesiredSize.x > data->DesiredSize.y ? data->DesiredSize.x : data->DesiredSize.y); }
+ // Helper functions to demonstrate programmatic constraints
+ static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y); }
static void Step(ImGuiSizeCallbackData* data) { float step = (float)(int)(intptr_t)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); }
};
+ const char* test_desc[] =
+ {
+ "Resize vertical only",
+ "Resize horizontal only",
+ "Width > 100, Height > 100",
+ "Width 400-500",
+ "Height 400-500",
+ "Custom: Always Square",
+ "Custom: Fixed Steps (100)",
+ };
+
static bool auto_resize = false;
static int type = 0;
static int display_lines = 10;
@@ -5500,21 +5960,11 @@
ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;
if (ImGui::Begin("Example: Constrained Resize", p_open, flags))
{
- const char* desc[] =
- {
- "Resize vertical only",
- "Resize horizontal only",
- "Width > 100, Height > 100",
- "Width 400-500",
- "Height 400-500",
- "Custom: Always Square",
- "Custom: Fixed Steps (100)",
- };
if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine();
if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine();
if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); }
ImGui::SetNextItemWidth(200);
- ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc));
+ ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc));
ImGui::SetNextItemWidth(200);
ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100);
ImGui::Checkbox("Auto-resize", &auto_resize);
@@ -5528,7 +5978,8 @@
// [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay()
//-----------------------------------------------------------------------------
-// Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use.
+// Demonstrate creating a simple static window with no decoration
+// + a context-menu to choose which corner of the screen to use.
static void ShowExampleAppSimpleOverlay(bool* p_open)
{
const float DISTANCE = 10.0f;
@@ -5541,7 +5992,10 @@
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
}
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
- if (ImGui::Begin("Example: Simple overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
+ if (corner != -1)
+ window_flags |= ImGuiWindowFlags_NoMove;
+ if (ImGui::Begin("Example: Simple overlay", p_open, window_flags))
{
ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)");
ImGui::Separator();
@@ -5568,7 +6022,8 @@
//-----------------------------------------------------------------------------
// Demonstrate using "##" and "###" in identifiers to manipulate ID generation.
-// This apply to all regular items as well. Read FAQ section "How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs." for details.
+// This apply to all regular items as well.
+// Read FAQ section "How can I have multiple widgets with the same label?" for details.
static void ShowExampleAppWindowTitles(bool*)
{
// By default, Windows are uniquely identified by their title.
@@ -5607,10 +6062,10 @@
return;
}
- // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of overloaded operators, etc.
- // Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your types and ImVec2/ImVec4.
- // ImGui defines overloaded operators but they are internal to imgui.cpp and not exposed outside (to avoid messing with your types)
- // In this example we are not using the maths operators!
+ // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
+ // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
+ // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
+ // exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::BeginTabBar("##TabBar"))
@@ -5624,17 +6079,19 @@
ImGui::Text("Gradients");
ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
{
- ImVec2 p = ImGui::GetCursorScreenPos();
+ ImVec2 p0 = ImGui::GetCursorScreenPos();
+ ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
ImU32 col_a = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
ImU32 col_b = ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
- draw_list->AddRectFilledMultiColor(p, ImVec2(p.x + gradient_size.x, p.y + gradient_size.y), col_a, col_b, col_b, col_a);
+ draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
ImGui::InvisibleButton("##gradient1", gradient_size);
}
{
- ImVec2 p = ImGui::GetCursorScreenPos();
+ ImVec2 p0 = ImGui::GetCursorScreenPos();
+ ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
ImU32 col_a = ImGui::GetColorU32(ImVec4(0.0f, 1.0f, 0.0f, 1.0f));
ImU32 col_b = ImGui::GetColorU32(ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
- draw_list->AddRectFilledMultiColor(p, ImVec2(p.x + gradient_size.x, p.y + gradient_size.y), col_a, col_b, col_b, col_a);
+ draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
ImGui::InvisibleButton("##gradient2", gradient_size);
}
@@ -5705,23 +6162,25 @@
if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } }
ImGui::Text("Left-click and drag to add lines,\nRight-click to undo");
- // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered()
- // But you can also draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos().
- // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max).
- ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
- ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
- if (canvas_size.x < 50.0f) canvas_size.x = 50.0f;
- if (canvas_size.y < 50.0f) canvas_size.y = 50.0f;
- draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255));
- draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255, 255, 255, 255));
+ // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use
+ // IsItemHovered(). But you can also draw directly and poll mouse/keyboard by yourself.
+ // You can manipulate the cursor using GetCursorPos() and SetCursorPos().
+ // If you only use the ImDrawList API, you can notify the owner window of its extends with SetCursorPos(max).
+ ImVec2 canvas_p = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
+ ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
+ if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
+ if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
+ draw_list->AddRectFilledMultiColor(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255));
+ draw_list->AddRect(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), IM_COL32(255, 255, 255, 255));
bool adding_preview = false;
- ImGui::InvisibleButton("canvas", canvas_size);
- ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y);
+ ImGui::InvisibleButton("canvas", canvas_sz);
+ ImVec2 mouse_pos_global = ImGui::GetIO().MousePos;
+ ImVec2 mouse_pos_canvas = ImVec2(mouse_pos_global.x - canvas_p.x, mouse_pos_global.y - canvas_p.y);
if (adding_line)
{
adding_preview = true;
- points.push_back(mouse_pos_in_canvas);
+ points.push_back(mouse_pos_canvas);
if (!ImGui::IsMouseDown(0))
adding_line = adding_preview = false;
}
@@ -5729,7 +6188,7 @@
{
if (!adding_line && ImGui::IsMouseClicked(0))
{
- points.push_back(mouse_pos_in_canvas);
+ points.push_back(mouse_pos_canvas);
adding_line = true;
}
if (ImGui::IsMouseClicked(1) && !points.empty())
@@ -5739,9 +6198,11 @@
points.pop_back();
}
}
- draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.)
+
+ // Draw all lines in the canvas (with a clipping rectangle so they don't stray out of it).
+ draw_list->PushClipRect(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), true);
for (int i = 0; i < points.Size - 1; i += 2)
- draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i + 1].x, canvas_pos.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
+ draw_list->AddLine(ImVec2(canvas_p.x + points[i].x, canvas_p.y + points[i].y), ImVec2(canvas_p.x + points[i + 1].x, canvas_p.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
draw_list->PopClipRect();
if (adding_preview)
points.pop_back();
@@ -5760,7 +6221,7 @@
ImVec2 window_size = ImGui::GetWindowSize();
ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);
if (draw_bg)
- ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10+4);
+ ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4);
if (draw_fg)
ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10);
ImGui::EndTabItem();
@@ -5779,14 +6240,14 @@
// Simplified structure to mimic a Document model
struct MyDocument
{
- const char* Name; // Document title
- bool Open; // Set when the document is open (in this demo, we keep an array of all available documents to simplify the demo)
- bool OpenPrev; // Copy of Open from last update.
- bool Dirty; // Set when the document has been modified
- bool WantClose; // Set when the document
- ImVec4 Color; // An arbitrary variable associated to the document
+ const char* Name; // Document title
+ bool Open; // Set when open (we keep an array of all available documents to simplify demo code!)
+ bool OpenPrev; // Copy of Open from last update.
+ bool Dirty; // Set when the document has been modified
+ bool WantClose; // Set when the document
+ ImVec4 Color; // An arbitrary variable associated to the document
- MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f,1.0f,1.0f,1.0f))
+ MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
{
Name = name;
Open = OpenPrev = open;
@@ -5848,10 +6309,11 @@
};
// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
-// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, as opposed
-// to clicking on the regular tab closing button) and stops being submitted, it will take a frame for the tab bar to notice its absence.
-// During this frame there will be a gap in the tab bar, and if the tab that has disappeared was the selected one, the tab bar
-// will report no selected tab during the frame. This will effectively give the impression of a flicker for one frame.
+// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
+// as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
+// the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has
+// disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
+// give the impression of a flicker for one frame.
// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app)
--- a/DoConfig/imgui/imgui_draw.cpp
+++ b/DoConfig/imgui/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.76 WIP
+// dear imgui, v1.78 WIP
// (drawing and font code)
/*
@@ -36,7 +36,7 @@
#include <stdio.h> // vsnprintf, sscanf, printf
#if !defined(alloca)
-#if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__) || defined(__APPLE__) || defined(__SWITCH__)
+#if defined(__GLIBC__) || defined(__sun) || defined(__APPLE__) || defined(__NEWLIB__)
#include <alloca.h> // alloca (glibc uses <alloca.h>. Note that Cygwin may have _WIN32 defined, so the order matters here)
#elif defined(_WIN32)
#include <malloc.h> // alloca
@@ -57,22 +57,19 @@
// Clang/GCC warnings with -Weverything
#if defined(__clang__)
-#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
-#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
-#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is.
-#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
-#if __has_warning("-Wzero-as-null-pointer-constant")
-#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
#endif
-#if __has_warning("-Wcomma")
-#pragma clang diagnostic ignored "-Wcomma" // warning : possible misuse of comma operator here //
-#endif
-#if __has_warning("-Wreserved-id-macro")
-#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier //
-#endif
-#if __has_warning("-Wdouble-promotion")
-#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
-#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
+#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
+#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
+#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
+#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
+#pragma clang diagnostic ignored "-Wcomma" // warning: possible misuse of comma operator here
+#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
+#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
+#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@@ -108,7 +105,7 @@
#pragma clang diagnostic ignored "-Wunused-function"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
-#pragma clang diagnostic ignored "-Wcast-qual" // warning : cast from 'const xxxx *' to 'xxx *' drops const qualifier //
+#pragma clang diagnostic ignored "-Wcast-qual" // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier
#endif
#if defined(__GNUC__)
@@ -120,7 +117,7 @@
#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
#define STBRP_STATIC
-#define STBRP_ASSERT(x) IM_ASSERT(x)
+#define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0)
#define STBRP_SORT ImQsort
#define STB_RECT_PACK_IMPLEMENTATION
#endif
@@ -135,7 +132,7 @@
#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
#define STBTT_malloc(x,u) ((void)(u), IM_ALLOC(x))
#define STBTT_free(x,u) ((void)(u), IM_FREE(x))
-#define STBTT_assert(x) IM_ASSERT(x)
+#define STBTT_assert(x) do { IM_ASSERT(x); } while(0)
#define STBTT_fmod(x,y) ImFmod(x,y)
#define STBTT_sqrt(x) ImSqrt(x)
#define STBTT_pow(x,y) ImPow(x,y)
@@ -391,13 +388,20 @@
}
}
-void ImDrawList::Clear()
+// Initialize before use in a new frame. We always have a command ready in the buffer.
+void ImDrawList::_ResetForNewFrame()
{
+ // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
+ // (those should be IM_STATIC_ASSERT() in theory but with our pre C++11 setup the whole check doesn't compile with GCC)
+ IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
+ IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
+ IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
+
CmdBuffer.resize(0);
IdxBuffer.resize(0);
VtxBuffer.resize(0);
- Flags = _Data ? _Data->InitialFlags : ImDrawListFlags_None;
- _VtxCurrentOffset = 0;
+ Flags = _Data->InitialFlags;
+ memset(&_CmdHeader, 0, sizeof(_CmdHeader));
_VtxCurrentIdx = 0;
_VtxWritePtr = NULL;
_IdxWritePtr = NULL;
@@ -405,13 +409,15 @@
_TextureIdStack.resize(0);
_Path.resize(0);
_Splitter.Clear();
+ CmdBuffer.push_back(ImDrawCmd());
}
-void ImDrawList::ClearFreeMemory()
+void ImDrawList::_ClearFreeMemory()
{
CmdBuffer.clear();
IdxBuffer.clear();
VtxBuffer.clear();
+ Flags = ImDrawListFlags_None;
_VtxCurrentIdx = 0;
_VtxWritePtr = NULL;
_IdxWritePtr = NULL;
@@ -431,16 +437,12 @@
return dst;
}
-// Using macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug builds
-#define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : _Data->ClipRectFullscreen)
-#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : (ImTextureID)NULL)
-
void ImDrawList::AddDrawCmd()
{
ImDrawCmd draw_cmd;
- draw_cmd.ClipRect = GetCurrentClipRect();
- draw_cmd.TextureId = GetCurrentTextureId();
- draw_cmd.VtxOffset = _VtxCurrentOffset;
+ draw_cmd.ClipRect = _CmdHeader.ClipRect; // Same as calling ImDrawCmd_HeaderCopy()
+ draw_cmd.TextureId = _CmdHeader.TextureId;
+ draw_cmd.VtxOffset = _CmdHeader.VtxOffset;
draw_cmd.IdxOffset = IdxBuffer.Size;
IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);
@@ -447,70 +449,105 @@
CmdBuffer.push_back(draw_cmd);
}
+// Pop trailing draw command (used before merging or presenting to user)
+// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL
+void ImDrawList::_PopUnusedDrawCmd()
+{
+ if (CmdBuffer.Size == 0)
+ return;
+ ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ if (curr_cmd->ElemCount == 0 && curr_cmd->UserCallback == NULL)
+ CmdBuffer.pop_back();
+}
+
void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
{
- ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL;
- if (!current_cmd || current_cmd->ElemCount != 0 || current_cmd->UserCallback != NULL)
+ ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ IM_ASSERT(curr_cmd->UserCallback == NULL);
+ if (curr_cmd->ElemCount != 0)
{
AddDrawCmd();
- current_cmd = &CmdBuffer.back();
+ curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
}
- current_cmd->UserCallback = callback;
- current_cmd->UserCallbackData = callback_data;
+ curr_cmd->UserCallback = callback;
+ curr_cmd->UserCallbackData = callback_data;
AddDrawCmd(); // Force a new command after us (see comment below)
}
+// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
+#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
+#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
+#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
+
// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.
// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.
-void ImDrawList::UpdateClipRect()
+void ImDrawList::_OnChangedClipRect()
{
// If current command is used with different settings we need to add a new command
- const ImVec4 curr_clip_rect = GetCurrentClipRect();
- ImDrawCmd* curr_cmd = CmdBuffer.Size > 0 ? &CmdBuffer.Data[CmdBuffer.Size-1] : NULL;
- if (!curr_cmd || (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL)
+ ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0)
{
AddDrawCmd();
return;
}
+ IM_ASSERT(curr_cmd->UserCallback == NULL);
// Try to merge with previous command if it matches, else use current command
- ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
- if (curr_cmd->ElemCount == 0 && prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL)
+ ImDrawCmd* prev_cmd = curr_cmd - 1;
+ if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL)
+ {
CmdBuffer.pop_back();
- else
- curr_cmd->ClipRect = curr_clip_rect;
+ return;
+ }
+
+ curr_cmd->ClipRect = _CmdHeader.ClipRect;
}
-void ImDrawList::UpdateTextureID()
+void ImDrawList::_OnChangedTextureID()
{
// If current command is used with different settings we need to add a new command
- const ImTextureID curr_texture_id = GetCurrentTextureId();
- ImDrawCmd* curr_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL;
- if (!curr_cmd || (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) || curr_cmd->UserCallback != NULL)
+ ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId)
{
AddDrawCmd();
return;
}
+ IM_ASSERT(curr_cmd->UserCallback == NULL);
// Try to merge with previous command if it matches, else use current command
- ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
- if (curr_cmd->ElemCount == 0 && prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->UserCallback == NULL)
+ ImDrawCmd* prev_cmd = curr_cmd - 1;
+ if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL)
+ {
CmdBuffer.pop_back();
- else
- curr_cmd->TextureId = curr_texture_id;
+ return;
+ }
+
+ curr_cmd->TextureId = _CmdHeader.TextureId;
}
-#undef GetCurrentClipRect
-#undef GetCurrentTextureId
+void ImDrawList::_OnChangedVtxOffset()
+{
+ // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this.
+ _VtxCurrentIdx = 0;
+ ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset);
+ if (curr_cmd->ElemCount != 0)
+ {
+ AddDrawCmd();
+ return;
+ }
+ IM_ASSERT(curr_cmd->UserCallback == NULL);
+ curr_cmd->VtxOffset = _CmdHeader.VtxOffset;
+}
// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect)
{
ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y);
- if (intersect_with_current_clip_rect && _ClipRectStack.Size)
+ if (intersect_with_current_clip_rect)
{
- ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size-1];
+ ImVec4 current = _CmdHeader.ClipRect;
if (cr.x < current.x) cr.x = current.x;
if (cr.y < current.y) cr.y = current.y;
if (cr.z > current.z) cr.z = current.z;
@@ -520,7 +557,8 @@
cr.w = ImMax(cr.y, cr.w);
_ClipRectStack.push_back(cr);
- UpdateClipRect();
+ _CmdHeader.ClipRect = cr;
+ _OnChangedClipRect();
}
void ImDrawList::PushClipRectFullScreen()
@@ -530,22 +568,23 @@
void ImDrawList::PopClipRect()
{
- IM_ASSERT(_ClipRectStack.Size > 0);
_ClipRectStack.pop_back();
- UpdateClipRect();
+ _CmdHeader.ClipRect = (_ClipRectStack.Size == 0) ? _Data->ClipRectFullscreen : _ClipRectStack.Data[_ClipRectStack.Size - 1];
+ _OnChangedClipRect();
}
void ImDrawList::PushTextureID(ImTextureID texture_id)
{
_TextureIdStack.push_back(texture_id);
- UpdateTextureID();
+ _CmdHeader.TextureId = texture_id;
+ _OnChangedTextureID();
}
void ImDrawList::PopTextureID()
{
- IM_ASSERT(_TextureIdStack.Size > 0);
_TextureIdStack.pop_back();
- UpdateTextureID();
+ _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1];
+ _OnChangedTextureID();
}
// Reserve space for a number of vertices and indices.
@@ -557,13 +596,12 @@
IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset))
{
- _VtxCurrentOffset = VtxBuffer.Size;
- _VtxCurrentIdx = 0;
- AddDrawCmd();
+ _CmdHeader.VtxOffset = VtxBuffer.Size;
+ _OnChangedVtxOffset();
}
- ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size - 1];
- draw_cmd.ElemCount += idx_count;
+ ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ draw_cmd->ElemCount += idx_count;
int vtx_buffer_old_size = VtxBuffer.Size;
VtxBuffer.resize(vtx_buffer_old_size + vtx_count);
@@ -579,8 +617,8 @@
{
IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
- ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size - 1];
- draw_cmd.ElemCount -= idx_count;
+ ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
+ draw_cmd->ElemCount -= idx_count;
VtxBuffer.shrink(VtxBuffer.Size - vtx_count);
IdxBuffer.shrink(IdxBuffer.Size - idx_count);
}
@@ -630,7 +668,7 @@
_IdxWritePtr += 6;
}
-// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds.
+// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds.
// Those macros expects l-values.
#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0)
#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } while (0)
@@ -642,13 +680,12 @@
if (points_count < 2)
return;
- const ImVec2 uv = _Data->TexUvWhitePixel;
-
+ const ImVec2 opaque_uv = _Data->TexUvWhitePixel;
int count = points_count;
if (!closed)
- count = points_count-1;
+ count = points_count - 1;
- const bool thick_line = thickness > 1.0f;
+ const bool thick_line = (thickness > 1.0f);
if (Flags & ImDrawListFlags_AntiAliasedLines)
{
// Anti-aliased stroke
@@ -655,8 +692,8 @@
const float AA_SIZE = 1.0f;
const ImU32 col_trans = col & ~IM_COL32_A_MASK;
- const int idx_count = thick_line ? count*18 : count*12;
- const int vtx_count = thick_line ? points_count*4 : points_count*3;
+ const int idx_count = thick_line ? count * 18 : count * 12;
+ const int vtx_count = thick_line ? points_count * 4 : points_count * 3;
PrimReserve(idx_count, vtx_count);
// Temporary buffer
@@ -665,7 +702,7 @@
for (int i1 = 0; i1 < count; i1++)
{
- const int i2 = (i1+1) == points_count ? 0 : i1+1;
+ const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
float dx = points[i2].x - points[i1].x;
float dy = points[i2].y - points[i1].y;
IM_NORMALIZE2F_OVER_ZERO(dx, dy);
@@ -673,7 +710,7 @@
temp_normals[i1].y = -dx;
}
if (!closed)
- temp_normals[points_count-1] = temp_normals[points_count-2];
+ temp_normals[points_count - 1] = temp_normals[points_count - 2];
if (!thick_line)
{
@@ -689,8 +726,8 @@
unsigned int idx1 = _VtxCurrentIdx;
for (int i1 = 0; i1 < count; i1++)
{
- const int i2 = (i1+1) == points_count ? 0 : i1+1;
- unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3;
+ const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
+ unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3;
// Average normals
float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
@@ -699,8 +736,8 @@
dm_x *= AA_SIZE;
dm_y *= AA_SIZE;
- // Add temporary vertexes
- ImVec2* out_vtx = &temp_points[i2*2];
+ // Add temporary vertices
+ ImVec2* out_vtx = &temp_points[i2 * 2];
out_vtx[0].x = points[i2].x + dm_x;
out_vtx[0].y = points[i2].y + dm_y;
out_vtx[1].x = points[i2].x - dm_x;
@@ -716,12 +753,12 @@
idx1 = idx2;
}
- // Add vertexes
+ // Add vertices
for (int i = 0; i < points_count; i++)
{
- _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
- _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans;
- _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col_trans;
+ _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;
+ _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans;
+ _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans;
_VtxWritePtr += 3;
}
}
@@ -730,14 +767,15 @@
const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;
if (!closed)
{
+ const int points_last = points_count - 1;
temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE);
temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness);
temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness);
temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE);
- temp_points[(points_count-1)*4+0] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE);
- temp_points[(points_count-1)*4+1] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness);
- temp_points[(points_count-1)*4+2] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness);
- temp_points[(points_count-1)*4+3] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE);
+ temp_points[points_last * 4 + 0] = points[points_last] + temp_normals[points_last] * (half_inner_thickness + AA_SIZE);
+ temp_points[points_last * 4 + 1] = points[points_last] + temp_normals[points_last] * (half_inner_thickness);
+ temp_points[points_last * 4 + 2] = points[points_last] - temp_normals[points_last] * (half_inner_thickness);
+ temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE);
}
// FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.
@@ -744,8 +782,8 @@
unsigned int idx1 = _VtxCurrentIdx;
for (int i1 = 0; i1 < count; i1++)
{
- const int i2 = (i1+1) == points_count ? 0 : i1+1;
- unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4;
+ const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment
+ const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment
// Average normals
float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
@@ -756,8 +794,8 @@
float dm_in_x = dm_x * half_inner_thickness;
float dm_in_y = dm_y * half_inner_thickness;
- // Add temporary vertexes
- ImVec2* out_vtx = &temp_points[i2*4];
+ // Add temporary vertices
+ ImVec2* out_vtx = &temp_points[i2 * 4];
out_vtx[0].x = points[i2].x + dm_out_x;
out_vtx[0].y = points[i2].y + dm_out_y;
out_vtx[1].x = points[i2].x + dm_in_x;
@@ -768,24 +806,24 @@
out_vtx[3].y = points[i2].y - dm_out_y;
// Add indexes
- _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
- _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+1);
- _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0);
- _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10] = (ImDrawIdx)(idx2+0); _IdxWritePtr[11] = (ImDrawIdx)(idx2+1);
- _IdxWritePtr[12] = (ImDrawIdx)(idx2+2); _IdxWritePtr[13] = (ImDrawIdx)(idx1+2); _IdxWritePtr[14] = (ImDrawIdx)(idx1+3);
- _IdxWritePtr[15] = (ImDrawIdx)(idx1+3); _IdxWritePtr[16] = (ImDrawIdx)(idx2+3); _IdxWritePtr[17] = (ImDrawIdx)(idx2+2);
+ _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2);
+ _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 1);
+ _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0);
+ _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1);
+ _IdxWritePtr[12] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[13] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[14] = (ImDrawIdx)(idx1 + 3);
+ _IdxWritePtr[15] = (ImDrawIdx)(idx1 + 3); _IdxWritePtr[16] = (ImDrawIdx)(idx2 + 3); _IdxWritePtr[17] = (ImDrawIdx)(idx2 + 2);
_IdxWritePtr += 18;
idx1 = idx2;
}
- // Add vertexes
+ // Add vertices
for (int i = 0; i < points_count; i++)
{
- _VtxWritePtr[0].pos = temp_points[i*4+0]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col_trans;
- _VtxWritePtr[1].pos = temp_points[i*4+1]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
- _VtxWritePtr[2].pos = temp_points[i*4+2]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
- _VtxWritePtr[3].pos = temp_points[i*4+3]; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col_trans;
+ _VtxWritePtr[0].pos = temp_points[i * 4 + 0]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col_trans;
+ _VtxWritePtr[1].pos = temp_points[i * 4 + 1]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;
+ _VtxWritePtr[2].pos = temp_points[i * 4 + 2]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;
+ _VtxWritePtr[3].pos = temp_points[i * 4 + 3]; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col_trans;
_VtxWritePtr += 4;
}
}
@@ -794,13 +832,13 @@
else
{
// Non Anti-aliased Stroke
- const int idx_count = count*6;
- const int vtx_count = count*4; // FIXME-OPT: Not sharing edges
+ const int idx_count = count * 6;
+ const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges
PrimReserve(idx_count, vtx_count);
for (int i1 = 0; i1 < count; i1++)
{
- const int i2 = (i1+1) == points_count ? 0 : i1+1;
+ const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
const ImVec2& p1 = points[i1];
const ImVec2& p2 = points[i2];
@@ -810,14 +848,14 @@
dx *= (thickness * 0.5f);
dy *= (thickness * 0.5f);
- _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
- _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
- _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
- _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col;
+ _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;
+ _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;
+ _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;
+ _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col;
_VtxWritePtr += 4;
- _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2);
- _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3);
+ _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + 2);
+ _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx + 2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + 3);
_IdxWritePtr += 6;
_VtxCurrentIdx += 4;
}
@@ -837,22 +875,22 @@
// Anti-aliased Fill
const float AA_SIZE = 1.0f;
const ImU32 col_trans = col & ~IM_COL32_A_MASK;
- const int idx_count = (points_count-2)*3 + points_count*6;
- const int vtx_count = (points_count*2);
+ const int idx_count = (points_count - 2)*3 + points_count * 6;
+ const int vtx_count = (points_count * 2);
PrimReserve(idx_count, vtx_count);
// Add indexes for fill
unsigned int vtx_inner_idx = _VtxCurrentIdx;
- unsigned int vtx_outer_idx = _VtxCurrentIdx+1;
+ unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
for (int i = 2; i < points_count; i++)
{
- _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+((i-1)<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx+(i<<1));
+ _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + ((i - 1) << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (i << 1));
_IdxWritePtr += 3;
}
// Compute normals
ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630
- for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++)
+ for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
{
const ImVec2& p0 = points[i0];
const ImVec2& p1 = points[i1];
@@ -863,7 +901,7 @@
temp_normals[i0].y = -dx;
}
- for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++)
+ for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
{
// Average normals
const ImVec2& n0 = temp_normals[i0];
@@ -880,8 +918,8 @@
_VtxWritePtr += 2;
// Add indexes for fringes
- _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+(i0<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx+(i0<<1));
- _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx+(i1<<1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx+(i1<<1));
+ _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
+ _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
_IdxWritePtr += 6;
}
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
@@ -889,7 +927,7 @@
else
{
// Non Anti-aliased Fill
- const int idx_count = (points_count-2)*3;
+ const int idx_count = (points_count - 2)*3;
const int vtx_count = points_count;
PrimReserve(idx_count, vtx_count);
for (int i = 0; i < vtx_count; i++)
@@ -899,7 +937,7 @@
}
for (int i = 2; i < points_count; i++)
{
- _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+i-1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+i);
+ _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i - 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + i);
_IdxWritePtr += 3;
}
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
@@ -966,20 +1004,20 @@
float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
d2 = (d2 >= 0) ? d2 : -d2;
d3 = (d3 >= 0) ? d3 : -d3;
- if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy))
+ if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
{
path->push_back(ImVec2(x4, y4));
}
else if (level < 10)
{
- float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f;
- float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f;
- float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f;
- float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f;
- float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f;
- float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f;
- PathBezierToCasteljau(path, x1,y1, x12,y12, x123,y123, x1234,y1234, tess_tol, level+1);
- PathBezierToCasteljau(path, x1234,y1234, x234,y234, x34,y34, x4,y4, tess_tol, level+1);
+ float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
+ float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
+ float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
+ float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
+ float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
+ float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
+ PathBezierToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
+ PathBezierToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
}
}
@@ -1039,9 +1077,9 @@
if ((col & IM_COL32_A_MASK) == 0)
return;
if (Flags & ImDrawListFlags_AntiAliasedLines)
- PathRect(p_min + ImVec2(0.50f,0.50f), p_max - ImVec2(0.50f,0.50f), rounding, rounding_corners);
+ PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, rounding_corners);
else
- PathRect(p_min + ImVec2(0.50f,0.50f), p_max - ImVec2(0.49f,0.49f), rounding, rounding_corners); // Better looking lower-right corner and rounded non-AA shapes.
+ PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, rounding_corners); // Better looking lower-right corner and rounded non-AA shapes.
PathStroke(col, true, thickness);
}
@@ -1069,8 +1107,8 @@
const ImVec2 uv = _Data->TexUvWhitePixel;
PrimReserve(6, 4);
- PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2));
- PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+3));
+ PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2));
+ PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 3));
PrimWriteVtx(p_min, uv, col_upr_left);
PrimWriteVtx(ImVec2(p_max.x, p_min.y), uv, col_upr_right);
PrimWriteVtx(p_max, uv, col_bot_right);
@@ -1234,9 +1272,9 @@
if (font_size == 0.0f)
font_size = _Data->FontSize;
- IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
+ IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
- ImVec4 clip_rect = _ClipRectStack.back();
+ ImVec4 clip_rect = _CmdHeader.ClipRect;
if (cpu_fine_clip_rect)
{
clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x);
@@ -1257,7 +1295,7 @@
if ((col & IM_COL32_A_MASK) == 0)
return;
- const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back();
+ const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;
if (push_texture_id)
PushTextureID(user_texture_id);
@@ -1273,7 +1311,7 @@
if ((col & IM_COL32_A_MASK) == 0)
return;
- const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back();
+ const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;
if (push_texture_id)
PushTextureID(user_texture_id);
@@ -1356,27 +1394,20 @@
if (_Channels[i]._CmdBuffer.Size == 0)
{
ImDrawCmd draw_cmd;
- draw_cmd.ClipRect = draw_list->_ClipRectStack.back();
- draw_cmd.TextureId = draw_list->_TextureIdStack.back();
+ ImDrawCmd_HeaderCopy(&draw_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset
_Channels[i]._CmdBuffer.push_back(draw_cmd);
}
}
}
-static inline bool CanMergeDrawCommands(ImDrawCmd* a, ImDrawCmd* b)
-{
- return memcmp(&a->ClipRect, &b->ClipRect, sizeof(a->ClipRect)) == 0 && a->TextureId == b->TextureId && a->VtxOffset == b->VtxOffset && !a->UserCallback && !b->UserCallback;
-}
-
void ImDrawListSplitter::Merge(ImDrawList* draw_list)
{
- // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.
+ // Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.
if (_Count <= 1)
return;
SetCurrentChannel(draw_list, 0);
- if (draw_list->CmdBuffer.Size != 0 && draw_list->CmdBuffer.back().ElemCount == 0)
- draw_list->CmdBuffer.pop_back();
+ draw_list->_PopUnusedDrawCmd();
// Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command.
int new_cmd_buffer_count = 0;
@@ -1386,14 +1417,21 @@
for (int i = 1; i < _Count; i++)
{
ImDrawChannel& ch = _Channels[i];
+
+ // Equivalent of PopUnusedDrawCmd() for this channel's cmdbuffer and except we don't need to test for UserCallback.
if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0)
ch._CmdBuffer.pop_back();
- if (ch._CmdBuffer.Size > 0 && last_cmd != NULL && CanMergeDrawCommands(last_cmd, &ch._CmdBuffer[0]))
+
+ if (ch._CmdBuffer.Size > 0 && last_cmd != NULL)
{
- // Merge previous channel last draw command with current channel first draw command if matching.
- last_cmd->ElemCount += ch._CmdBuffer[0].ElemCount;
- idx_offset += ch._CmdBuffer[0].ElemCount;
- ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges.
+ ImDrawCmd* next_cmd = &ch._CmdBuffer[0];
+ if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL)
+ {
+ // Merge previous channel last draw command with current channel first draw command if matching.
+ last_cmd->ElemCount += next_cmd->ElemCount;
+ idx_offset += next_cmd->ElemCount;
+ ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges.
+ }
}
if (ch._CmdBuffer.Size > 0)
last_cmd = &ch._CmdBuffer.back();
@@ -1418,8 +1456,18 @@
if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; }
}
draw_list->_IdxWritePtr = idx_write;
- draw_list->UpdateClipRect(); // We call this instead of AddDrawCmd(), so that empty channels won't produce an extra draw call.
- draw_list->UpdateTextureID();
+
+ // Ensure there's always a non-callback draw command trailing the command-buffer
+ if (draw_list->CmdBuffer.Size == 0 || draw_list->CmdBuffer.back().UserCallback != NULL)
+ draw_list->AddDrawCmd();
+
+ // If current command is used with different settings we need to add a new command
+ ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];
+ if (curr_cmd->ElemCount == 0)
+ ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset
+ else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)
+ draw_list->AddDrawCmd();
+
_Count = 1;
}
@@ -1428,6 +1476,7 @@
IM_ASSERT(idx >= 0 && idx < _Count);
if (_Current == idx)
return;
+
// Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap()
memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer));
memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer));
@@ -1435,6 +1484,13 @@
memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer));
memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer));
draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size;
+
+ // If current command is used with different settings we need to add a new command
+ ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];
+ if (curr_cmd->ElemCount == 0)
+ ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset
+ else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)
+ draw_list->AddDrawCmd();
}
//-----------------------------------------------------------------------------
@@ -1558,7 +1614,6 @@
// The white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.
const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 108;
const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;
-const unsigned int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0x80000000;
static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =
{
"..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX "
@@ -1747,8 +1802,8 @@
}
// Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)
-static unsigned int stb_decompress_length(const unsigned char *input);
-static unsigned int stb_decompress(unsigned char *output, const unsigned char *input, unsigned int length);
+static unsigned int stb_decompress_length(const unsigned char* input);
+static unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length);
static const char* GetDefaultCompressedFontDataTTFBase85();
static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; }
static void Decode85(const unsigned char* src, unsigned char* dst)
@@ -1755,7 +1810,7 @@
{
while (*src)
{
- unsigned int tmp = Decode85Byte(src[0]) + 85*(Decode85Byte(src[1]) + 85*(Decode85Byte(src[2]) + 85*(Decode85Byte(src[3]) + 85*Decode85Byte(src[4]))));
+ unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4]))));
dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness.
src += 5;
dst += 4;
@@ -1822,7 +1877,7 @@
ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
{
const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data);
- unsigned char* buf_decompressed_data = (unsigned char *)IM_ALLOC(buf_decompressed_size);
+ unsigned char* buf_decompressed_data = (unsigned char*)IM_ALLOC(buf_decompressed_size);
stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size);
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
@@ -1841,14 +1896,11 @@
return font;
}
-int ImFontAtlas::AddCustomRectRegular(unsigned int id, int width, int height)
+int ImFontAtlas::AddCustomRectRegular(int width, int height)
{
- // Breaking change on 2019/11/21 (1.74): ImFontAtlas::AddCustomRectRegular() now requires an ID >= 0x110000 (instead of >= 0x10000)
- IM_ASSERT(id >= 0x110000);
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
ImFontAtlasCustomRect r;
- r.ID = id;
r.Width = (unsigned short)width;
r.Height = (unsigned short)height;
CustomRects.push_back(r);
@@ -1857,13 +1909,16 @@
int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset)
{
+#ifdef IMGUI_USE_WCHAR32
+ IM_ASSERT(id <= IM_UNICODE_CODEPOINT_MAX);
+#endif
IM_ASSERT(font != NULL);
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
ImFontAtlasCustomRect r;
- r.ID = id;
r.Width = (unsigned short)width;
r.Height = (unsigned short)height;
+ r.GlyphID = id;
r.GlyphAdvanceX = advance_x;
r.GlyphOffset = offset;
r.Font = font;
@@ -1888,7 +1943,6 @@
IM_ASSERT(CustomRectIds[0] != -1);
ImFontAtlasCustomRect& r = CustomRects[CustomRectIds[0]];
- IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID);
ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r.X, (float)r.Y);
ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1];
*out_size = size;
@@ -2110,7 +2164,7 @@
if (atlas->TexDesiredWidth > 0)
atlas->TexWidth = atlas->TexDesiredWidth;
else
- atlas->TexWidth = (surface_sqrt >= 4096*0.7f) ? 4096 : (surface_sqrt >= 2048*0.7f) ? 2048 : (surface_sqrt >= 1024*0.7f) ? 1024 : 512;
+ atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
// 5. Start packing
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
@@ -2223,9 +2277,9 @@
if (atlas->CustomRectIds[0] >= 0)
return;
if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors))
- atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H);
+ atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H);
else
- atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_ID, 2, 2);
+ atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(2, 2);
}
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
@@ -2274,7 +2328,6 @@
IM_ASSERT(atlas->CustomRectIds[0] >= 0);
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->CustomRectIds[0]];
- IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID);
IM_ASSERT(r.IsPacked());
const int w = atlas->TexWidth;
@@ -2309,13 +2362,13 @@
for (int i = 0; i < atlas->CustomRects.Size; i++)
{
const ImFontAtlasCustomRect& r = atlas->CustomRects[i];
- if (r.Font == NULL || r.ID >= 0x110000)
+ if (r.Font == NULL || r.GlyphID == 0)
continue;
IM_ASSERT(r.Font->ContainerAtlas == atlas);
ImVec2 uv0, uv1;
atlas->CalcCustomRectUV(&r, &uv0, &uv1);
- r.Font->AddGlyph((ImWchar)r.ID, r.GlyphOffset.x, r.GlyphOffset.y, r.GlyphOffset.x + r.Width, r.GlyphOffset.y + r.Height, uv0.x, uv0.y, uv1.x, uv1.y, r.GlyphAdvanceX);
+ r.Font->AddGlyph((ImWchar)r.GlyphID, r.GlyphOffset.x, r.GlyphOffset.y, r.GlyphOffset.x + r.Width, r.GlyphOffset.y + r.Height, uv0.x, uv0.y, uv1.x, uv1.y, r.GlyphAdvanceX);
}
// Build all fonts lookup tables
@@ -2325,7 +2378,7 @@
// Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
// However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
- // FIXME: Also note that 0x2026 is currently seldomly included in our font ranges. Because of this we are more likely to use three individual dots.
+ // FIXME: Also note that 0x2026 is currently seldom included in our font ranges. Because of this we are more likely to use three individual dots.
for (int i = 0; i < atlas->Fonts.size(); i++)
{
ImFont* font = atlas->Fonts[i];
@@ -2358,7 +2411,7 @@
{
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x3131, 0x3163, // Korean alphabets
- 0xAC00, 0xD79D, // Korean characters
+ 0xAC00, 0xD7A3, // Korean characters
0,
};
return &ranges[0];
@@ -2674,7 +2727,7 @@
tab_glyph.Codepoint = '\t';
tab_glyph.AdvanceX *= IM_TABSIZE;
IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX;
- IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1);
+ IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size - 1);
}
// Mark special glyphs as not visible (note that AddGlyph already mark as non-visible glyphs with zero-size polygons)
@@ -2865,7 +2918,7 @@
}
// Allow wrapping after punctuation.
- inside_word = !(c == '.' || c == ',' || c == ';' || c == '!' || c == '?' || c == '\"');
+ inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"');
}
// We ignore blank width at the end of the line (they can be skipped)
@@ -2891,7 +2944,7 @@
const float line_height = size;
const float scale = size / FontSize;
- ImVec2 text_size = ImVec2(0,0);
+ ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
const bool word_wrap_enabled = (wrap_width > 0.0f);
@@ -3169,7 +3222,7 @@
// Give back unused vertices (clipped ones, blanks) ~ this is essentially a PrimUnreserve() action.
draw_list->VtxBuffer.Size = (int)(vtx_write - draw_list->VtxBuffer.Data); // Same as calling shrink()
draw_list->IdxBuffer.Size = (int)(idx_write - draw_list->IdxBuffer.Data);
- draw_list->CmdBuffer[draw_list->CmdBuffer.Size-1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);
+ draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);
draw_list->_VtxWritePtr = vtx_write;
draw_list->_IdxWritePtr = idx_write;
draw_list->_VtxCurrentIdx = vtx_current_idx;
@@ -3255,10 +3308,10 @@
pos -= offset;
const ImTextureID tex_id = font_atlas->TexID;
draw_list->PushTextureID(tex_id);
- draw_list->AddImage(tex_id, pos + ImVec2(1,0)*scale, pos + ImVec2(1,0)*scale + size*scale, uv[2], uv[3], col_shadow);
- draw_list->AddImage(tex_id, pos + ImVec2(2,0)*scale, pos + ImVec2(2,0)*scale + size*scale, uv[2], uv[3], col_shadow);
- draw_list->AddImage(tex_id, pos, pos + size*scale, uv[2], uv[3], col_border);
- draw_list->AddImage(tex_id, pos, pos + size*scale, uv[0], uv[1], col_fill);
+ draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
+ draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
+ draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border);
+ draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill);
draw_list->PopTextureID();
}
}
@@ -3347,7 +3400,7 @@
// Helper for ColorPicker4()
// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
-// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether.
+// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.
// FIXME: uses ImGui::GetColorU32
void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags)
{
@@ -3513,7 +3566,7 @@
// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).
// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size.
//-----------------------------------------------------------------------------
-static const char proggy_clean_ttf_compressed_data_base85[11980+1] =
+static const char proggy_clean_ttf_compressed_data_base85[11980 + 1] =
"7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/"
"2*>]b(MC;$jPfY.;h^`IWM9<Lh2TlS+f-s$o6Q<BWH`YiU.xfLq$N;$0iR/GX:U(jcW2p/W*q?-qmnUCI;jHSAiFWM.R*kU@C=GH?a9wp8f$e.-4^Qg1)Q-GL(lf(r/7GrRgwV%MS=C#"
"`8ND>Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1<q-UE31#^-V'8IRUo7Qf./L>=Ke$$'5F%)]0^#0X@U.a<r:QLtFsLcL6##lOj)#.Y5<-R&KgLwqJfLgN&;Q?gI^#DY2uL"
--- a/DoConfig/imgui/imgui_impl_glfw.cpp
+++ b/DoConfig/imgui/imgui_impl_glfw.cpp
@@ -68,7 +68,7 @@
static GLFWwindow* g_Window = NULL; // Main window
static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown;
static double g_Time = 0.0;
-static bool g_MouseJustPressed[5] = { false, false, false, false, false };
+static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {};
static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
static bool g_InstalledCallbacks = false;
@@ -358,7 +358,7 @@
// Setup time step
double current_time = glfwGetTime();
- io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f);
+ io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);
g_Time = current_time;
ImGui_ImplGlfw_UpdateMousePosAndButtons();
--- a/DoConfig/imgui/imgui_impl_glfw.h
+++ b/DoConfig/imgui/imgui_impl_glfw.h
@@ -17,6 +17,7 @@
// Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure!
#pragma once
+#include "imgui.h" // IMGUI_IMPL_API
struct GLFWwindow;
--- a/DoConfig/imgui/imgui_impl_opengl3.cpp
+++ b/DoConfig/imgui/imgui_impl_opengl3.cpp
@@ -13,6 +13,9 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
+// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
+// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
+// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
@@ -76,28 +79,8 @@
#else
#include <stdint.h> // intptr_t
#endif
-#if defined(__APPLE__)
-#include "TargetConditionals.h"
-#endif
-// Auto-enable GLES on matching platforms
-#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
-#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
-#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
-#elif defined(__EMSCRIPTEN__)
-#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
-#endif
-#endif
-#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3)
-#undef IMGUI_IMPL_OPENGL_LOADER_GL3W
-#undef IMGUI_IMPL_OPENGL_LOADER_GLEW
-#undef IMGUI_IMPL_OPENGL_LOADER_GLAD
-#undef IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
-#undef IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
-#undef IMGUI_IMPL_OPENGL_LOADER_CUSTOM
-#endif
-
// GL includes
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
@@ -119,12 +102,16 @@
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
+#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
+#endif
#include <glbinding/Binding.h> // Needs to be initialized with glbinding::Binding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
+#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
+#endif
#include <glbinding/glbinding.h>// Needs to be initialized with glbinding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
@@ -141,25 +128,25 @@
#endif
// OpenGL Data
-static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries.
+static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
-static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
-static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
+static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
+static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
- // Query for GL version
+ // Query for GL version (e.g. 320 for GL 3.2)
#if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major, minor;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
- g_GlVersion = major * 1000 + minor;
+ g_GlVersion = (GLuint)(major * 100 + minor * 10);
#else
- g_GlVersion = 2000; // GLES 2
+ g_GlVersion = 200; // GLES 2
#endif
// Setup back-end capabilities flags
@@ -166,7 +153,7 @@
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
- if (g_GlVersion >= 3200)
+ if (g_GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
@@ -178,6 +165,9 @@
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == NULL)
glsl_version = "#version 300 es";
+#elif defined(__APPLE__)
+ if (glsl_version == NULL)
+ glsl_version = "#version 150";
#else
if (glsl_version == NULL)
glsl_version = "#version 130";
@@ -242,6 +232,14 @@
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
+ // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
+ bool clip_origin_lower_left = true;
+#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
+ GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin);
+ if (current_clip_origin == GL_UPPER_LEFT)
+ clip_origin_lower_left = false;
+#endif
+
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
@@ -249,6 +247,7 @@
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
+ if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
@@ -293,14 +292,14 @@
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
- GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
- GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
+ GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
+ GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
#ifdef GL_SAMPLER_BINDING
- GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
+ GLuint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler);
#endif
- GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
+ GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
- GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
+ GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
@@ -317,12 +316,6 @@
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
- bool clip_origin_lower_left = true;
-#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
- GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
- if (last_clip_origin == GL_UPPER_LEFT)
- clip_origin_lower_left = false;
-#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
@@ -343,8 +336,8 @@
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
- glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
+ glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
@@ -370,15 +363,12 @@
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
- if (clip_origin_lower_left)
- glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
- else
- glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
+ glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
- if (g_GlVersion >= 3200)
+ if (g_GlVersion >= 320)
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
#endif
@@ -653,9 +643,9 @@
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
- g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position");
- g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV");
- g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color");
+ g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position");
+ g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV");
+ g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
--- a/DoConfig/imgui/imgui_impl_opengl3.h
+++ b/DoConfig/imgui/imgui_impl_opengl3.h
@@ -12,7 +12,7 @@
// https://github.com/ocornut/imgui
// About Desktop OpenGL function loaders:
-// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
+// Modern Desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
@@ -22,6 +22,7 @@
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
+#include "imgui.h" // IMGUI_IMPL_API
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
@@ -35,36 +36,49 @@
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
-// Specific OpenGL versions
+// Specific OpenGL ES versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
-// Desktop OpenGL: attempt to detect default GL loader based on available header files.
+// Attempt to auto-detect the default Desktop GL loader based on available header files.
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash in ImGui_ImplOpenGL3_Init().
-// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
-#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
+// You can explicitly select a loader by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
+#if !defined(IMGUI_IMPL_OPENGL_ES2) \
+ && !defined(IMGUI_IMPL_OPENGL_ES3) \
+ && !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
- #if defined(__has_include)
- #if __has_include(<GL/glew.h>)
- #define IMGUI_IMPL_OPENGL_LOADER_GLEW
- #elif __has_include(<glad/glad.h>)
- #define IMGUI_IMPL_OPENGL_LOADER_GLAD
- #elif __has_include(<GL/gl3w.h>)
- #define IMGUI_IMPL_OPENGL_LOADER_GL3W
- #elif __has_include(<glbinding/glbinding.h>)
- #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
- #elif __has_include(<glbinding/Binding.h>)
- #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
- #else
- #error "Cannot detect OpenGL loader!"
- #endif
- #else
- #define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W
- #endif
+
+// Try to detect GLES on matching platforms
+#if defined(__APPLE__)
+#include "TargetConditionals.h"
#endif
+#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
+#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
+#elif defined(__EMSCRIPTEN__)
+#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
+// Otherwise try to detect supported Desktop OpenGL loaders..
+#elif defined(__has_include)
+#if __has_include(<GL/glew.h>)
+ #define IMGUI_IMPL_OPENGL_LOADER_GLEW
+#elif __has_include(<glad/glad.h>)
+ #define IMGUI_IMPL_OPENGL_LOADER_GLAD
+#elif __has_include(<GL/gl3w.h>)
+ #define IMGUI_IMPL_OPENGL_LOADER_GL3W
+#elif __has_include(<glbinding/glbinding.h>)
+ #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
+#elif __has_include(<glbinding/Binding.h>)
+ #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
+#else
+ #error "Cannot detect OpenGL loader!"
+#endif
+#else
+ #define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
+#endif
+
+#endif
--- a/DoConfig/imgui/imgui_internal.h
+++ b/DoConfig/imgui/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.76 WIP
+// dear imgui, v1.78 WIP
// (internal structures/api)
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
@@ -9,16 +9,27 @@
/*
Index of this file:
-// Header mess
-// Forward declarations
-// STB libraries includes
-// Context pointer
-// Generic helpers
-// Misc data structures
-// Main imgui context
-// Tab bar, tab item
-// Internal API
+// [SECTION] Header mess
+// [SECTION] Forward declarations
+// [SECTION] Context pointer
+// [SECTION] STB libraries includes
+// [SECTION] Macros
+// [SECTION] Generic helpers
+// [SECTION] ImDrawList support
+// [SECTION] Widgets support: flags, enums, data structures
+// [SECTION] Columns support
+// [SECTION] Settings support
+// [SECTION] Multi-select support
+// [SECTION] Docking support
+// [SECTION] Viewport support
+// [SECTION] ImGuiContext (main imgui context)
+// [SECTION] ImGuiWindowTempData, ImGuiWindow
+// [SECTION] Tab bar, Tab item support
+// [SECTION] Table support
+// [SECTION] Internal API
+// [SECTION] Test Engine Hooks (imgui_test_engine)
+
*/
#pragma once
@@ -25,7 +36,7 @@
#ifndef IMGUI_DISABLE
//-----------------------------------------------------------------------------
-// Header mess
+// [SECTION] Header mess
//-----------------------------------------------------------------------------
#ifndef IMGUI_VERSION
@@ -46,31 +57,32 @@
// Clang/GCC warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h
-#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx'
+#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
+#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h
+#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h
#pragma clang diagnostic ignored "-Wold-style-cast"
-#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
-#endif
-#if __has_warning("-Wdouble-promotion")
#pragma clang diagnostic ignored "-Wdouble-promotion"
-#endif
+#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#elif defined(__GNUC__)
#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
-#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
+#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
+#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif
// Legacy defines
-#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Renamed in 1.74
+#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Renamed in 1.74
#error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
#endif
-#ifdef IMGUI_DISABLE_MATH_FUNCTIONS // Renamed in 1.74
+#ifdef IMGUI_DISABLE_MATH_FUNCTIONS // Renamed in 1.74
#error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
#endif
//-----------------------------------------------------------------------------
-// Forward declarations
+// [SECTION] Forward declarations
//-----------------------------------------------------------------------------
struct ImBitVector; // Store 1-bit per value
@@ -86,7 +98,7 @@
struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box
struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() internal data
struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only
-struct ImGuiNavMoveResult; // Result of a directional navigation move query result
+struct ImGuiNavMoveResult; // Result of a gamepad/keyboard directional navigation move query result
struct ImGuiNextWindowData; // Storage for SetNextWindow** functions
struct ImGuiNextItemData; // Storage for SetNextItem** functions
struct ImGuiPopupData; // Storage for current popup stack
@@ -119,8 +131,17 @@
typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx()
typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx()
+//-----------------------------------------------------------------------------
+// [SECTION] Context pointer
+// See implementation of this variable in imgui.cpp for comments and details.
+//-----------------------------------------------------------------------------
+
+#ifndef GImGui
+extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
+#endif
+
//-------------------------------------------------------------------------
-// STB libraries includes
+// [SECTION] STB libraries includes
//-------------------------------------------------------------------------
namespace ImStb
@@ -130,7 +151,7 @@
#undef STB_TEXTEDIT_CHARTYPE
#define STB_TEXTEDIT_STRING ImGuiInputTextState
#define STB_TEXTEDIT_CHARTYPE ImWchar
-#define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f
+#define STB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f)
#define STB_TEXTEDIT_UNDOSTATECOUNT 99
#define STB_TEXTEDIT_UNDOCHARCOUNT 999
#include "imstb_textedit.h"
@@ -138,17 +159,9 @@
} // namespace ImStb
//-----------------------------------------------------------------------------
-// Context pointer
+// [SECTION] Macros
//-----------------------------------------------------------------------------
-#ifndef GImGui
-extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
-#endif
-
-//-----------------------------------------------------------------------------
-// Macros
-//-----------------------------------------------------------------------------
-
// Debug Logging
#ifndef IMGUI_DEBUG_LOG
#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__)
@@ -196,12 +209,25 @@
#define IMGUI_CDECL
#endif
+// Debug Tools
+// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item.
+#ifndef IM_DEBUG_BREAK
+#if defined(__clang__)
+#define IM_DEBUG_BREAK() __builtin_debugtrap()
+#elif defined (_MSC_VER)
+#define IM_DEBUG_BREAK() __debugbreak()
+#else
+#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!
+#endif
+#endif // #ifndef IM_DEBUG_BREAK
+
//-----------------------------------------------------------------------------
-// Generic helpers
+// [SECTION] Generic helpers
// Note that the ImXXX helpers functions are lower-level than ImGui functions.
// ImGui functions or the ImGui context are never called/used from other ImXXX functions.
//-----------------------------------------------------------------------------
-// - Helpers: Misc
+// - Helpers: Hashing
+// - Helpers: Sorting
// - Helpers: Bit manipulation
// - Helpers: String, Formatting
// - Helpers: UTF-8 <> wchar conversions
@@ -208,7 +234,9 @@
// - Helpers: ImVec2/ImVec4 operators
// - Helpers: Maths
// - Helpers: Geometry
-// - Helpers: Bit arrays
+// - Helper: ImVec1
+// - Helper: ImVec2ih
+// - Helper: ImRect
// - Helper: ImBitArray
// - Helper: ImBitVector
// - Helper: ImSpan<>, ImSpanAllocator<>
@@ -216,8 +244,7 @@
// - Helper: ImChunkStream<>
//-----------------------------------------------------------------------------
-// Helpers: Misc
-#define ImQsort qsort
+// Helpers: Hashing
IMGUI_API ImU32 ImHashData(const void* data, size_t data_size, ImU32 seed = 0);
IMGUI_API ImU32 ImHashStr(const char* data, size_t data_size = 0, ImU32 seed = 0);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -224,6 +251,9 @@
static inline ImU32 ImHash(const void* data, int size, ImU32 seed = 0) { return size ? ImHashData(data, (size_t)size, seed) : ImHashStr((const char*)data, 0, seed); } // [moved to ImHashStr/ImHashData in 1.68]
#endif
+// Helpers: Sorting
+#define ImQsort qsort
+
// Helpers: Color Blending
IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);
@@ -266,12 +296,12 @@
// We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.)
// We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself.
#ifdef IMGUI_DEFINE_MATH_OPERATORS
-static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); }
-static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); }
-static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); }
-static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); }
-static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); }
-static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); }
+static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); }
+static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); }
+static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }
+static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); }
+static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
+static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); }
static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; }
static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; }
static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }
@@ -278,9 +308,9 @@
static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }
static inline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs) { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; }
static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }
-static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z, lhs.w+rhs.w); }
-static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); }
-static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x*rhs.x, lhs.y*rhs.y, lhs.z*rhs.z, lhs.w*rhs.w); }
+static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }
+static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }
+static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
#endif
// Helpers: File System
@@ -293,7 +323,6 @@
static inline ImU64 ImFileRead(void*, ImU64, ImU64, ImFileHandle) { return 0; }
static inline ImU64 ImFileWrite(const void*, ImU64, ImU64, ImFileHandle) { return 0; }
#endif
-
#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
typedef FILE* ImFileHandle;
IMGUI_API ImFileHandle ImFileOpen(const char* filename, const char* mode);
@@ -339,9 +368,9 @@
static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); }
static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
-static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; }
-static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; }
-static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; }
+static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
+static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
+static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; }
static inline float ImFloor(float f) { return (float)(int)(f); }
static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
@@ -361,7 +390,62 @@
inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; }
IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy);
-// Helpers: Bit arrays
+// Helper: ImVec1 (1D vector)
+// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
+struct ImVec1
+{
+ float x;
+ ImVec1() { x = 0.0f; }
+ ImVec1(float _x) { x = _x; }
+};
+
+// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)
+struct ImVec2ih
+{
+ short x, y;
+ ImVec2ih() { x = y = 0; }
+ ImVec2ih(short _x, short _y) { x = _x; y = _y; }
+ explicit ImVec2ih(const ImVec2& rhs) { x = (short)rhs.x; y = (short)rhs.y; }
+};
+
+// Helper: ImRect (2D axis aligned bounding-box)
+// NB: we can't rely on ImVec2 math operators being available here!
+struct IMGUI_API ImRect
+{
+ ImVec2 Min; // Upper-left
+ ImVec2 Max; // Lower-right
+
+ ImRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {}
+ ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {}
+ ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {}
+ ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {}
+
+ ImVec2 GetCenter() const { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); }
+ ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); }
+ float GetWidth() const { return Max.x - Min.x; }
+ float GetHeight() const { return Max.y - Min.y; }
+ ImVec2 GetTL() const { return Min; } // Top-left
+ ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right
+ ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left
+ ImVec2 GetBR() const { return Max; } // Bottom-right
+ bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; }
+ bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; }
+ bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; }
+ void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; }
+ void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }
+ void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; }
+ void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
+ void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }
+ void TranslateX(float dx) { Min.x += dx; Max.x += dx; }
+ void TranslateY(float dy) { Min.y += dy; Max.y += dy; }
+ void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.
+ void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.
+ void Floor() { Min.x = IM_FLOOR(Min.x); Min.y = IM_FLOOR(Min.y); Max.x = IM_FLOOR(Max.x); Max.y = IM_FLOOR(Max.y); }
+ bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
+ ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); }
+};
+
+// Helper: ImBitArray
inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }
inline void ImBitArrayClearBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; }
inline void ImBitArraySetBit(ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; }
@@ -377,7 +461,7 @@
}
}
-// Helper: ImBitArray (wrapper over ImBitArray functions)
+// Helper: ImBitArray class (wrapper over ImBitArray functions)
// Store 1-bit per value. NOT CLEARED by constructor.
template<int BITCOUNT>
struct IMGUI_API ImBitArray
@@ -447,7 +531,7 @@
inline void SetArenaBasePtr(void* base_ptr) { BasePtr = (char*)base_ptr; }
inline void* GetSpanPtrBegin(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (void*)(BasePtr + Offsets[n]); }
inline void* GetSpanPtrEnd(int n) { IM_ASSERT(n >= 0 && n < CHUNKS && CurrSpan == CHUNKS); return (n + 1 < CHUNKS) ? BasePtr + Offsets[n + 1] : (void*)(BasePtr + TotalSize); }
- template<typename T>
+ template<typename T>
inline void GetSpan(int n, ImSpan<T>* span) { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }
};
@@ -500,9 +584,88 @@
};
//-----------------------------------------------------------------------------
-// Misc data structures
+// [SECTION] ImDrawList support
//-----------------------------------------------------------------------------
+// ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value.
+#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 12
+#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX 512
+#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp((int)((IM_PI * 2.0f) / ImAcos(((_RAD) - (_MAXERROR)) / (_RAD))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)
+
+// ImDrawList: You may set this to higher values (e.g. 2 or 3) to increase tessellation of fast rounded corners path.
+#ifndef IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER
+#define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER 1
+#endif
+
+// Data shared between all ImDrawList instances
+// You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
+struct IMGUI_API ImDrawListSharedData
+{
+ ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas
+ ImFont* Font; // Current/default font (optional, for simplified AddText overload)
+ float FontSize; // Current/default font size (optional, for simplified AddText overload)
+ float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo()
+ float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
+ ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
+ ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
+
+ // [Internal] Lookup tables
+ ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas
+ ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead)
+
+ ImDrawListSharedData();
+ void SetCircleSegmentMaxError(float max_error);
+};
+
+struct ImDrawDataBuilder
+{
+ ImVector<ImDrawList*> Layers[2]; // Global layers for: regular, tooltip
+
+ void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); }
+ void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); }
+ IMGUI_API void FlattenIntoSingleLayer();
+};
+
+//-----------------------------------------------------------------------------
+// [SECTION] Widgets support: flags, enums, data structures
+//-----------------------------------------------------------------------------
+
+// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin().
+// This is going to be exposed in imgui.h when stabilized enough.
+enum ImGuiItemFlags_
+{
+ ImGuiItemFlags_None = 0,
+ ImGuiItemFlags_NoTabStop = 1 << 0, // false
+ ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings.
+ ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211
+ ImGuiItemFlags_NoNav = 1 << 3, // false
+ ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false
+ ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window
+ ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
+ ImGuiItemFlags_Default_ = 0
+};
+
+// Storage for LastItem data
+enum ImGuiItemStatusFlags_
+{
+ ImGuiItemStatusFlags_None = 0,
+ ImGuiItemStatusFlags_HoveredRect = 1 << 0,
+ ImGuiItemStatusFlags_HasDisplayRect = 1 << 1,
+ ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
+ ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues.
+ ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state.
+ ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.
+ ImGuiItemStatusFlags_Deactivated = 1 << 6 // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.
+
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ , // [imgui_tests only]
+ ImGuiItemStatusFlags_Openable = 1 << 10, //
+ ImGuiItemStatusFlags_Opened = 1 << 11, //
+ ImGuiItemStatusFlags_Checkable = 1 << 12, //
+ ImGuiItemStatusFlags_Checked = 1 << 13 //
+#endif
+};
+
enum ImGuiButtonFlags_
{
ImGuiButtonFlags_None = 0,
@@ -545,17 +708,6 @@
ImGuiDragFlags_Vertical = 1 << 0
};
-enum ImGuiColumnsFlags_
-{
- // Default: 0
- ImGuiColumnsFlags_None = 0,
- ImGuiColumnsFlags_NoBorder = 1 << 0, // Disable column dividers
- ImGuiColumnsFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers
- ImGuiColumnsFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns
- ImGuiColumnsFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window
- ImGuiColumnsFlags_GrowParentContentsSize= 1 << 4 // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.
-};
-
// Extend ImGuiSelectableFlags_
enum ImGuiSelectableFlagsPrivate_
{
@@ -582,42 +734,6 @@
ImGuiSeparatorFlags_SpanAllColumns = 1 << 2
};
-// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin().
-// This is going to be exposed in imgui.h when stabilized enough.
-enum ImGuiItemFlags_
-{
- ImGuiItemFlags_None = 0,
- ImGuiItemFlags_NoTabStop = 1 << 0, // false
- ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings.
- ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211
- ImGuiItemFlags_NoNav = 1 << 3, // false
- ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false
- ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window
- ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
- ImGuiItemFlags_Default_ = 0
-};
-
-// Storage for LastItem data
-enum ImGuiItemStatusFlags_
-{
- ImGuiItemStatusFlags_None = 0,
- ImGuiItemStatusFlags_HoveredRect = 1 << 0,
- ImGuiItemStatusFlags_HasDisplayRect = 1 << 1,
- ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
- ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues.
- ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state.
- ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.
- ImGuiItemStatusFlags_Deactivated = 1 << 6 // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.
-
-#ifdef IMGUI_ENABLE_TEST_ENGINE
- , // [imgui_tests only]
- ImGuiItemStatusFlags_Openable = 1 << 10, //
- ImGuiItemStatusFlags_Opened = 1 << 11, //
- ImGuiItemStatusFlags_Checkable = 1 << 12, //
- ImGuiItemStatusFlags_Checked = 1 << 13 //
-#endif
-};
-
enum ImGuiTextFlags_
{
ImGuiTextFlags_None = 0,
@@ -731,60 +847,11 @@
ImGuiPopupPositionPolicy_ComboBox
};
-// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
-struct ImVec1
+struct ImGuiDataTypeTempStorage
{
- float x;
- ImVec1() { x = 0.0f; }
- ImVec1(float _x) { x = _x; }
+ ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT
};
-// 2D vector (half-size integer)
-struct ImVec2ih
-{
- short x, y;
- ImVec2ih() { x = y = 0; }
- ImVec2ih(short _x, short _y) { x = _x; y = _y; }
- explicit ImVec2ih(const ImVec2& rhs) { x = (short)rhs.x; y = (short)rhs.y; }
-};
-
-// 2D axis aligned bounding-box
-// NB: we can't rely on ImVec2 math operators being available here
-struct IMGUI_API ImRect
-{
- ImVec2 Min; // Upper-left
- ImVec2 Max; // Lower-right
-
- ImRect() : Min(0.0f, 0.0f), Max(0.0f, 0.0f) {}
- ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {}
- ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {}
- ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {}
-
- ImVec2 GetCenter() const { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); }
- ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); }
- float GetWidth() const { return Max.x - Min.x; }
- float GetHeight() const { return Max.y - Min.y; }
- ImVec2 GetTL() const { return Min; } // Top-left
- ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right
- ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left
- ImVec2 GetBR() const { return Max; } // Bottom-right
- bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; }
- bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; }
- bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; }
- void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; }
- void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }
- void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; }
- void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
- void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }
- void TranslateX(float dx) { Min.x += dx; Max.x += dx; }
- void TranslateY(float dy) { Min.y += dy; Max.y += dy; }
- void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.
- void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.
- void Floor() { Min.x = IM_FLOOR(Min.x); Min.y = IM_FLOOR(Min.y); Max.x = IM_FLOOR(Max.x); Max.y = IM_FLOOR(Max.y); }
- bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
- ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); }
-};
-
// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
struct ImGuiDataTypeInfo
{
@@ -793,6 +860,14 @@
const char* ScanFmt; // Default scanf format for the type
};
+// Extend ImGuiDataType_
+enum ImGuiDataTypePrivate_
+{
+ ImGuiDataType_String = ImGuiDataType_COUNT + 1,
+ ImGuiDataType_Pointer,
+ ImGuiDataType_ID
+};
+
// Stacked color modifier, backup of modified data so we can restore it
struct ImGuiColorMod
{
@@ -872,32 +947,6 @@
void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; }
};
-// Windows data saved in imgui.ini file
-// Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily.
-// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure)
-struct ImGuiWindowSettings
-{
- ImGuiID ID;
- ImVec2ih Pos;
- ImVec2ih Size;
- bool Collapsed;
-
- ImGuiWindowSettings() { ID = 0; Pos = Size = ImVec2ih(0, 0); Collapsed = false; }
- char* GetName() { return (char*)(this + 1); }
-};
-
-struct ImGuiSettingsHandler
-{
- const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']'
- ImGuiID TypeHash; // == ImHashStr(TypeName)
- void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]"
- void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry
- void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf'
- void* UserData;
-
- ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); }
-};
-
// Storage for current popup stack
struct ImGuiPopupData
{
@@ -912,89 +961,6 @@
ImGuiPopupData() { PopupId = 0; Window = SourceWindow = NULL; OpenFrameCount = -1; OpenParentId = 0; }
};
-struct ImGuiColumnData
-{
- float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)
- float OffsetNormBeforeResize;
- ImGuiColumnsFlags Flags; // Not exposed
- ImRect ClipRect;
-
- ImGuiColumnData() { OffsetNorm = OffsetNormBeforeResize = 0.0f; Flags = ImGuiColumnsFlags_None; }
-};
-
-struct ImGuiColumns
-{
- ImGuiID ID;
- ImGuiColumnsFlags Flags;
- bool IsFirstFrame;
- bool IsBeingResized;
- int Current;
- int Count;
- float OffMinX, OffMaxX; // Offsets from HostWorkRect.Min.x
- float LineMinY, LineMaxY;
- float HostCursorPosY; // Backup of CursorPos at the time of BeginColumns()
- float HostCursorMaxPosX; // Backup of CursorMaxPos at the time of BeginColumns()
- ImRect HostClipRect; // Backup of ClipRect at the time of BeginColumns()
- ImRect HostWorkRect; // Backup of WorkRect at the time of BeginColumns()
- ImVector<ImGuiColumnData> Columns;
- ImDrawListSplitter Splitter;
-
- ImGuiColumns() { Clear(); }
- void Clear()
- {
- ID = 0;
- Flags = ImGuiColumnsFlags_None;
- IsFirstFrame = false;
- IsBeingResized = false;
- Current = 0;
- Count = 1;
- OffMinX = OffMaxX = 0.0f;
- LineMinY = LineMaxY = 0.0f;
- HostCursorPosY = 0.0f;
- HostCursorMaxPosX = 0.0f;
- Columns.clear();
- }
-};
-
-// ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value.
-#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN 12
-#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX 512
-#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR) ImClamp((int)((IM_PI * 2.0f) / ImAcos(((_RAD) - (_MAXERROR)) / (_RAD))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)
-
-// ImDrawList: You may set this to higher values (e.g. 2 or 3) to increase tessellation of fast rounded corners path.
-#ifndef IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER
-#define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER 1
-#endif
-
-// Data shared between all ImDrawList instances
-// You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
-struct IMGUI_API ImDrawListSharedData
-{
- ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas
- ImFont* Font; // Current/default font (optional, for simplified AddText overload)
- float FontSize; // Current/default font size (optional, for simplified AddText overload)
- float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo()
- float CircleSegmentMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc
- ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen()
- ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
-
- // [Internal] Lookup tables
- ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas
- ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead)
-
- ImDrawListSharedData();
- void SetCircleSegmentMaxError(float max_error);
-};
-
-struct ImDrawDataBuilder
-{
- ImVector<ImDrawList*> Layers[2]; // Global layers for: regular, tooltip
-
- void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); }
- void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); }
- IMGUI_API void FlattenIntoSingleLayer();
-};
-
struct ImGuiNavMoveResult
{
ImGuiWindow* Window; // Best candidate window
@@ -1018,7 +984,8 @@
ImGuiNextWindowDataFlags_HasCollapsed = 1 << 3,
ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4,
ImGuiNextWindowDataFlags_HasFocus = 1 << 5,
- ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6
+ ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6,
+ ImGuiNextWindowDataFlags_HasScroll = 1 << 7
};
// Storage for SetNexWindow** functions
@@ -1032,6 +999,7 @@
ImVec2 PosPivotVal;
ImVec2 SizeVal;
ImVec2 ContentSizeVal;
+ ImVec2 ScrollVal;
bool CollapsedVal;
ImRect SizeConstraintRect;
ImGuiSizeCallback SizeCallback;
@@ -1062,29 +1030,143 @@
inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()!
};
+struct ImGuiShrinkWidthItem
+{
+ int Index;
+ float Width;
+};
+
+struct ImGuiPtrOrIndex
+{
+ void* Ptr; // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool.
+ int Index; // Usually index in a main pool.
+
+ ImGuiPtrOrIndex(void* ptr) { Ptr = ptr; Index = -1; }
+ ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
+};
+
//-----------------------------------------------------------------------------
-// Tabs
+// [SECTION] Columns support
//-----------------------------------------------------------------------------
-struct ImGuiShrinkWidthItem
+enum ImGuiColumnsFlags_
{
- int Index;
- float Width;
+ // Default: 0
+ ImGuiColumnsFlags_None = 0,
+ ImGuiColumnsFlags_NoBorder = 1 << 0, // Disable column dividers
+ ImGuiColumnsFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers
+ ImGuiColumnsFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns
+ ImGuiColumnsFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window
+ ImGuiColumnsFlags_GrowParentContentsSize= 1 << 4 // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.
};
-struct ImGuiPtrOrIndex
+struct ImGuiColumnData
{
- void* Ptr; // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool.
- int Index; // Usually index in a main pool.
+ float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)
+ float OffsetNormBeforeResize;
+ ImGuiColumnsFlags Flags; // Not exposed
+ ImRect ClipRect;
- ImGuiPtrOrIndex(void* ptr) { Ptr = ptr; Index = -1; }
- ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
+ ImGuiColumnData() { OffsetNorm = OffsetNormBeforeResize = 0.0f; Flags = ImGuiColumnsFlags_None; }
};
+struct ImGuiColumns
+{
+ ImGuiID ID;
+ ImGuiColumnsFlags Flags;
+ bool IsFirstFrame;
+ bool IsBeingResized;
+ int Current;
+ int Count;
+ float OffMinX, OffMaxX; // Offsets from HostWorkRect.Min.x
+ float LineMinY, LineMaxY;
+ float HostCursorPosY; // Backup of CursorPos at the time of BeginColumns()
+ float HostCursorMaxPosX; // Backup of CursorMaxPos at the time of BeginColumns()
+ ImRect HostInitialClipRect; // Backup of ClipRect at the time of BeginColumns()
+ ImRect HostBackupClipRect; // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground()
+ ImRect HostWorkRect; // Backup of WorkRect at the time of BeginColumns()
+ ImVector<ImGuiColumnData> Columns;
+ ImDrawListSplitter Splitter;
+
+ ImGuiColumns() { Clear(); }
+ void Clear()
+ {
+ ID = 0;
+ Flags = ImGuiColumnsFlags_None;
+ IsFirstFrame = false;
+ IsBeingResized = false;
+ Current = 0;
+ Count = 1;
+ OffMinX = OffMaxX = 0.0f;
+ LineMinY = LineMaxY = 0.0f;
+ HostCursorPosY = 0.0f;
+ HostCursorMaxPosX = 0.0f;
+ Columns.clear();
+ }
+};
+
//-----------------------------------------------------------------------------
-// Main Dear ImGui context
+// [SECTION] Multi-select support
//-----------------------------------------------------------------------------
+#ifdef IMGUI_HAS_MULTI_SELECT
+// <this is filled in 'range_select' branch>
+#endif // #ifdef IMGUI_HAS_MULTI_SELECT
+
+//-----------------------------------------------------------------------------
+// [SECTION] Docking support
+//-----------------------------------------------------------------------------
+
+#ifdef IMGUI_HAS_DOCK
+// <this is filled in 'docking' branch>
+#endif // #ifdef IMGUI_HAS_DOCK
+
+//-----------------------------------------------------------------------------
+// [SECTION] Viewport support
+//-----------------------------------------------------------------------------
+
+#ifdef IMGUI_HAS_VIEWPORT
+// <this is filled in 'docking' branch>
+#endif // #ifdef IMGUI_HAS_VIEWPORT
+
+//-----------------------------------------------------------------------------
+// [SECTION] Settings support
+//-----------------------------------------------------------------------------
+
+// Windows data saved in imgui.ini file
+// Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily.
+// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure)
+struct ImGuiWindowSettings
+{
+ ImGuiID ID;
+ ImVec2ih Pos;
+ ImVec2ih Size;
+ bool Collapsed;
+ bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
+
+ ImGuiWindowSettings() { ID = 0; Pos = Size = ImVec2ih(0, 0); Collapsed = WantApply = false; }
+ char* GetName() { return (char*)(this + 1); }
+};
+
+struct ImGuiSettingsHandler
+{
+ const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']'
+ ImGuiID TypeHash; // == ImHashStr(TypeName)
+ void (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Clear all settings data
+ void (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called before reading (in registration order)
+ void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]"
+ void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry
+ void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order)
+ void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf'
+ void* UserData;
+
+ ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); }
+};
+
+//-----------------------------------------------------------------------------
+// [SECTION] ImGuiContext (main imgui context)
+//-----------------------------------------------------------------------------
+
struct ImGuiContext
{
bool Initialized;
@@ -1102,6 +1184,9 @@
bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame()
bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
bool WithinEndChild; // Set within EndChild()
+ bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
+ ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID()
+ void* TestEngine; // Test engine user data
// Windows state
ImVector<ImGuiWindow*> Windows; // Windows, sorted in display order, back to front
@@ -1132,7 +1217,7 @@
bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state.
bool ActiveIdHasBeenEditedThisFrame;
- ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those directional navigation requests (e.g. can activate a button and move away from it)
+ ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs.
ImU64 ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.
ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
@@ -1160,7 +1245,7 @@
// Gamepad/keyboard Navigation
ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow'
ImGuiID NavId; // Focused item for navigation
- ImGuiID NavFocusScopeId;
+ ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set)
ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem()
ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0
ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0
@@ -1168,9 +1253,10 @@
ImGuiID NavJustTabbedId; // Just tabbed to this id.
ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest).
ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest).
+ ImGuiKeyModFlags NavJustMovedToKeyMods;
ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame.
ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard.
- ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring.
+ ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring.
int NavScoringCount; // Metrics for debugging
ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.
int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing
@@ -1181,22 +1267,25 @@
bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest
bool NavInitRequest; // Init request for appearing window to select first item
bool NavInitRequestFromMove;
- ImGuiID NavInitResultId;
- ImRect NavInitResultRectRel;
+ ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called)
+ ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window)
bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items
bool NavMoveRequest; // Move request for this frame
ImGuiNavMoveFlags NavMoveRequestFlags;
ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu)
+ ImGuiKeyModFlags NavMoveRequestKeyMods;
ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request
ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename?
ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow
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)
+ ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around.
+ ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags.
- // Navigation: Windowing (CTRL+TAB, holding Menu button + directional pads to move/resize)
- ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed top-most.
- ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f
- ImGuiWindow* NavWindowingList;
+ // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
+ ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
+ ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.
+ ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents
float NavWindowingTimer;
float NavWindowingHighlightAlpha;
bool NavWindowingToggleLayer;
@@ -1233,6 +1322,7 @@
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
+ ImGuiID DragDropHoldJustPressedId; // Set when holding a payload just made ButtonBehavior() return a press.
ImVector<unsigned char> DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size
unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads
@@ -1263,7 +1353,7 @@
float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage?
int TooltipOverrideCount;
- ImVector<char> PrivateClipboard; // If no custom clipboard handler is defined
+ ImVector<char> ClipboardHandlerData; // If no custom clipboard handler is defined
ImVector<ImGuiID> MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once
// Platform support
@@ -1279,8 +1369,8 @@
ImChunkStream<ImGuiTableSettings> SettingsTables; // ImGuiTable .ini settings entries
// Capture/Logging
- bool LogEnabled;
- ImGuiLogType LogType;
+ bool LogEnabled; // Currently capturing
+ ImGuiLogType LogType; // Capture target
ImFileHandle LogFile; // If != NULL log to stdout/ file
ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
float LogLinePosY;
@@ -1290,7 +1380,7 @@
int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
// Debug Tools
- bool DebugItemPickerActive;
+ bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker())
ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id
// Misc
@@ -1300,19 +1390,22 @@
int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags
int WantCaptureKeyboardNextFrame;
int WantTextInputNextFrame;
- char TempBuffer[1024*3+1]; // Temporary text buffer
+ char TempBuffer[1024 * 3 + 1]; // Temporary text buffer
ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(&DrawListSharedData), ForegroundDrawList(&DrawListSharedData)
{
Initialized = false;
+ FontAtlasOwnedByContext = shared_font_atlas ? false : true;
Font = NULL;
FontSize = FontBaseSize = 0.0f;
- FontAtlasOwnedByContext = shared_font_atlas ? false : true;
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
Time = 0.0f;
FrameCount = 0;
FrameCountEnded = FrameCountRendered = -1;
WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
+ TestEngineHookItems = false;
+ TestEngineHookIdInfo = 0;
+ TestEngine = NULL;
WindowsActiveCount = 0;
CurrentWindow = NULL;
@@ -1337,7 +1430,7 @@
ActiveIdUsingNavDirMask = 0x00;
ActiveIdUsingNavInputMask = 0x00;
ActiveIdUsingKeyInputMask = 0x00;
- ActiveIdClickOffset = ImVec2(-1,-1);
+ ActiveIdClickOffset = ImVec2(-1, -1);
ActiveIdWindow = NULL;
ActiveIdSource = ImGuiInputSource_None;
ActiveIdMouseButton = 0;
@@ -1351,8 +1444,9 @@
NavWindow = NULL;
NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0;
NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;
+ NavJustMovedToKeyMods = ImGuiKeyModFlags_None;
NavInputSource = ImGuiInputSource_None;
- NavScoringRectScreen = ImRect();
+ NavScoringRect = ImRect();
NavScoringCount = 0;
NavLayer = ImGuiNavLayer_Main;
NavIdTabCounter = INT_MAX;
@@ -1368,9 +1462,12 @@
NavMoveRequest = false;
NavMoveRequestFlags = ImGuiNavMoveFlags_None;
NavMoveRequestForward = ImGuiNavForward_None;
+ NavMoveRequestKeyMods = ImGuiKeyModFlags_None;
NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None;
+ NavWrapRequestWindow = NULL;
+ NavWrapRequestFlags = ImGuiNavMoveFlags_None;
- NavWindowingTarget = NavWindowingTargetAnim = NavWindowingList = NULL;
+ NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false;
@@ -1393,6 +1490,7 @@
DragDropAcceptIdCurrRectSurface = 0.0f;
DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
DragDropAcceptFrameCount = -1;
+ DragDropHoldJustPressedId = 0;
memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
CurrentTable = NULL;
@@ -1434,7 +1532,7 @@
};
//-----------------------------------------------------------------------------
-// ImGuiWindow
+// [SECTION] ImGuiWindowTempData, ImGuiWindow
//-----------------------------------------------------------------------------
// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow.
@@ -1544,7 +1642,7 @@
ImVec2 ContentSize; // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding.
ImVec2 ContentSizeExplicit; // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize().
ImVec2 WindowPadding; // Window padding at the time of Begin().
- float WindowRounding; // Window rounding at the time of Begin().
+ float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.
float WindowBorderSize; // Window border size at the time of Begin().
int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)!
ImGuiID MoveId; // == window->GetID("#MOVE")
@@ -1553,7 +1651,7 @@
ImVec2 ScrollMax;
ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)
ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
- ImVec2 ScrollbarSizes; // Size taken by scrollbars on each axis
+ ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.
bool ScrollbarX, ScrollbarY; // Are scrollbars visible?
bool Active; // Set to true on Begin(), unless Collapsed
bool WasActive;
@@ -1562,7 +1660,7 @@
bool WantCollapseToggle;
bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed)
bool Appearing; // Set during the frame where the window is appearing (or re-appearing)
- bool Hidden; // Do not display (== (HiddenFrames*** > 0))
+ bool Hidden; // Do not display (== HiddenFrames*** > 0)
bool IsFallbackWindow; // Set on the "Debug##Default" window.
bool HasCloseButton; // Set when the window has a close button (p_open != NULL)
signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3)
@@ -1580,7 +1678,7 @@
ImGuiCond SetWindowSizeAllowFlags; // store acceptable condition flags for SetNextWindowSize() use.
ImGuiCond SetWindowCollapsedAllowFlags; // store acceptable condition flags for SetNextWindowCollapsed() use.
ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size)
- ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right.
+ ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right.
ImVector<ImGuiID> IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure)
ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name.
@@ -1613,8 +1711,8 @@
ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1)
ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space
- bool MemoryCompacted;
- int MemoryDrawListIdxCapacity;
+ bool MemoryCompacted; // Set when window extraneous data have been garbage collected
+ int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy
int MemoryDrawListVtxCapacity;
public:
@@ -1630,12 +1728,12 @@
ImGuiID GetIDFromRectangle(const ImRect& r_abs);
// We don't use g.FontSize because the window may be != g.CurrentWidow.
- ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); }
- float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }
- float TitleBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; }
- ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
- float MenuBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; }
- ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }
+ ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
+ float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }
+ float TitleBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; }
+ ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
+ float MenuBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; }
+ ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }
};
// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data.
@@ -1652,7 +1750,7 @@
};
//-----------------------------------------------------------------------------
-// Tab bar, tab item
+// [SECTION] Tab bar, Tab item support
//-----------------------------------------------------------------------------
// Extend ImGuiTabBarFlags_
@@ -1722,165 +1820,17 @@
};
//-----------------------------------------------------------------------------
-// Internal API
-// No guarantee of forward compatibility here.
+// [SECTION] Table support
//-----------------------------------------------------------------------------
-namespace ImGui
-{
- // Windows
- // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window)
- // If this ever crash because g.CurrentWindow is NULL it means that either
- // - ImGui::NewFrame() has never been called, which is illegal.
- // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
- inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
- inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
- IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id);
- IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
- IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
- IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window);
- IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
- IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
- IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window);
- IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);
- IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0);
- IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0);
+#ifdef IMGUI_HAS_TABLE
- // Windows: Display Order and Focus Order
- IMGUI_API void FocusWindow(ImGuiWindow* window);
- IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window);
- IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window);
- IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window);
- IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window);
-
- // Fonts, drawing
- IMGUI_API void SetCurrentFont(ImFont* font);
- inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
- inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); ImGuiContext& g = *GImGui; return &g.ForegroundDrawList; } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
-
- // Init
- IMGUI_API void Initialize(ImGuiContext* context);
- IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext().
-
- // NewFrame
- IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
- IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
- IMGUI_API void UpdateMouseMovingWindowNewFrame();
- IMGUI_API void UpdateMouseMovingWindowEndFrame();
-
- // Settings
- IMGUI_API void MarkIniSettingsDirty();
- IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window);
- IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name);
- IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id);
- IMGUI_API ImGuiWindowSettings* FindOrCreateWindowSettings(const char* name);
- IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);
-
- // Scrolling
- IMGUI_API void SetScrollX(ImGuiWindow* window, float new_scroll_x);
- IMGUI_API void SetScrollY(ImGuiWindow* window, float new_scroll_y);
- IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio = 0.5f);
- IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio = 0.5f);
- IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect);
-
- // Basic Accessors
- inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; }
- inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; }
- inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; }
- inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; }
- IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window);
- IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window);
- IMGUI_API void ClearActiveID();
- IMGUI_API ImGuiID GetHoveredID();
- IMGUI_API void SetHoveredID(ImGuiID id);
- IMGUI_API void KeepAliveID(ImGuiID id);
- IMGUI_API void MarkItemEdited(ImGuiID id); // Mark data associated to given item as "edited", used by IsItemDeactivatedAfterEdit() function.
- IMGUI_API void PushOverrideID(ImGuiID id); // Push given value at the top of the ID stack (whereas PushID combines old and new hashes)
-
- // Basic Helpers for widget code
- IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
- IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f);
- IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL);
- IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id);
- IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged);
- IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested
- IMGUI_API void FocusableItemUnregister(ImGuiWindow* window);
- IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h);
- IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
- IMGUI_API void PushMultiItemsWidths(int components, float width_full);
- IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled);
- IMGUI_API void PopItemFlag();
- IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly)
- IMGUI_API ImVec2 GetContentRegionMaxAbs();
- IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);
-
- // Logging/Capture
- IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
- IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer
-
- // Popups, Modals, Tooltips
- IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
- IMGUI_API void OpenPopupEx(ImGuiID id);
- IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
- IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
- IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack!
- IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
- IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags);
- IMGUI_API ImGuiWindow* GetTopMostPopupModal();
- IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window);
- IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default);
-
- // Navigation
- IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit);
- IMGUI_API bool NavMoveRequestButNoResultYet();
- IMGUI_API void NavMoveRequestCancel();
- IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags);
- IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
- IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode);
- IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f);
- IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
- IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again.
- IMGUI_API void SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id);
- IMGUI_API void SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
-
- // Focus scope (WIP)
- IMGUI_API void PushFocusScope(ImGuiID id); // Note: this is storing in same stack as IDStack, so Push/Pop mismatch will be reported there.
- IMGUI_API void PopFocusScope();
- inline ImGuiID GetFocusScopeID() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; }
-
- // Inputs
- // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
- inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
- inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; }
- inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; }
- IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
- inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { ImGuiContext& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; }
- inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
- inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); }
-
- // Drag and Drop
- IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
- IMGUI_API void ClearDragDrop();
- IMGUI_API bool IsDragDropPayloadBeingAccepted();
-
- // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables 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().
- IMGUI_API void EndColumns(); // close columns
- IMGUI_API void PushColumnClipRect(int column_index);
- IMGUI_API void PushColumnsBackground();
- IMGUI_API void PopColumnsBackground();
- IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count);
- IMGUI_API ImGuiColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id);
- IMGUI_API float GetColumnOffsetFromNorm(const ImGuiColumns* columns, float offset_norm);
- IMGUI_API float GetColumnNormFromOffset(const ImGuiColumns* columns, float offset);
-}
-
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code
#define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64.
#define IMGUI_TABLE_MAX_DRAW_CHANNELS (2 + 64 * 2) // See TableUpdateDrawChannels()
// [Internal] sizeof() ~ 100
-// We use the terminology "Active" to refer to a column that is not Hidden by user or programmatically. We don't use the term "Visible" because it is ambiguous since an Active column can be non-visible because of scrolling.
+// We use the terminology "Visible" to refer to a column that is not Hidden by user or settings. However it may still be out of view and clipped (see IsClipped).
struct ImGuiTableColumn
{
ImRect ClipRect; // Clipping rectangle for the column
@@ -1889,31 +1839,31 @@
ImGuiTableColumnFlags Flags; // Effective flags. See ImGuiTableColumnFlags_
float MinX; // Absolute positions
float MaxX;
- float ResizeWeight; // ~1.0f. Master width data when (Flags & _WidthStretch)
- float WidthRequested; // Master width data when !(Flags & _WidthStretch)
- float WidthGiven; // == (MaxX - MinX). FIXME-TABLE: Store all persistent width in multiple of FontSize?
+ float WidthStretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.
+ float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from WidthStretchWeight in TableUpdateLayout()
+ float WidthGiven; // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be >WidthRequest to honor minimum width, may be <WidthRequest to honor shrinking columns down in tight space.
float StartXRows; // Start position for the frame, currently ~(MinX + CellPaddingX)
- float StartXHeaders;
+ float StartXHeaders;
float ContentMaxPosRowsFrozen; // Submitted contents absolute maximum position, from which we can infer width.
float ContentMaxPosRowsUnfrozen; // (kept as float because we need to manipulate those between each cell change)
float ContentMaxPosHeadersUsed;
- float ContentMaxPosHeadersDesired;
+ float ContentMaxPosHeadersIdeal;
ImS16 ContentWidthRowsFrozen; // Contents width. Because row freezing is not correlated with headers/not-headers we need all 4 variants (ImDrawCmd merging uses different data than alignment code).
ImS16 ContentWidthRowsUnfrozen; // (encoded as ImS16 because we actually rarely use those width)
ImS16 ContentWidthHeadersUsed; // TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls
- ImS16 ContentWidthHeadersDesired;
+ ImS16 ContentWidthHeadersIdeal;
ImS16 NameOffset; // Offset into parent ColumnsNames[]
- bool IsActive; // Is the column not marked Hidden by the user (regardless of clipping). We're not calling this "Visible" here because visibility also depends on clipping.
- bool IsActiveNextFrame;
- bool IsClipped; // Set when not overlapping the host window clipping rectangle. We don't use the opposite "!Visible" name because Clipped can be altered by events.
+ bool IsVisible; // Is the column not marked Hidden by the user? (could be clipped by scrolling, etc).
+ bool IsVisibleNextFrame;
+ bool IsClipped; // Set when not overlapping the host window clipping rectangle.
bool SkipItems;
ImS8 DisplayOrder; // Index within Table's IndexToDisplayOrder[] (column may be reordered by users)
- ImS8 IndexWithinActiveSet; // Index within active/visible set (<= IndexToDisplayOrder)
+ ImS8 IndexWithinVisibleSet; // Index within visible set (<= IndexToDisplayOrder)
ImS8 DrawChannelCurrent; // Index within DrawSplitter.Channels[]
ImS8 DrawChannelRowsBeforeFreeze;
ImS8 DrawChannelRowsAfterFreeze;
- ImS8 PrevActiveColumn; // Index of prev active column within Columns[], -1 if first active column
- ImS8 NextActiveColumn; // Index of next active column within Columns[], -1 if last active column
+ ImS8 PrevVisibleColumn; // Index of prev visible column within Columns[], -1 if first visible column
+ ImS8 NextVisibleColumn; // Index of next visible column within Columns[], -1 if last visible column
ImS8 AutoFitQueue; // Queue of 8 values for the next 8 frames to request auto-fit
ImS8 CannotSkipItemsQueue; // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem
ImS8 SortOrder; // -1: Not sorting on this column
@@ -1922,15 +1872,15 @@
ImGuiTableColumn()
{
memset(this, 0, sizeof(*this));
- ResizeWeight = WidthRequested = WidthGiven = -1.0f;
+ WidthStretchWeight = WidthRequest = WidthGiven = -1.0f;
NameOffset = -1;
- IsActive = IsActiveNextFrame = true;
- DisplayOrder = IndexWithinActiveSet = -1;
+ IsVisible = IsVisibleNextFrame = true;
+ DisplayOrder = IndexWithinVisibleSet = -1;
DrawChannelCurrent = DrawChannelRowsBeforeFreeze = DrawChannelRowsAfterFreeze = -1;
- PrevActiveColumn = NextActiveColumn = -1;
+ PrevVisibleColumn = NextVisibleColumn = -1;
AutoFitQueue = CannotSkipItemsQueue = (1 << 3) - 1; // Skip for three frames
SortOrder = -1;
- SortDirection = ImGuiSortDirection_Ascending;
+ SortDirection = ImGuiSortDirection_None;
}
};
@@ -1941,17 +1891,17 @@
ImVector<char> RawData;
ImSpan<ImGuiTableColumn> Columns; // Point within RawData[]
ImSpan<ImS8> DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
- ImU64 ActiveMaskByIndex; // Column Index -> IsActive map (Active == not hidden by user/api) in a format adequate for iterating column without touching cold data
- ImU64 ActiveMaskByDisplayOrder; // Column DisplayOrder -> IsActive map
- ImU64 VisibleMaskByIndex; // Visible (== Active and not Clipped)
- ImGuiTableFlags SettingsSaveFlags; // Pre-compute which data we are going to save into the .ini file (e.g. when order is not altered we won't save order)
+ ImU64 VisibleMaskByIndex; // Column Index -> IsVisible map (== not hidden by user/api) in a format adequate for iterating column without touching cold data
+ ImU64 VisibleMaskByDisplayOrder; // Column DisplayOrder -> IsVisible map
+ ImU64 VisibleUnclippedMaskByIndex;// Visible and not Clipped, aka "actually visible" "not hidden by some scrolling"
+ ImGuiTableFlags SettingsLoadedFlags; // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)
int SettingsOffset; // Offset in g.SettingsTables
int LastFrameActive;
int ColumnsCount; // Number of columns declared in BeginTable()
- int ColumnsActiveCount; // Number of non-hidden columns (<= ColumnsCount)
+ int ColumnsVisibleCount; // Number of non-hidden columns (<= ColumnsCount)
int CurrentColumn;
int CurrentRow;
- ImS16 InstanceNo; // Count of BeginTable() calls with same ID in the same frame (generally 0)
+ ImS16 InstanceCurrent; // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple table with same ID look are multiple tables, they are just synched.
ImS16 InstanceInteracted; // Mark which instance (generally 0) of the same ID is being interacted with
float RowPosY1;
float RowPosY2;
@@ -1973,9 +1923,11 @@
float CellSpacingX; // Spacing between non-bordered cells
float LastOuterHeight; // Outer height from last frame
float LastFirstRowHeight; // Height of first row from last frame
- float ColumnsTotalWidth;
- float InnerWidth;
+ float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details.
+ float ColumnsTotalWidth; // Sum of current column width
+ float ColumnsAutoFitWidth; // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window
float ResizedColumnNextWidth;
+ float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
ImRect OuterRect; // Note: OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
ImRect WorkRect;
ImRect InnerClipRect;
@@ -1982,6 +1934,7 @@
ImRect BackgroundClipRect; // We use this to cpu-clip cell background color fill
ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
ImRect HostWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable()
+ ImRect HostBackupClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground()
ImVec2 HostCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
ImGuiWindow* OuterWindow; // Parent window for the table
ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window)
@@ -1991,14 +1944,14 @@
ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs()
ImS8 SortSpecsCount;
ImS8 DeclColumnsCount; // Count calls to TableSetupColumn()
- ImS8 HoveredColumnBody; // [DEBUG] Unlike HoveredColumnBorder this doesn't fulfill all Hovering rules properly. Used for debugging/tools for now.
+ ImS8 HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!
ImS8 HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing).
- ImS8 ResizedColumn; // Index of column being resized. Reset by InstanceNo==0.
+ ImS8 ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0.
ImS8 LastResizedColumn; // Index of column being resized from previous frame.
- ImS8 HeldHeaderColumn; // Index of column header being held.
+ ImS8 HeldHeaderColumn; // Index of column header being held.
ImS8 ReorderColumn; // Index of column being reordered. (not cleared)
ImS8 ReorderColumnDir; // -1 or +1
- ImS8 RightMostActiveColumn; // Index of right-most non-hidden column.
+ ImS8 RightMostVisibleColumn; // Index of right-most non-hidden column.
ImS8 LeftMostStretchedColumnDisplayOrder; // Display order of left-most stretched column.
ImS8 ContextPopupColumn; // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot
ImS8 DummyDrawChannel; // Redirect non-visible columns here.
@@ -2010,10 +1963,10 @@
bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow().
bool IsInitializing;
bool IsSortSpecsDirty;
+ bool IsSortSpecsChangedForUser; // Reported to end-user via TableGetSortSpecs()->SpecsChanged and then clear.
bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag.
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
bool IsSettingsRequestLoad;
- bool IsSettingsLoaded;
bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.
bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
bool IsResetDisplayOrderRequest;
@@ -2029,6 +1982,8 @@
LastResizedColumn = -1;
ContextPopupColumn = -1;
ReorderColumn = -1;
+ ResizedColumn = -1;
+ HoveredColumnBody = HoveredColumnBorder = -1;
}
};
@@ -2040,8 +1995,9 @@
ImS8 Index;
ImS8 DisplayOrder;
ImS8 SortOrder;
- ImS8 SortDirection : 7;
- ImU8 Visible : 1; // This is called Active in ImGuiTableColumn, in .ini file we call it Visible.
+ ImU8 SortDirection : 2;
+ ImU8 IsVisible : 1;
+ ImU8 IsWeighted : 1;
ImGuiTableColumnSettings()
{
@@ -2050,7 +2006,8 @@
Index = -1;
DisplayOrder = SortOrder = -1;
SortDirection = ImGuiSortDirection_None;
- Visible = 1;
+ IsVisible = 1;
+ IsWeighted = 0;
}
};
@@ -2058,17 +2015,179 @@
struct ImGuiTableSettings
{
ImGuiID ID; // Set to 0 to invalidate/delete the setting
- ImGuiTableFlags SaveFlags;
+ ImGuiTableFlags SaveFlags; // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..)
+ float RefScale; // Reference scale to be able to rescale columns on font/dpi changes.
ImS8 ColumnsCount;
- ImS8 ColumnsCountMax;
+ ImS8 ColumnsCountMax; // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher
+ bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
ImGuiTableSettings() { memset(this, 0, sizeof(*this)); }
ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); }
};
+#endif // #ifdef IMGUI_HAS_TABLE
+
+//-----------------------------------------------------------------------------
+// [SECTION] Internal API
+// No guarantee of forward compatibility here!
+//-----------------------------------------------------------------------------
+
namespace ImGui
{
- // [Internal]
+ // Windows
+ // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window)
+ // If this ever crash because g.CurrentWindow is NULL it means that either
+ // - ImGui::NewFrame() has never been called, which is illegal.
+ // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
+ inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
+ inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
+ IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id);
+ IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
+ IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
+ IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window);
+ IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
+ IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
+ IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window);
+ IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);
+ IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0);
+ IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0);
+
+ // Windows: Display Order and Focus Order
+ IMGUI_API void FocusWindow(ImGuiWindow* window);
+ IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window);
+ IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window);
+ IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window);
+ IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window);
+
+ // Fonts, drawing
+ IMGUI_API void SetCurrentFont(ImFont* font);
+ inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }
+ inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); ImGuiContext& g = *GImGui; return &g.ForegroundDrawList; } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
+
+ // Init
+ IMGUI_API void Initialize(ImGuiContext* context);
+ IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext().
+
+ // NewFrame
+ IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
+ IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
+ IMGUI_API void UpdateMouseMovingWindowNewFrame();
+ IMGUI_API void UpdateMouseMovingWindowEndFrame();
+
+ // Settings
+ IMGUI_API void MarkIniSettingsDirty();
+ IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window);
+ IMGUI_API void ClearIniSettings();
+ IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name);
+ IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id);
+ IMGUI_API ImGuiWindowSettings* FindOrCreateWindowSettings(const char* name);
+ IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);
+
+ // Scrolling
+ IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // Use -1.0f on one axis to leave as-is
+ IMGUI_API void SetScrollX(ImGuiWindow* window, float new_scroll_x);
+ IMGUI_API void SetScrollY(ImGuiWindow* window, float new_scroll_y);
+ IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio = 0.5f);
+ IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio = 0.5f);
+ IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect);
+
+ // Basic Accessors
+ inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand)
+ inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; }
+ inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; }
+ inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; }
+ IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window);
+ IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window);
+ IMGUI_API void ClearActiveID();
+ IMGUI_API ImGuiID GetHoveredID();
+ IMGUI_API void SetHoveredID(ImGuiID id);
+ IMGUI_API void KeepAliveID(ImGuiID id);
+ IMGUI_API void MarkItemEdited(ImGuiID id); // Mark data associated to given item as "edited", used by IsItemDeactivatedAfterEdit() function.
+ IMGUI_API void PushOverrideID(ImGuiID id); // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes)
+
+ // Basic Helpers for widget code
+ IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
+ IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f);
+ IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL);
+ IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id);
+ IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged);
+ IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested
+ IMGUI_API void FocusableItemUnregister(ImGuiWindow* window);
+ IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h);
+ IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
+ IMGUI_API void PushMultiItemsWidths(int components, float width_full);
+ IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled);
+ IMGUI_API void PopItemFlag();
+ IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly)
+ IMGUI_API ImVec2 GetContentRegionMaxAbs();
+ IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);
+
+ // Logging/Capture
+ IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
+ IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer
+
+ // Popups, Modals, Tooltips
+ IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
+ IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None);
+ IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
+ IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
+ IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags);
+ IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
+ IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags);
+ IMGUI_API ImGuiWindow* GetTopMostPopupModal();
+ IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window);
+ IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default);
+
+ // Gamepad/Keyboard Navigation
+ IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit);
+ IMGUI_API bool NavMoveRequestButNoResultYet();
+ IMGUI_API void NavMoveRequestCancel();
+ IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags);
+ IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
+ IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode);
+ IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f);
+ IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
+ IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again.
+ IMGUI_API void SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id);
+ IMGUI_API void SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
+
+ // Focus Scope (WIP)
+ // This is generally used to identify a selection set (multiple of which may be in the same window), as selection
+ // patterns generally need to react (e.g. clear selection) when landing on an item of the set.
+ IMGUI_API void PushFocusScope(ImGuiID id);
+ IMGUI_API void PopFocusScope();
+ inline ImGuiID GetFocusScopeID() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; }
+
+ // Inputs
+ // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
+ inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
+ inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; }
+ inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; }
+ IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
+ inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { ImGuiContext& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; }
+ inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
+ inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); }
+ IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags();
+
+ // Drag and Drop
+ IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
+ IMGUI_API void ClearDragDrop();
+ IMGUI_API bool IsDragDropPayloadBeingAccepted();
+
+ // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)
+ IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect);
+ 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().
+ IMGUI_API void EndColumns(); // close columns
+ IMGUI_API void PushColumnClipRect(int column_index);
+ IMGUI_API void PushColumnsBackground();
+ IMGUI_API void PopColumnsBackground();
+ IMGUI_API ImGuiID GetColumnsID(const char* str_id, int count);
+ IMGUI_API ImGuiColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id);
+ IMGUI_API float GetColumnOffsetFromNorm(const ImGuiColumns* columns, float offset_norm);
+ IMGUI_API float GetColumnNormFromOffset(const ImGuiColumns* columns, float offset);
+
+ // Tables
+ IMGUI_API ImGuiTable* FindTableByID(ImGuiID id);
IMGUI_API bool BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
IMGUI_API void TableBeginUpdateColumns(ImGuiTable* table);
IMGUI_API void TableUpdateDrawChannels(ImGuiTable* table);
@@ -2077,26 +2196,32 @@
IMGUI_API void TableSetColumnWidth(int column_n, float width);
IMGUI_API void TableSetColumnWidth(ImGuiTable* table, ImGuiTableColumn* column, float width);
IMGUI_API void TableDrawBorders(ImGuiTable* table);
- IMGUI_API void TableDrawMergeChannels(ImGuiTable* table);
- IMGUI_API void TableDrawContextMenu(ImGuiTable* table, int column_n);
+ IMGUI_API void TableDrawContextMenu(ImGuiTable* table);
+ IMGUI_API void TableOpenContextMenu(ImGuiTable* table, int column_n);
+ IMGUI_API void TableReorderDrawChannelsForMerge(ImGuiTable* table);
IMGUI_API void TableSortSpecsClickColumn(ImGuiTable* table, ImGuiTableColumn* column, bool add_to_existing_sort_orders);
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
+ IMGUI_API void TableSortSpecsBuild(ImGuiTable* table);
IMGUI_API void TableBeginRow(ImGuiTable* table);
IMGUI_API void TableEndRow(ImGuiTable* table);
IMGUI_API void TableBeginCell(ImGuiTable* table, int column_n);
IMGUI_API void TableEndCell(ImGuiTable* table);
IMGUI_API ImRect TableGetCellRect();
- IMGUI_API const char* TableGetColumnName(ImGuiTable* table, int column_n);
+ IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n);
+ IMGUI_API ImGuiID TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no = 0);
IMGUI_API void TableSetColumnAutofit(ImGuiTable* table, int column_n);
IMGUI_API void PushTableBackground();
IMGUI_API void PopTableBackground();
- IMGUI_API void TableLoadSettings(ImGuiTable* table);
- IMGUI_API void TableSaveSettings(ImGuiTable* table);
- IMGUI_API ImGuiTableSettings* TableFindSettings(ImGuiTable* table);
- IMGUI_API void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
- IMGUI_API void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
- IMGUI_API void TableSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
+ // Tables - Settings
+ IMGUI_API void TableLoadSettings(ImGuiTable* table);
+ IMGUI_API void TableSaveSettings(ImGuiTable* table);
+ IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
+ IMGUI_API void TableSettingsInstallHandler(ImGuiContext* context);
+ IMGUI_API ImGuiTableSettings* TableSettingsCreate(ImGuiID id, int columns_count);
+ IMGUI_API ImGuiTableSettings* TableSettingsFindByID(ImGuiID id);
+ IMGUI_API void TableSettingsClearByID(ImGuiID id);
+
// Tab Bars
IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);
IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);
@@ -2106,7 +2231,7 @@
IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags);
IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button);
IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col);
- IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id);
+ IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible);
// Render helpers
// AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.
@@ -2113,7 +2238,7 @@
// NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)
IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
- IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL);
+ IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
@@ -2139,12 +2264,14 @@
// Widgets
IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
- IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0);
+ IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos);
IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos);
IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
IMGUI_API void Scrollbar(ImGuiAxis axis);
IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawCornerFlags rounding_corners);
+ IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col);
+ IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);
IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);
IMGUI_API ImGuiID GetWindowResizeID(ImGuiWindow* window, int n); // 0..3: corners, 4..7: borders
IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags);
@@ -2171,11 +2298,12 @@
IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format);
IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format);
+ IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);
// InputText
IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags);
- IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format);
+ IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL);
inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); }
inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active
@@ -2185,7 +2313,7 @@
IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags);
// Plot
- IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size);
+ IMGUI_API int PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size);
// Shade functions (write over already created vertices)
IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);
@@ -2210,33 +2338,30 @@
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
-// Debug Tools
-// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item.
-#ifndef IM_DEBUG_BREAK
-#if defined(__clang__)
-#define IM_DEBUG_BREAK() __builtin_debugtrap()
-#elif defined (_MSC_VER)
-#define IM_DEBUG_BREAK() __debugbreak()
-#else
-#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!
-#endif
-#endif // #ifndef IM_DEBUG_BREAK
+//-----------------------------------------------------------------------------
+// [SECTION] Test Engine Hooks (imgui_test_engine)
+//-----------------------------------------------------------------------------
-// Test Engine Hooks (imgui_tests)
-//#define IMGUI_ENABLE_TEST_ENGINE
#ifdef IMGUI_ENABLE_TEST_ENGINE
+extern void ImGuiTestEngineHook_Shutdown(ImGuiContext* ctx);
extern void ImGuiTestEngineHook_PreNewFrame(ImGuiContext* ctx);
extern void ImGuiTestEngineHook_PostNewFrame(ImGuiContext* ctx);
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id);
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);
+extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id);
+extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end);
extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);
-#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box
-#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional)
-#define IMGUI_TEST_ENGINE_LOG(_FMT, ...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
+#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box
+#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional)
+#define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
+#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA));
+#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2));
#else
-#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) do { } while (0)
-#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) do { } while (0)
-#define IMGUI_TEST_ENGINE_LOG(_FMT, ...) do { } while (0)
+#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) do { } while (0)
+#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) do { } while (0)
+#define IMGUI_TEST_ENGINE_LOG(_FMT,...) do { } while (0)
+#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) do { } while (0)
+#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) do { } while (0)
#endif
#if defined(__clang__)
--- a/DoConfig/imgui/imgui_widgets.cpp
+++ b/DoConfig/imgui/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.76 WIP
+// dear imgui, v1.78 WIP
// (widgets code)
/*
@@ -59,19 +59,19 @@
// Clang/GCC warnings with -Weverything
#if defined(__clang__)
-#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
-#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
-#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
-#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
-#if __has_warning("-Wzero-as-null-pointer-constant")
-#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
+#if __has_warning("-Wunknown-warning-option")
+#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
#endif
-#if __has_warning("-Wdouble-promotion")
-#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
-#endif
-#if __has_warning("-Wdeprecated-enum-enum-conversion")
-#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
-#endif
+#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
+#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
+#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
+#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
+#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
+#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
+#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
+#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
+#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
@@ -161,7 +161,7 @@
// - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop.
const char* line = text;
const float line_height = GetTextLineHeight();
- ImVec2 text_size(0,0);
+ ImVec2 text_size(0, 0);
// Lines to skip (can't skip when logging text)
ImVec2 pos = text_pos;
@@ -333,8 +333,8 @@
const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
- const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
- const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size);
+ const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2));
+ const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y * 2) + label_size);
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, 0))
return;
@@ -342,7 +342,7 @@
// Render
const char* value_text_begin = &g.TempBuffer[0];
const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
- RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f));
+ RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f, 0.5f));
if (label_size.x > 0.0f)
RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
}
@@ -378,7 +378,7 @@
// Render
ImU32 text_col = GetColorU32(ImGuiCol_Text);
- RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, g.FontSize*0.5f), text_col);
+ RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col);
RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false);
}
@@ -392,8 +392,10 @@
// - ArrowButton()
// - CloseButton() [Internal]
// - CollapseButton() [Internal]
-// - ScrollbarEx() [Internal]
+// - GetWindowScrollbarID() [Internal]
+// - GetWindowScrollbarRect() [Internal]
// - Scrollbar() [Internal]
+// - ScrollbarEx() [Internal]
// - Image()
// - ImageButton()
// - Checkbox()
@@ -485,7 +487,7 @@
#ifdef IMGUI_ENABLE_TEST_ENGINE
if (id != 0 && window->DC.LastItemId != id)
- ImGuiTestEngineHook_ItemAdd(&g, bb, id);
+ IMGUI_TEST_ENGINE_ITEM_ADD(bb, id);
#endif
bool pressed = false;
@@ -499,11 +501,13 @@
if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
{
+ const float DRAG_DROP_HOLD_TIMER = 0.70f;
hovered = true;
SetHoveredID(id);
- if (CalcTypematicRepeatAmount(g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, g.HoveredIdTimer + 0.0001f, 0.70f, 0.00f))
+ if (CalcTypematicRepeatAmount(g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, g.HoveredIdTimer + 0.0001f, DRAG_DROP_HOLD_TIMER, 0.00f))
{
pressed = true;
+ g.DragDropHoldJustPressedId = id;
FocusWindow(window);
}
}
@@ -554,7 +558,8 @@
if ((flags & ImGuiButtonFlags_PressedOnRelease) && mouse_button_released != -1)
{
// Repeat mode trumps on release behavior
- if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay))
+ const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay;
+ if (!has_repeated_at_least_once)
pressed = true;
ClearActiveID();
}
@@ -777,8 +782,8 @@
float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
ImU32 cross_col = GetColorU32(ImGuiCol_Text);
center -= ImVec2(0.5f, 0.5f);
- window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f);
- window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f);
+ window->DrawList->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f);
+ window->DrawList->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f);
return pressed;
}
@@ -813,6 +818,49 @@
return window->GetIDNoKeepAlive(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY");
}
+// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set.
+ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
+{
+ const ImRect outer_rect = window->Rect();
+ const ImRect inner_rect = window->InnerRect;
+ const float border_size = window->WindowBorderSize;
+ const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
+ IM_ASSERT(scrollbar_size > 0.0f);
+ if (axis == ImGuiAxis_X)
+ return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x, outer_rect.Max.y);
+ else
+ return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x, inner_rect.Max.y);
+}
+
+void ImGui::Scrollbar(ImGuiAxis axis)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ const ImGuiID id = GetWindowScrollbarID(window, axis);
+ KeepAliveID(id);
+
+ // Calculate scrollbar bounding box
+ ImRect bb = GetWindowScrollbarRect(window, axis);
+ ImDrawCornerFlags rounding_corners = 0;
+ if (axis == ImGuiAxis_X)
+ {
+ rounding_corners |= ImDrawCornerFlags_BotLeft;
+ if (!window->ScrollbarY)
+ rounding_corners |= ImDrawCornerFlags_BotRight;
+ }
+ else
+ {
+ if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar))
+ rounding_corners |= ImDrawCornerFlags_TopRight;
+ if (!window->ScrollbarX)
+ rounding_corners |= ImDrawCornerFlags_BotRight;
+ }
+ float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];
+ float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;
+ ScrollbarEx(bb, id, axis, &window->Scroll[axis], size_avail, size_contents, rounding_corners);
+}
+
// Vertical/Horizontal scrollbar
// The entire piece of code below is rather confusing because:
// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
@@ -831,7 +879,7 @@
if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)
return false;
- // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the resize grab)
+ // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab)
float alpha = 1.0f;
if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f)
alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f));
@@ -840,13 +888,12 @@
const ImGuiStyle& style = g.Style;
const bool allow_interaction = (alpha >= 1.0f);
- const bool horizontal = (axis == ImGuiAxis_X);
ImRect bb = bb_frame;
bb.Expand(ImVec2(-ImClamp(IM_FLOOR((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_FLOOR((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
- const float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight();
+ const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
// Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
// But we maintain a minimum size in pixel to allow for the user to still aim inside.
@@ -862,11 +909,11 @@
float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v);
float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max);
- float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
+ float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space
if (held && allow_interaction && grab_h_norm < 1.0f)
{
- float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y;
- float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y;
+ float scrollbar_pos_v = bb.Min[axis];
+ float mouse_pos_v = g.IO.MousePos[axis];
// Click position in scrollbar normalized space (0.0f->1.0f)
const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
@@ -883,7 +930,7 @@
g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
}
- // Apply scroll
+ // Apply scroll (p_scroll_v will generally point on one member of window->Scroll)
// It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position
const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm));
*p_scroll_v = IM_ROUND(scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v));
@@ -898,10 +945,11 @@
}
// Render
- window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, rounding_corners);
+ const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);
const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
+ window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, rounding_corners);
ImRect grab_rect;
- if (horizontal)
+ if (axis == ImGuiAxis_X)
grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y);
else
grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels);
@@ -910,38 +958,6 @@
return held;
}
-void ImGui::Scrollbar(ImGuiAxis axis)
-{
- ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
-
- const ImGuiID id = GetWindowScrollbarID(window, axis);
- KeepAliveID(id);
-
- // Calculate scrollbar bounding box
- const ImRect outer_rect = window->Rect();
- const ImRect inner_rect = window->InnerRect;
- const float border_size = window->WindowBorderSize;
- const float scrollbar_size = window->ScrollbarSizes[axis ^ 1];
- IM_ASSERT(scrollbar_size > 0.0f);
- const float other_scrollbar_size = window->ScrollbarSizes[axis];
- ImDrawCornerFlags rounding_corners = (other_scrollbar_size <= 0.0f) ? ImDrawCornerFlags_BotRight : 0;
- ImRect bb;
- if (axis == ImGuiAxis_X)
- {
- bb.Min = ImVec2(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size));
- bb.Max = ImVec2(inner_rect.Max.x, outer_rect.Max.y);
- rounding_corners |= ImDrawCornerFlags_BotLeft;
- }
- else
- {
- bb.Min = ImVec2(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y);
- bb.Max = ImVec2(outer_rect.Max.x, window->InnerRect.Max.y);
- rounding_corners |= ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0;
- }
- ScrollbarEx(bb, id, axis, &window->Scroll[axis], inner_rect.Max[axis] - inner_rect.Min[axis], window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f, rounding_corners);
-}
-
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiWindow* window = GetCurrentWindow();
@@ -966,28 +982,16 @@
}
}
-// frame_padding < 0: uses FramePadding from style (default)
-// frame_padding = 0: no framing
-// frame_padding > 0: set framing size
-// The color used are the button colors.
-bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
+// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390)
+// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API.
+bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col)
{
+ ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
- ImGuiContext& g = *GImGui;
- const ImGuiStyle& style = g.Style;
-
- // Default to using texture ID as ID. User can still push string/integer prefixes.
- // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
- PushID((void*)(intptr_t)user_texture_id);
- const ImGuiID id = window->GetID("#image");
- PopID();
-
- const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
- const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
ItemSize(bb);
if (!ItemAdd(bb, id))
return false;
@@ -998,14 +1002,33 @@
// Render
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
RenderNavHighlight(bb, id);
- RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding));
+ RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
if (bg_col.w > 0.0f)
- window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col));
- window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col));
+ window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
+ window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
return pressed;
}
+// frame_padding < 0: uses FramePadding from style (default)
+// frame_padding = 0: no framing
+// frame_padding > 0: set framing size
+bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ if (window->SkipItems)
+ return false;
+
+ // Default to using texture ID as ID. User can still push string/integer prefixes.
+ PushID((void*)(intptr_t)user_texture_id);
+ const ImGuiID id = window->GetID("#image");
+ PopID();
+
+ const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding;
+ return ImageButtonEx(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col);
+}
+
bool ImGui::Checkbox(const char* label, bool* v)
{
ImGuiWindow* window = GetCurrentWindow();
@@ -1045,7 +1068,7 @@
else if (*v)
{
const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
- RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad*2.0f);
+ RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);
}
if (g.LogEnabled)
@@ -1111,7 +1134,7 @@
if (style.FrameBorderSize > 0.0f)
{
- window->DrawList->AddCircle(center + ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
+ window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
}
@@ -1120,6 +1143,7 @@
if (label_size.x > 0.0f)
RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label);
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
return pressed;
}
@@ -1143,7 +1167,7 @@
const ImGuiStyle& style = g.Style;
ImVec2 pos = window->DC.CursorPos;
- ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f);
+ ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f);
ImRect bb(pos, pos + size);
ItemSize(size, style.FramePadding.y);
if (!ItemAdd(bb, 0))
@@ -1160,13 +1184,13 @@
char overlay_buf[32];
if (!overlay)
{
- ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f);
+ ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f);
overlay = overlay_buf;
}
ImVec2 overlay_size = CalcTextSize(overlay, NULL);
if (overlay_size.x > 0.0f)
- RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
+ RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb);
}
void ImGui::Bullet()
@@ -1177,18 +1201,18 @@
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
+ const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2), g.FontSize);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
ItemSize(bb);
if (!ItemAdd(bb, 0))
{
- SameLine(0, style.FramePadding.x*2);
+ SameLine(0, style.FramePadding.x * 2);
return;
}
// Render and stay on same line
ImU32 text_col = GetColorU32(ImGuiCol_Text);
- RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f), text_col);
+ RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col);
SameLine(0, style.FramePadding.x * 2.0f);
}
@@ -1210,7 +1234,7 @@
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
- ItemSize(ImVec2(0,0));
+ ItemSize(ImVec2(0, 0));
}
void ImGui::Dummy(const ImVec2& size)
@@ -1234,7 +1258,7 @@
const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
window->DC.LayoutType = ImGuiLayoutType_Vertical;
if (window->DC.CurrLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
- ItemSize(ImVec2(0,0));
+ ItemSize(ImVec2(0, 0));
else
ItemSize(ImVec2(0.0f, g.FontSize));
window->DC.LayoutType = backup_layout_type;
@@ -1461,7 +1485,7 @@
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const float expected_w = CalcItemWidth();
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w;
- const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
+ const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, id, &frame_bb))
@@ -1469,7 +1493,7 @@
bool hovered, held;
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
- bool popup_open = IsPopupOpen(id);
+ bool popup_open = IsPopupOpen(id, ImGuiPopupFlags_None);
const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size);
@@ -1486,7 +1510,7 @@
}
RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
- RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
+ RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f, 0.0f));
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@@ -1494,7 +1518,7 @@
{
if (window->DC.NavLayerCurrent == 0)
window->NavLastIds[0] = id;
- OpenPopupEx(id);
+ OpenPopupEx(id, ImGuiPopupFlags_None);
popup_open = true;
}
@@ -1596,7 +1620,7 @@
// The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint))
- SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
+ SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
return false;
@@ -1654,6 +1678,7 @@
// - DataTypeFormatString()
// - DataTypeApplyOp()
// - DataTypeApplyOpFromText()
+// - DataTypeClamp()
// - GetMinimumStepAtDecimalPrecision
// - RoundScalarWithFormat<>()
//-------------------------------------------------------------------------
@@ -1805,11 +1830,9 @@
return false;
// Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.
- IM_ASSERT(data_type < ImGuiDataType_COUNT);
- int data_backup[2];
- const ImGuiDataTypeInfo* type_info = ImGui::DataTypeGetInfo(data_type);
- IM_ASSERT(type_info->Size <= sizeof(data_backup));
- memcpy(data_backup, p_data, type_info->Size);
+ const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);
+ ImGuiDataTypeTempStorage data_backup;
+ memcpy(&data_backup, p_data, type_info->Size);
if (format == NULL)
format = type_info->ScanFmt;
@@ -1881,9 +1904,37 @@
IM_ASSERT(0);
}
- return memcmp(data_backup, p_data, type_info->Size) != 0;
+ return memcmp(&data_backup, p_data, type_info->Size) != 0;
}
+template<typename T>
+static bool ClampBehaviorT(T* v, T v_min, T v_max)
+{
+ if (*v < v_min) { *v = v_min; return true; }
+ if (*v > v_max) { *v = v_max; return true; }
+ return false;
+}
+
+bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max)
+{
+ switch (data_type)
+ {
+ case ImGuiDataType_S8: return ClampBehaviorT<ImS8 >((ImS8* )p_data, *(const ImS8* )p_min, *(const ImS8* )p_max);
+ case ImGuiDataType_U8: return ClampBehaviorT<ImU8 >((ImU8* )p_data, *(const ImU8* )p_min, *(const ImU8* )p_max);
+ case ImGuiDataType_S16: return ClampBehaviorT<ImS16 >((ImS16* )p_data, *(const ImS16* )p_min, *(const ImS16* )p_max);
+ case ImGuiDataType_U16: return ClampBehaviorT<ImU16 >((ImU16* )p_data, *(const ImU16* )p_min, *(const ImU16* )p_max);
+ case ImGuiDataType_S32: return ClampBehaviorT<ImS32 >((ImS32* )p_data, *(const ImS32* )p_min, *(const ImS32* )p_max);
+ case ImGuiDataType_U32: return ClampBehaviorT<ImU32 >((ImU32* )p_data, *(const ImU32* )p_min, *(const ImU32* )p_max);
+ case ImGuiDataType_S64: return ClampBehaviorT<ImS64 >((ImS64* )p_data, *(const ImS64* )p_min, *(const ImS64* )p_max);
+ case ImGuiDataType_U64: return ClampBehaviorT<ImU64 >((ImU64* )p_data, *(const ImU64* )p_min, *(const ImU64* )p_max);
+ case ImGuiDataType_Float: return ClampBehaviorT<float >((float* )p_data, *(const float* )p_min, *(const float* )p_max);
+ case ImGuiDataType_Double: return ClampBehaviorT<double>((double*)p_data, *(const double*)p_min, *(const double*)p_max);
+ case ImGuiDataType_COUNT: break;
+ }
+ IM_ASSERT(0);
+ return false;
+}
+
static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
{
static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
@@ -1961,7 +2012,7 @@
// Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings
float adjust_delta = 0.0f;
- if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f)
+ if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f * 1.0f)
{
adjust_delta = g.IO.MouseDelta[axis];
if (g.IO.KeyAlt)
@@ -2098,7 +2149,7 @@
const ImGuiID id = window->GetID(label);
const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
- const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
+ const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
ItemSize(total_bb, style.FramePadding.y);
@@ -2133,8 +2184,10 @@
}
}
}
+
+ // Our current specs do NOT clamp when using CTRL+Click manual input, but we should eventually add a flag for that..
if (temp_input_is_active || temp_input_start)
- return TempInputScalar(frame_bb, id, label, data_type, p_data, format);
+ return TempInputScalar(frame_bb, id, label, data_type, p_data, format);// , p_min, p_max);
// Draw frame
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
@@ -2317,12 +2370,12 @@
if (v_clamped < 0.0f)
{
const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min));
- return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos;
+ return (1.0f - ImPow(f, 1.0f / power)) * linear_zero_pos;
}
else
{
const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min)));
- return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos);
+ return linear_zero_pos + ImPow(f, 1.0f / power) * (1.0f - linear_zero_pos);
}
}
@@ -2330,7 +2383,7 @@
return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min));
}
-// FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc.
+// FIXME: Move some of the code into SliderBehavior(). Current responsibility is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc.
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb)
{
@@ -2398,7 +2451,7 @@
}
else if (delta != 0.0f)
{
- clicked_t = SliderCalcRatioFromValueT<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
+ clicked_t = SliderCalcRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
if ((decimal_precision > 0) || is_power)
{
@@ -2470,7 +2523,7 @@
}
// Round to user desired precision based on format string
- v_new = RoundScalarWithFormatT<TYPE,SIGNEDTYPE>(format, data_type, v_new);
+ v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
// Apply result
if (*v != v_new)
@@ -2513,23 +2566,23 @@
case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, power, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }
case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, power, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }
case ImGuiDataType_S32:
- IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN/2 && *(const ImS32*)p_max <= IM_S32_MAX/2);
+ IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2);
return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)p_v, *(const ImS32*)p_min, *(const ImS32*)p_max, format, power, flags, out_grab_bb);
case ImGuiDataType_U32:
- IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX/2);
+ IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2);
return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)p_v, *(const ImU32*)p_min, *(const ImU32*)p_max, format, power, flags, out_grab_bb);
case ImGuiDataType_S64:
- IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN/2 && *(const ImS64*)p_max <= IM_S64_MAX/2);
+ IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2);
return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)p_v, *(const ImS64*)p_min, *(const ImS64*)p_max, format, power, flags, out_grab_bb);
case ImGuiDataType_U64:
- IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX/2);
+ IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2);
return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)p_v, *(const ImU64*)p_min, *(const ImU64*)p_max, format, power, flags, out_grab_bb);
case ImGuiDataType_Float:
- IM_ASSERT(*(const float*)p_min >= -FLT_MAX/2.0f && *(const float*)p_max <= FLT_MAX/2.0f);
+ IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f);
return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)p_v, *(const float*)p_min, *(const float*)p_max, format, power, flags, out_grab_bb);
case ImGuiDataType_Double:
- IM_ASSERT(*(const double*)p_min >= -DBL_MAX/2.0f && *(const double*)p_max <= DBL_MAX/2.0f);
- return SliderBehaviorT<double,double,double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, power, flags, out_grab_bb);
+ IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f);
+ return SliderBehaviorT<double, double, double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, power, flags, out_grab_bb);
case ImGuiDataType_COUNT: break;
}
IM_ASSERT(0);
@@ -2550,7 +2603,7 @@
const float w = CalcItemWidth();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
- const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
+ const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
ItemSize(total_bb, style.FramePadding.y);
@@ -2584,8 +2637,10 @@
}
}
}
+
+ // Our current specs do NOT clamp when using CTRL+Click manual input, but we should eventually add a flag for that..
if (temp_input_is_active || temp_input_start)
- return TempInputScalar(frame_bb, id, label, data_type, p_data, format);
+ return TempInputScalar(frame_bb, id, label, data_type, p_data, format);// , p_min, p_max);
// Draw frame
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
@@ -2605,7 +2660,7 @@
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
char value_buf[64];
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
- RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
+ RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@@ -2674,9 +2729,9 @@
{
if (format == NULL)
format = "%.0f deg";
- float v_deg = (*v_rad) * 360.0f / (2*IM_PI);
+ float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, 1.0f);
- *v_rad = v_deg * (2*IM_PI) / 360.0f;
+ *v_rad = v_deg * (2 * IM_PI) / 360.0f;
return value_changed;
}
@@ -2752,7 +2807,7 @@
// For the vertical slider we allow centered text to overlap the frame padding
char value_buf[64];
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
- RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f));
+ RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@@ -2884,7 +2939,21 @@
return value_changed;
}
-bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format)
+// Note that Drag/Slider functions are currently NOT forwarding the min/max values clamping values!
+// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility.
+// However this may not be ideal for all uses, as some user code may break on out of bound values.
+// In the future we should add flags to Slider/Drag to specify how to enforce min/max values with CTRL+Click.
+// See GitHub issues #1829 and #3209
+// In the meanwhile, you can easily "wrap" those functions to enforce clamping, using wrapper functions, e.g.
+// bool SliderFloatClamp(const char* label, float* v, float v_min, float v_max)
+// {
+// float v_backup = *v;
+// if (!SliderFloat(label, v, v_min, v_max))
+// return false;
+// *v = ImClamp(*v, v_min, v_max);
+// return v_backup != *v;
+// }
+bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max)
{
ImGuiContext& g = *GImGui;
@@ -2896,10 +2965,21 @@
ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited;
flags |= ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal);
- bool value_changed = TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags);
- if (value_changed)
+ bool value_changed = false;
+ if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))
{
- value_changed = DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL);
+ // Backup old value
+ size_t data_type_size = DataTypeGetInfo(data_type)->Size;
+ ImGuiDataTypeTempStorage data_backup;
+ memcpy(&data_backup, p_data, data_type_size);
+
+ // Apply new value (or operations) then clamp
+ DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL);
+ if (p_clamp_min && p_clamp_max)
+ DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max);
+
+ // Only mark as edited if new value is different
+ value_changed = memcmp(&data_backup, p_data, data_type_size) != 0;
if (value_changed)
MarkItemEdited(id);
}
@@ -2927,7 +3007,7 @@
if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
flags |= ImGuiInputTextFlags_CharsDecimal;
flags |= ImGuiInputTextFlags_AutoSelectAll;
- flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselve by comparing the actual data rather than the string.
+ flags |= ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
if (p_step != NULL)
{
@@ -3018,7 +3098,7 @@
bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)
{
flags |= ImGuiInputTextFlags_CharsScientific;
- return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, flags);
+ return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags);
}
bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags)
@@ -3075,7 +3155,7 @@
{
// 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* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
- return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, flags);
+ return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL), format, flags);
}
bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags)
@@ -3096,7 +3176,7 @@
bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)
{
flags |= ImGuiInputTextFlags_CharsScientific;
- return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, flags);
+ return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags);
}
//-------------------------------------------------------------------------
@@ -3111,7 +3191,7 @@
bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
{
IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
- return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data);
+ return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
}
bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
@@ -3146,7 +3226,7 @@
const float line_height = g.FontSize;
const float scale = line_height / font->FontSize;
- ImVec2 text_size = ImVec2(0,0);
+ ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
const ImWchar* s = text_begin;
@@ -3207,10 +3287,10 @@
}
static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
-static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; }
+static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
#ifdef __APPLE__ // FIXME: Move setting to IO structure
-static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; }
+static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
#else
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
@@ -3345,7 +3425,7 @@
if (!is_resizable)
return;
- // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!)
+ // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!)
ImGuiContext& g = *GImGui;
ImGuiInputTextState* edit_state = &g.InputTextState;
IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);
@@ -3398,21 +3478,25 @@
// Generic named filters
if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))
{
+ // Allow 0-9 . - + * /
if (flags & ImGuiInputTextFlags_CharsDecimal)
if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
return false;
+ // Allow 0-9 . - + * / e E
if (flags & ImGuiInputTextFlags_CharsScientific)
if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))
return false;
+ // Allow 0-9 a-F A-F
if (flags & ImGuiInputTextFlags_CharsHexadecimal)
if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
return false;
+ // Turn a-z into A-Z
if (flags & ImGuiInputTextFlags_CharsUppercase)
if (c >= 'a' && c <= 'z')
- *p_char = (c += (unsigned int)('A'-'a'));
+ *p_char = (c += (unsigned int)('A' - 'a'));
if (flags & ImGuiInputTextFlags_CharsNoBlank)
if (ImCharIsBlankW(c))
@@ -3452,6 +3536,7 @@
if (window->SkipItems)
return false;
+ IM_ASSERT(buf != NULL && buf_size >= 0);
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys)
IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
@@ -3472,7 +3557,7 @@
BeginGroup();
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
- const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line
+ const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line
const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
@@ -3662,7 +3747,7 @@
// Edit in progress
const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX;
- const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
+ const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize * 0.5f));
const bool is_osx = io.ConfigMacOSXBehaviors;
if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0]))
@@ -3729,14 +3814,16 @@
if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
{
IM_ASSERT(state != NULL);
+ IM_ASSERT(io.KeyMods == GetMergedKeyModFlags() && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); // We rarely do this check, but if anything let's do it here.
+
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
const bool is_osx = io.ConfigMacOSXBehaviors;
- const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
- const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt;
+ const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiKeyModFlags_Super | ImGuiKeyModFlags_Shift));
const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
- const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper;
- const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper;
+ const bool is_ctrl_key_only = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
+ const bool is_shift_key_only = (io.KeyMods == ImGuiKeyModFlags_Shift);
+ const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
@@ -3756,9 +3843,9 @@
if (!state->HasSelection())
{
if (is_wordmove_key_down)
- state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
+ state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT);
else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl)
- state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
+ state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT);
}
state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
}
@@ -3817,7 +3904,7 @@
{
// Filter pasted buffer
const int clipboard_len = (int)strlen(clipboard);
- ImWchar* clipboard_filtered = (ImWchar*)IM_ALLOC((clipboard_len+1) * sizeof(ImWchar));
+ ImWchar* clipboard_filtered = (ImWchar*)IM_ALLOC((clipboard_len + 1) * sizeof(ImWchar));
int clipboard_filtered_len = 0;
for (const char* s = clipboard; *s; )
{
@@ -4149,7 +4236,7 @@
{
ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true);
if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
- ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn));
+ ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
rect.ClipWith(clip_rect);
if (rect.Overlaps(clip_rect))
draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
@@ -4209,7 +4296,7 @@
PopFont();
// Log as text
- if (g.LogEnabled && !(is_password && !is_displaying_hint))
+ if (g.LogEnabled && (!is_password || is_displaying_hint))
LogRenderedText(&draw_pos, buf_display, buf_display_end);
if (label_size.x > 0)
@@ -4320,8 +4407,8 @@
if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
{
// RGB/HSV 0..255 Sliders
- const float w_item_one = ImMax(1.0f, IM_FLOOR((w_inputs - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
- const float w_item_last = ImMax(1.0f, IM_FLOOR(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
+ const float w_item_one = ImMax(1.0f, IM_FLOOR((w_inputs - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
+ const float w_item_last = ImMax(1.0f, IM_FLOOR(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
static const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
@@ -4348,7 +4435,7 @@
// FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0.
if (flags & ImGuiColorEditFlags_Float)
{
- value_changed |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
+ value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
value_changed_as_float |= value_changed;
}
else
@@ -4356,7 +4443,7 @@
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
- OpenPopupOnItemClick("context");
+ OpenPopupContextItem("context");
}
}
else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
@@ -4364,9 +4451,9 @@
// RGB Hexadecimal Input
char buf[64];
if (alpha)
- ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255));
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255));
else
- ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255));
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255));
SetNextItemWidth(w_inputs);
if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
{
@@ -4381,7 +4468,7 @@
sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
- OpenPopupOnItemClick("context");
+ OpenPopupContextItem("context");
}
ImGuiWindow* picker_active_window = NULL;
@@ -4398,11 +4485,11 @@
// Store current color and open a picker
g.ColorPickerRef = col_v4;
OpenPopup("picker");
- SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
+ SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1, style.ItemSpacing.y));
}
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
- OpenPopupOnItemClick("context");
+ OpenPopupContextItem("context");
if (BeginPopup("picker"))
{
@@ -4559,7 +4646,7 @@
float wheel_thickness = sv_picker_size * 0.08f;
float wheel_r_outer = sv_picker_size * 0.50f;
float wheel_r_inner = wheel_r_outer - wheel_thickness;
- ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f);
+ ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f);
// Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
@@ -4598,10 +4685,10 @@
ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
ImVec2 current_off = g.IO.MousePos - wheel_center;
float initial_dist2 = ImLengthSqr(initial_off);
- if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1))
+ if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1))
{
// Interactive with Hue wheel
- H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f;
+ H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f;
if (H < 0.0f)
H += 1.0f;
value_changed = value_changed_h = true;
@@ -4622,7 +4709,7 @@
}
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
- OpenPopupOnItemClick("context");
+ OpenPopupContextItem("context");
}
else if (flags & ImGuiColorEditFlags_PickerHueBar)
{
@@ -4630,12 +4717,12 @@
InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
if (IsItemActive())
{
- S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1));
- V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
+ S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));
+ V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
value_changed = value_changed_sv = true;
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
- OpenPopupOnItemClick("context");
+ OpenPopupContextItem("context");
// Hue bar logic
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
@@ -4642,7 +4729,7 @@
InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
if (IsItemActive())
{
- H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
+ H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
value_changed = value_changed_h = true;
}
}
@@ -4654,7 +4741,7 @@
InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
if (IsItemActive())
{
- col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
+ col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
value_changed = true;
}
}
@@ -4705,7 +4792,7 @@
{
if (flags & ImGuiColorEditFlags_InputRGB)
{
- ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
+ ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]);
g.ColorEditLastHue = H;
g.ColorEditLastSat = S;
memcpy(g.ColorEditLastColor, col, sizeof(float) * 3);
@@ -4728,7 +4815,7 @@
if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags__DisplayMask) == 0)
if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB))
{
- // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.
+ // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.
// For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050)
value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap);
value_changed = true;
@@ -4808,17 +4895,17 @@
// Paint colors over existing vertices
ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);
ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);
- ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n+1]);
+ ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]);
}
// Render Cursor + preview on Hue Wheel
float cos_hue_angle = ImCos(H * 2.0f * IM_PI);
float sin_hue_angle = ImSin(H * 2.0f * IM_PI);
- ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f);
+ ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f);
float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32);
draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
- draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, col_midgrey, hue_cursor_segments);
+ draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments);
draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments);
// Render SV triangle (rotated according to hue)
@@ -4856,7 +4943,7 @@
// Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;
draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, 12);
- draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, col_midgrey, 12);
+ draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, 12);
draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, 12);
// Render alpha bar
@@ -4928,8 +5015,8 @@
if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f)
{
float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f);
- RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight);
- window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
+ RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight);
+ window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft);
}
else
{
@@ -5047,7 +5134,7 @@
if (allow_opt_inputs || allow_opt_datatype)
Separator();
- if (Button("Copy as..", ImVec2(-1,0)))
+ if (Button("Copy as..", ImVec2(-1, 0)))
OpenPopup("Copy");
if (BeginPopup("Copy"))
{
@@ -5091,7 +5178,7 @@
// Draw small/thumbnail version of each picker type (over an invisible button for selection)
if (picker_type > 0) Separator();
PushID(picker_type);
- ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha);
+ ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha);
if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
ImVec2 backup_pos = GetCursorScreenPos();
@@ -5275,7 +5362,7 @@
const ImVec2 label_size = CalcTextSize(label, label_end, false);
// We vertically grow up to current line height up the typical widget height.
- const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
+ const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2);
ImRect frame_bb;
frame_bb.Min.x = (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
frame_bb.Min.y = window->DC.CursorPos.y;
@@ -5289,9 +5376,9 @@
frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f);
}
- const float text_offset_x = g.FontSize + (display_frame ? padding.x*3 : padding.x*2); // Collapser arrow width + Spacing
+ const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapser arrow width + Spacing
const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
- const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
+ const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapser
ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);
ItemSize(ImVec2(text_width, frame_height), padding.y);
@@ -5320,18 +5407,9 @@
return is_open;
}
- // Flags that affects opening behavior:
- // - 0 (default) .................... single-click anywhere to open
- // - OpenOnDoubleClick .............. double-click anywhere to open
- // - OpenOnArrow .................... single-click on arrow to open
- // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
- ImGuiButtonFlags button_flags = 0;
+ ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;
if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
button_flags |= ImGuiButtonFlags_AllowItemOverlap;
- if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
- button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
- else
- button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
if (!is_leaf)
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
@@ -5338,12 +5416,31 @@
// We allow clicking on the arrow section with keyboard modifiers held, in order to easily
// allow browsing a tree while preserving selection with code implementing multi-selection patterns.
// When clicking on the rest of the tree node we always disallow keyboard modifiers.
- const float hit_padding_x = style.TouchExtraPadding.x;
- const float arrow_hit_x1 = (text_pos.x - text_offset_x) - hit_padding_x;
- const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + hit_padding_x;
- if (window != g.HoveredWindow || !(g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2))
+ const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x;
+ const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;
+ const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2);
+ if (window != g.HoveredWindow || !is_mouse_x_over_arrow)
button_flags |= ImGuiButtonFlags_NoKeyModifiers;
+ // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags.
+ // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support.
+ // - Single-click on label = Toggle on MouseUp (default)
+ // - Single-click on arrow = Toggle on MouseUp (when _OpenOnArrow=0)
+ // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1)
+ // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1)
+ // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0)
+ // This makes _OpenOnArrow have a subtle effect on _OpenOnDoubleClick: arrow click reacts on Down rather than Up.
+ // It is rather standard that arrow click react on Down rather than Up and we'd be tempted to make it the default
+ // (by removing the _OpenOnArrow test below), however this would have a perhaps surprising effect on CollapsingHeader()?
+ // So right now we are making this optional. May evolve later.
+ // We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work.
+ if (is_mouse_x_over_arrow && (flags & ImGuiTreeNodeFlags_OpenOnArrow))
+ button_flags |= ImGuiButtonFlags_PressedOnClick;
+ else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
+ button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
+ else
+ button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
+
bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
const bool was_selected = selected;
@@ -5352,17 +5449,21 @@
bool toggled = false;
if (!is_leaf)
{
- if (pressed)
+ if (pressed && g.DragDropHoldJustPressedId != id)
{
if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == 0 || (g.NavActivateId == id))
toggled = true;
if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
- toggled |= (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2) && (!g.NavDisableMouseHover); // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
+ toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseDoubleClicked[0])
toggled = true;
- if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
- toggled = false;
}
+ else if (pressed && g.DragDropHoldJustPressedId == id)
+ {
+ IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold);
+ if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
+ toggled = true;
+ }
if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
{
@@ -5411,9 +5512,9 @@
// NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
const char log_prefix[] = "\n##";
const char log_suffix[] = "##";
- LogRenderedText(&text_pos, log_prefix, log_prefix+3);
+ LogRenderedText(&text_pos, log_prefix, log_prefix + 3);
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
- LogRenderedText(&text_pos, log_suffix, log_suffix+2);
+ LogRenderedText(&text_pos, log_suffix, log_suffix + 2);
}
else
{
@@ -5462,7 +5563,8 @@
void ImGui::TreePushOverrideID(ImGuiID id)
{
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
Indent();
window->DC.TreeDepth++;
window->IDStack.push_back(id);
@@ -5533,7 +5635,7 @@
if (p_open)
flags |= ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton;
bool is_open = TreeNodeBehavior(id, flags, label);
- if (p_open)
+ if (p_open != NULL)
{
// Create a small overlapping close button
// FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
@@ -5832,13 +5934,13 @@
// - PlotHistogram()
//-------------------------------------------------------------------------
-void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size)
+int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size)
{
+ ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
- return;
+ return -1;
- ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
@@ -5853,7 +5955,7 @@
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, 0, &frame_bb))
- return;
+ return -1;
const bool hovered = ItemHoverable(frame_bb, id);
// Determine scale from values if not specified
@@ -5878,6 +5980,7 @@
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1;
+ int idx_hovered = -1;
if (values_count >= values_count_min)
{
int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
@@ -5884,7 +5987,6 @@
int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
// Tooltip on hover
- int v_hovered = -1;
if (hovered && inner_bb.Contains(g.IO.MousePos))
{
const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
@@ -5894,10 +5996,10 @@
const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
if (plot_type == ImGuiPlotType_Lines)
- SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1);
+ SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1);
else if (plot_type == ImGuiPlotType_Histogram)
SetTooltip("%d: %8.4g", v_idx, v0);
- v_hovered = v_idx;
+ idx_hovered = v_idx;
}
const float t_step = 1.0f / (float)res_w;
@@ -5924,13 +6026,13 @@
ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
if (plot_type == ImGuiPlotType_Lines)
{
- window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
+ window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
}
else if (plot_type == ImGuiPlotType_Histogram)
{
if (pos1.x >= pos0.x + 2.0f)
pos1.x -= 1.0f;
- window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base);
+ window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
}
t0 = t1;
@@ -5940,10 +6042,14 @@
// Text overlay
if (overlay_text)
- RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
+ RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
+
+ // Return hovered index or -1 if none are hovered.
+ // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx().
+ return idx_hovered;
}
struct ImGuiPlotArrayGetterData
@@ -6175,7 +6281,7 @@
// When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
// FIXME: With this strategy we won't be able to restore a NULL focus.
ImGuiContext& g = *GImGui;
- if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0 && !g.NavAnyRequest)
+ if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest)
FocusTopMostWindowUnderOne(g.NavWindow, NULL);
End();
@@ -6190,7 +6296,7 @@
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
- bool menu_is_open = IsPopupOpen(id);
+ bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
@@ -6220,7 +6326,7 @@
g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
- // However the final position is going to be different! It is choosen by FindBestWindowPosForPopup().
+ // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
// e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.
ImVec2 popup_pos, pos = window->DC.CursorPos;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
@@ -6316,7 +6422,7 @@
if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
want_close = true;
- if (want_close && IsPopupOpen(id))
+ if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None))
ClosePopupToLevel(g.BeginPopupStack.Size, true);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
@@ -7003,6 +7109,7 @@
const ImGuiID id = TabBarCalcTabID(tab_bar, label);
// If the user called us with *p_open == false, we early out and don't render. We make a dummy call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
if (p_open && !*p_open)
{
PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
@@ -7060,6 +7167,7 @@
tab_bar->NextSelectedTabId = id;
// Lock visibility
+ // (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!)
bool tab_contents_visible = (tab_bar->VisibleTabId == id);
if (tab_contents_visible)
tab_bar->VisibleTabWasSubmitted = true;
@@ -7069,7 +7177,9 @@
if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))
tab_contents_visible = true;
- if (tab_appearing && !(tab_bar_appearing && !tab_is_new))
+ // Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted
+ // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'.
+ if (tab_appearing && (!tab_bar_appearing || tab_is_new))
{
PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true);
ItemAdd(ImRect(), id);
@@ -7165,7 +7275,7 @@
// Render tab label, process close button
const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0;
- bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id);
+ bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible);
if (just_closed && p_open != NULL)
{
*p_open = false;
@@ -7180,7 +7290,7 @@
// Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer)
// We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar (which g.HoveredId ignores)
if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f && IsItemHovered())
- if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip))
+ if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))
SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
return tab_contents_visible;
@@ -7187,7 +7297,9 @@
}
// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.
-// To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem()
+// To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem().
+// Tabs closed by the close button will automatically be flagged to avoid this issue.
+// FIXME: We should aim to support calling SetTabItemClosed() after the tab submission (for next frame)
void ImGui::SetTabItemClosed(const char* label)
{
ImGuiContext& g = *GImGui;
@@ -7240,7 +7352,7 @@
// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic
// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter.
-bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id)
+bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible)
{
ImGuiContext& g = *GImGui;
ImVec2 label_size = CalcTextSize(label, NULL, true);
@@ -7247,6 +7359,14 @@
if (bb.GetWidth() <= 1.0f)
return false;
+ // In Style V2 we'll have full override of all colors per state (e.g. focused, selected)
+ // But right now if you want to alter text color of tabs this is what you need to do.
+#if 0
+ const float backup_alpha = g.Style.Alpha;
+ if (!is_contents_visible)
+ g.Style.Alpha *= 0.7f;
+#endif
+
// Render text label (with clipping + alpha gradient) + unsaved marker
const char* TAB_UNSAVED_MARKER = "*";
ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
@@ -7266,8 +7386,9 @@
bool close_button_pressed = false;
bool close_button_visible = false;
if (close_button_id != 0)
- if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id)
- close_button_visible = true;
+ if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForUnselectedCloseButton)
+ if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id)
+ close_button_visible = true;
if (close_button_visible)
{
ImGuiItemHoveredDataBackup last_item_backup;
@@ -7288,6 +7409,11 @@
float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f;
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size);
+#if 0
+ if (!is_contents_visible)
+ g.Style.Alpha = backup_alpha;
+#endif
+
return close_button_pressed;
}
@@ -7296,6 +7422,7 @@
// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc.
// In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
//-------------------------------------------------------------------------
+// - SetWindowClipRectBeforeSetChannel() [Internal]
// - GetColumnIndex()
// - GetColumnCount()
// - GetColumnOffset()
@@ -7313,6 +7440,18 @@
// - Columns()
//-------------------------------------------------------------------------
+// [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences,
+// they would meddle many times with the underlying ImDrawCmd.
+// Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let
+// the subsequent single call to SetCurrentChannel() does it things once.
+void ImGui::SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect)
+{
+ ImVec4 clip_rect_vec4 = clip_rect.ToVec4();
+ window->ClipRect = clip_rect;
+ window->DrawList->_CmdHeader.ClipRect = clip_rect_vec4;
+ window->DrawList->_ClipRectStack.Data[window->DrawList->_ClipRectStack.Size - 1] = clip_rect_vec4;
+}
+
int ImGui::GetColumnIndex()
{
ImGuiWindow* window = GetCurrentWindowRead();
@@ -7407,7 +7546,7 @@
column_index = columns->Current;
IM_ASSERT(column_index < columns->Columns.Size);
- const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
+ const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count - 1);
const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
@@ -7447,11 +7586,11 @@
ImGuiColumns* columns = window->DC.CurrentColumns;
if (columns->Count == 1)
return;
+
+ // Optimization: avoid SetCurrentChannel() + PushClipRect()
+ columns->HostBackupClipRect = window->ClipRect;
+ SetWindowClipRectBeforeSetChannel(window, columns->HostInitialClipRect);
columns->Splitter.SetCurrentChannel(window->DrawList, 0);
- int cmd_size = window->DrawList->CmdBuffer.Size;
- PushClipRect(columns->HostClipRect.Min, columns->HostClipRect.Max, false);
- IM_UNUSED(cmd_size);
- IM_ASSERT(cmd_size == window->DrawList->CmdBuffer.Size); // Being in channel 0 this should not have created an ImDrawCmd
}
void ImGui::PopColumnsBackground()
@@ -7460,8 +7599,10 @@
ImGuiColumns* columns = window->DC.CurrentColumns;
if (columns->Count == 1)
return;
+
+ // Optimization: avoid PopClipRect() + SetCurrentChannel()
+ SetWindowClipRectBeforeSetChannel(window, columns->HostBackupClipRect);
columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);
- PopClipRect();
}
ImGuiColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id)
@@ -7495,8 +7636,8 @@
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
- IM_ASSERT(columns_count >= 1 && columns_count <= 64); // Maximum 64 columns
- IM_ASSERT(window->DC.CurrentColumns == NULL); // Nested columns are currently not supported
+ IM_ASSERT(columns_count >= 1);
+ IM_ASSERT(window->DC.CurrentColumns == NULL); // Nested columns are currently not supported
// Acquire storage for the columns set
ImGuiID id = GetColumnsID(str_id, columns_count);
@@ -7509,7 +7650,7 @@
columns->HostCursorPosY = window->DC.CursorPos.y;
columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x;
- columns->HostClipRect = window->ClipRect;
+ columns->HostInitialClipRect = window->ClipRect;
columns->HostWorkRect = window->WorkRect;
// Set state for first column
@@ -7581,25 +7722,31 @@
IM_ASSERT(columns->Current == 0);
return;
}
+
+ // Next column
+ if (++columns->Current == columns->Count)
+ columns->Current = 0;
+
PopItemWidth();
- PopClipRect();
+ // Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect()
+ // (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them),
+ ImGuiColumnData* column = &columns->Columns[columns->Current];
+ SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
+ columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);
+
const float column_padding = g.Style.ItemSpacing.x;
columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
- if (++columns->Current < columns->Count)
+ if (columns->Current > 0)
{
// Columns 1+ ignore IndentX (by canceling it out)
// FIXME-COLUMNS: Unnecessary, could be locked?
window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + column_padding;
- columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);
}
else
{
- // New row/line
- // Column 0 honor IndentX
+ // New row/line: column 0 honor IndentX.
window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);
- columns->Splitter.SetCurrentChannel(window->DrawList, 1);
- columns->Current = 0;
columns->LineMinY = columns->LineMaxY;
}
window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
@@ -7607,8 +7754,6 @@
window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
window->DC.CurrLineTextBaseOffset = 0.0f;
- PushColumnClipRect(columns->Current); // FIXME-COLUMNS: Could it be an overwrite?
-
// FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup.
float offset_0 = GetColumnOffset(columns->Current);
float offset_1 = GetColumnOffset(columns->Current + 1);
@@ -7719,29 +7864,31 @@
//-----------------------------------------------------------------------------
// Typical call flow: (root level is public API):
// - BeginTable() user begin into a table
-// - BeginChild() - (if ScrollX/ScrollY is set)
-// - TableBeginUpdateColumns() - apply resize/order requests, lock columns active state, order
+// | BeginChild() - (if ScrollX/ScrollY is set)
+// | TableBeginUpdateColumns() - apply resize/order requests, lock columns active state, order
+// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame)
+// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of weighted columns) from their respective width
// - TableSetupColumn() user submit columns details (optional)
// - TableAutoHeaders() or TableHeader() user submit a headers row (optional)
-// - TableSortSpecsClickColumn() - when clicked: alter sort order and sort direction
-// - TableGetSortSpecs() user queries updated sort specs (optional)
+// | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction
+// | TableOpenContextMenu() - when right-clicked: trigger opening of the default context menu
+// - TableGetSortSpecs() user queries updated sort specs (optional, generally after submitting headers)
// - TableNextRow() / TableNextCell() user begin into the first row, also automatically called by TableAutoHeaders()
-// - TableUpdateLayout() - called by the FIRST call to TableNextRow()!
-// - TableUpdateDrawChannels() - setup ImDrawList channels
-// - TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
-// - TableDrawContextMenu() - draw right-click context menu
-// - TableEndCell() - close existing cell if not the first time
-// - TableBeginCell() - enter into current cell
+// | TableUpdateLayout() - lock all widths, columns positions, clipping rectangles. called by the FIRST call to TableNextRow()!
+// | - TableUpdateDrawChannels() - setup ImDrawList channels
+// | - TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission
+// | - TableDrawContextMenu() - draw right-click context menu
+// | TableEndCell() - close existing cell if not the first time
+// | TableBeginCell() - enter into current cell
// - [...] user emit contents
// - EndTable() user ends the table
-// - TableDrawBorders() - draw outer borders, inner vertical borders
-// - TableDrawMergeChannels() - merge draw channels if clipping isn't required
-// - TableSetColumnWidth() - apply resizing width
-// - TableUpdateColumnsWeightFromWidth() - recompute columns weights (of weighted columns) from their respective width
-// - EndChild() - (if ScrollX/ScrollY is set)
+// | TableDrawBorders() - draw outer borders, inner vertical borders
+// | TableReorderDrawChannelsForMerge() - merge draw channels if clipping isn't required
+// | EndChild() - (if ScrollX/ScrollY is set)
//-----------------------------------------------------------------------------
// Configuration
+static const float TABLE_BORDER_SIZE = 1.0f; // FIXME-TABLE: Currently hard-coded.
static const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f; // Extend outside inner borders.
static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f; // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped.
@@ -7764,8 +7911,8 @@
if (flags & ImGuiTableFlags_Resizable)
flags |= ImGuiTableFlags_BordersVInner;
- // Adjust flags: disable top rows freezing if there's no scrolling
- // In theory we could want to assert if ScrollFreeze was set without the corresponding scroll flag, but that would hinder demos.
+ // Adjust flags: disable top rows freezing if there's no scrolling.
+ // We could want to assert if ScrollFreeze was set without the corresponding scroll flag, but that would hinder demos.
if ((flags & ImGuiTableFlags_ScrollX) == 0)
flags &= ~ImGuiTableFlags_ScrollFreezeColumnsMask_;
if ((flags & ImGuiTableFlags_ScrollY) == 0)
@@ -7775,7 +7922,8 @@
if ((flags & ImGuiTableFlags_NoHostExtendY) && (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0)
flags &= ~ImGuiTableFlags_NoHostExtendY;
- // Adjust flags: we don't support NoClipX with (FreezeColumns > 0), we could with some work but it doesn't appear to be worth the effort
+ // Adjust flags: we don't support NoClipX with (FreezeColumns > 0)
+ // We could with some work but it doesn't appear to be worth the effort.
if (flags & ImGuiTableFlags_ScrollFreezeColumnsMask_)
flags &= ~ImGuiTableFlags_NoClipX;
@@ -7782,28 +7930,38 @@
return flags;
}
-// About 'outer_size':
-// The meaning of outer_size needs to differ slightly depending of if we are using ScrollX/ScrollY flags.
-// With ScrollX/ScrollY: using a child window for scrolling:
+ImGuiTable* ImGui::FindTableByID(ImGuiID id)
+{
+ ImGuiContext& g = *GImGui;
+ return g.Tables.GetByKey(id);
+}
+
+// (Read carefully because this is subtle but it does make sense!)
+// About 'outer_size', its meaning needs to differ slightly depending of if we are using ScrollX/ScrollY flags:
+// X:
+// - outer_size.x < 0.0f -> right align from window/work-rect maximum x edge.
+// - outer_size.x = 0.0f -> auto enlarge, use all available space.
+// - outer_size.x > 0.0f -> fixed width
+// Y with ScrollX/ScrollY: using a child window for scrolling:
// - outer_size.y < 0.0f -> bottom align
-// - outer_size.y = 0.0f -> bottom align: consistent with BeginChild(), best to preserve (0,0) default arg
-// - outer_size.y > 0.0f -> fixed child height
-// Without scrolling, we output table directly in parent window:
+// - outer_size.y = 0.0f -> bottom align, consistent with BeginChild(). not recommended unless table is last item in parent window.
+// - outer_size.y > 0.0f -> fixed child height. recommended when using Scrolling on any axis.
+// Y without scrolling, we output table directly in parent window:
// - outer_size.y < 0.0f -> bottom align (will auto extend, unless NoHostExtendV is set)
// - outer_size.y = 0.0f -> zero minimum height (will auto extend, unless NoHostExtendV is set)
// - outer_size.y > 0.0f -> minimum height (will auto extend, unless NoHostExtendV is set)
-// About: 'inner_width':
+// About 'inner_width':
// With ScrollX:
// - inner_width < 0.0f -> *illegal* fit in known width (right align from outer_size.x) <-- weird
-// - inner_width = 0.0f -> auto enlarge: *only* fixed size columns, which will take space they need (proportional columns becomes fixed columns) <-- desired default :(
-// - inner_width > 0.0f -> fit in known width: fixed column take space they need if possible (otherwise shrink down), proportional columns share remaining space.
+// - inner_width = 0.0f -> fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.
+// - inner_width > 0.0f -> override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!
// Without ScrollX:
-// - inner_width < 0.0f -> fit in known width (right align from outer_size.x) <-- desired default
-// - inner_width = 0.0f -> auto enlarge: will emit contents size in parent window
-// - inner_width > 0.0f -> fit in known width (bypass outer_size.x, permitted but not useful, should instead alter outer_width)
-// FIXME-TABLE: This is currently not very useful.
-// FIXME-TABLE: Replace enlarge vs fixed width by a flag.
-// Even if not really useful, we allow 'inner_scroll_width < outer_size.x' for consistency and to facilitate understanding of what the value does.
+// - inner_width -> *ignored*
+// Details:
+// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept
+// of "available space" doesn't make sense.
+// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding
+// of what the value does.
bool ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)
{
ImGuiID id = GetID(str_id);
@@ -7841,7 +7999,7 @@
// Acquire storage for the table
ImGuiTable* table = g.Tables.GetOrAddByKey(id);
const ImGuiTableFlags table_last_flags = table->Flags;
- const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceNo + 1;
+ const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
const ImGuiID instance_id = id + instance_no;
if (instance_no > 0)
IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID");
@@ -7849,7 +8007,7 @@
// Initialize
table->ID = id;
table->Flags = flags;
- table->InstanceNo = (ImS16)instance_no;
+ table->InstanceCurrent = (ImS16)instance_no;
table->LastFrameActive = g.FrameCount;
table->OuterWindow = table->InnerWindow = outer_window;
table->ColumnsCount = columns_count;
@@ -7860,17 +8018,20 @@
table->OuterRect = outer_rect;
table->WorkRect = outer_rect;
+ // When not using a child window, WorkRect.Max will grow as we append contents.
if (use_child_window)
{
- // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)
+ // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent
+ // (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)
ImVec2 override_content_size(FLT_MAX, FLT_MAX);
if ((flags & ImGuiTableFlags_ScrollX) && !(flags & ImGuiTableFlags_ScrollY))
override_content_size.y = FLT_MIN;
- // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and never lead to any scrolling)
- // We don't handle inner_width < 0.0f, we could potentially use it to right-align based on the right side of the child window work rect,
- // which would require knowing ahead if we are going to have decoration taking horizontal spaces (typically a vertical scrollbar).
- if (inner_width != 0.0f)
+ // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and
+ // never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align
+ // based on the right side of the child window work rect, which would require knowing ahead if we are going to
+ // have decoration taking horizontal spaces (typically a vertical scrollbar).
+ if ((flags & ImGuiTableFlags_ScrollX) && inner_width > 0.0f)
override_content_size.x = inner_width;
if (override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX)
@@ -7883,12 +8044,10 @@
table->WorkRect = table->InnerWindow->WorkRect;
table->OuterRect = table->InnerWindow->Rect();
}
- else
- {
- // WorkRect.Max will grow as we append contents.
- PushID(instance_id);
- }
+ // Push a standardized ID for both child and not-child using tables, equivalent to BeginTable() doing PushID(label) matching
+ PushOverrideID(instance_id);
+
// Backup a copy of host window members we will modify
ImGuiWindow* inner_window = table->InnerWindow;
table->HostIndentX = inner_window->DC.Indent.x;
@@ -7926,9 +8085,7 @@
table->FreezeColumnsCount = (inner_window->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0;
table->IsFreezeRowsPassed = (table->FreezeRowsCount == 0);
table->DeclColumnsCount = 0;
- table->HoveredColumnBody = -1;
- table->HoveredColumnBorder = -1;
- table->RightMostActiveColumn = -1;
+ table->RightMostVisibleColumn = -1;
// Using opaque colors facilitate overlapping elements of the grid
table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong);
@@ -7970,12 +8127,27 @@
if (table->IsSettingsRequestLoad)
TableLoadSettings(table);
+ // Handle DPI/font resize
+ // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.
+ // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.
+ // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
+ // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
+ const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ?
+ if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)
+ {
+ const float scale_factor = new_ref_scale_unit / table->RefScale;
+ //IMGUI_DEBUG_LOG("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);
+ for (int n = 0; n < columns_count; n++)
+ table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;
+ }
+ table->RefScale = new_ref_scale_unit;
+
// Disable output until user calls TableNextRow() or TableNextCell() leading to the TableUpdateLayout() call..
- // This is not strictly necessary but will reduce cases were misleading "out of table" output will be confusing to the user.
+ // This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user.
// Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.
inner_window->SkipItems = true;
- // Update/lock which columns will be Active for the frame
+ // Update/lock which columns will be Visible for the frame
TableBeginUpdateColumns(table);
return true;
@@ -7987,7 +8159,7 @@
// (We process this at the first TableBegin of the frame)
// FIXME-TABLE: Preserve contents width _while resizing down_ until releasing.
// FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling.
- if (table->InstanceNo == 0)
+ if (table->InstanceCurrent == 0)
{
if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX)
TableSetColumnWidth(table, &table->Columns[table->ResizedColumn], table->ResizedColumnNextWidth);
@@ -7998,7 +8170,7 @@
// Handle reordering request
// Note: we don't clear ReorderColumn after handling the request.
- if (table->InstanceNo == 0)
+ if (table->InstanceCurrent == 0)
{
if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1)
table->ReorderColumn = -1;
@@ -8013,7 +8185,7 @@
IM_ASSERT(reorder_dir == -1 || reorder_dir == +1);
IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable);
ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn];
- ImGuiTableColumn* dst_column = &table->Columns[(reorder_dir == -1) ? src_column->PrevActiveColumn : src_column->NextActiveColumn];
+ ImGuiTableColumn* dst_column = &table->Columns[(reorder_dir == -1) ? src_column->PrevVisibleColumn : src_column->NextVisibleColumn];
IM_UNUSED(dst_column);
const int src_order = src_column->DisplayOrder;
const int dst_order = dst_column->DisplayOrder;
@@ -8022,7 +8194,8 @@
table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImS8)reorder_dir;
IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir);
- // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[], rebuild the later from the former.
+ // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[],
+ // rebuild the later from the former.
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImS8)column_n;
table->ReorderColumnDir = 0;
@@ -8039,10 +8212,10 @@
table->IsSettingsDirty = true;
}
- // Setup and lock Active state and order
- table->ColumnsActiveCount = 0;
+ // Setup and lock Visible state and order
+ table->ColumnsVisibleCount = 0;
table->IsDefaultDisplayOrder = true;
- ImGuiTableColumn* last_active_column = NULL;
+ ImGuiTableColumn* last_visible_column = NULL;
bool want_column_auto_fit = false;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
@@ -8052,12 +8225,12 @@
ImGuiTableColumn* column = &table->Columns[column_n];
column->NameOffset = -1;
if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide))
- column->IsActiveNextFrame = true;
- if (column->IsActive != column->IsActiveNextFrame)
+ column->IsVisibleNextFrame = true;
+ if (column->IsVisible != column->IsVisibleNextFrame)
{
- column->IsActive = column->IsActiveNextFrame;
+ column->IsVisible = column->IsVisibleNextFrame;
table->IsSettingsDirty = true;
- if (!column->IsActive && column->SortOrder != -1)
+ if (!column->IsVisible && column->SortOrder != -1)
table->IsSortSpecsDirty = true;
}
if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_MultiSortable))
@@ -8067,30 +8240,30 @@
ImU64 index_mask = (ImU64)1 << column_n;
ImU64 display_order_mask = (ImU64)1 << column->DisplayOrder;
- if (column->IsActive)
+ if (column->IsVisible)
{
- column->PrevActiveColumn = column->NextActiveColumn = -1;
- if (last_active_column)
+ column->PrevVisibleColumn = column->NextVisibleColumn = -1;
+ if (last_visible_column)
{
- last_active_column->NextActiveColumn = (ImS8)column_n;
- column->PrevActiveColumn = (ImS8)table->Columns.index_from_ptr(last_active_column);
+ last_visible_column->NextVisibleColumn = (ImS8)column_n;
+ column->PrevVisibleColumn = (ImS8)table->Columns.index_from_ptr(last_visible_column);
}
- column->IndexWithinActiveSet = (ImS8)table->ColumnsActiveCount;
- table->ColumnsActiveCount++;
- table->ActiveMaskByIndex |= index_mask;
- table->ActiveMaskByDisplayOrder |= display_order_mask;
- last_active_column = column;
+ column->IndexWithinVisibleSet = (ImS8)table->ColumnsVisibleCount;
+ table->ColumnsVisibleCount++;
+ table->VisibleMaskByIndex |= index_mask;
+ table->VisibleMaskByDisplayOrder |= display_order_mask;
+ last_visible_column = column;
}
else
{
- column->IndexWithinActiveSet = -1;
- table->ActiveMaskByIndex &= ~index_mask;
- table->ActiveMaskByDisplayOrder &= ~display_order_mask;
+ column->IndexWithinVisibleSet = -1;
+ table->VisibleMaskByIndex &= ~index_mask;
+ table->VisibleMaskByDisplayOrder &= ~display_order_mask;
}
- IM_ASSERT(column->IndexWithinActiveSet <= column->DisplayOrder);
+ IM_ASSERT(column->IndexWithinVisibleSet <= column->DisplayOrder);
}
- table->VisibleMaskByIndex = table->ActiveMaskByIndex; // Columns will be masked out by TableUpdateLayout() when Clipped
- table->RightMostActiveColumn = (ImS8)(last_active_column ? table->Columns.index_from_ptr(last_active_column) : -1);
+ table->VisibleUnclippedMaskByIndex = table->VisibleMaskByIndex; // Columns will be masked out by TableUpdateLayout() when Clipped
+ table->RightMostVisibleColumn = (ImS8)(last_visible_column ? table->Columns.index_from_ptr(last_visible_column) : -1);
// Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid
// the column fitting to wait until the first visible frame of the child container (may or not be a good thing).
@@ -8101,10 +8274,13 @@
void ImGui::TableUpdateDrawChannels(ImGuiTable* table)
{
// Allocate draw channels.
- // - We allocate them following the storage order instead of the display order so reordering columns won't needlessly increase overall dormant memory cost.
- // - We isolate headers draw commands in their own channels instead of just altering clip rects. This is in order to facilitate merging of draw commands.
- // - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of draw channels.
- // - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.
+ // - We allocate them following storage order instead of display order so reordering columns won't needlessly
+ // increase overall dormant memory cost.
+ // - We isolate headers draw commands in their own channels instead of just altering clip rects.
+ // This is in order to facilitate merging of draw commands.
+ // - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.
+ // - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other
+ // channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.
// Draw channel allocation (before merging):
// - NoClip --> 1+1 channels: background + foreground (same clip rect == 1 draw call)
// - Clip --> 1+N channels
@@ -8111,9 +8287,9 @@
// - FreezeRows || FreezeColumns --> 1+N*2 (unless scrolling value is zero)
// - FreezeRows && FreezeColunns --> 2+N*2 (unless scrolling value is zero)
const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;
- const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClipX) ? 1 : table->ColumnsActiveCount;
+ const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClipX) ? 1 : table->ColumnsVisibleCount;
const int channels_for_background = 1;
- const int channels_for_dummy = (table->ColumnsActiveCount < table->ColumnsCount || table->VisibleMaskByIndex != table->ActiveMaskByIndex) ? +1 : 0;
+ const int channels_for_dummy = (table->ColumnsVisibleCount < table->ColumnsCount || table->VisibleUnclippedMaskByIndex != table->VisibleMaskByIndex) ? +1 : 0;
const int channels_total = channels_for_background + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total);
table->DummyDrawChannel = channels_for_dummy ? (ImS8)(channels_total - 1) : -1;
@@ -8169,6 +8345,10 @@
static void TableFixColumnSortDirection(ImGuiTableColumn* column)
{
+ // Initial sort state
+ if (column->SortDirection == ImGuiSortDirection_None)
+ column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);
+
// Handle NoSortAscending/NoSortDescending
if (column->SortDirection == ImGuiSortDirection_Ascending && (column->Flags & ImGuiTableColumnFlags_NoSortAscending))
column->SortDirection = ImGuiSortDirection_Descending;
@@ -8185,24 +8365,32 @@
// Layout columns for the frame
// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() to be called first.
-// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for WidthAlwaysAutoResize columns,
-// increase feedback side-effect with widgets relying on WorkRect.Max.x. Maybe provide a default distribution for WidthAlwaysAutoResize columns?
+// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for WidthAlwaysAutoResize
+// columns, increase feedback side-effect with widgets relying on WorkRect.Max.x. Maybe provide a default distribution
+// for WidthAlwaysAutoResize columns?
void ImGui::TableUpdateLayout(ImGuiTable* table)
{
+ ImGuiContext& g = *GImGui;
IM_ASSERT(table->IsLayoutLocked == false);
+ table->HoveredColumnBody = -1;
+ table->HoveredColumnBorder = -1;
+
// Compute offset, clip rect for the frame
+ // (can't make auto padding larger than what WorkRect knows about so right-alignment matches)
const ImRect work_rect = table->WorkRect;
- const float padding_auto_x = table->CellPaddingX2; // Can't make auto padding larger than what WorkRect knows about so right-alignment matches.
+ const float padding_auto_x = table->CellPaddingX2;
+ const float spacing_auto_x = table->CellSpacingX * (1.0f + 2.0f); // CellSpacingX is >0.0f when there's no vertical border, in which case we add two extra CellSpacingX to make auto-fit look nice instead of cramped. We may want to expose this somehow.
const float min_column_width = TableGetMinColumnWidth();
int count_fixed = 0;
- float width_fixed = 0.0f;
- float total_weights = 0.0f;
+ float sum_weights_stretched = 0.0f; // Sum of all weights for weighted columns.
+ float sum_width_fixed_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns.
table->LeftMostStretchedColumnDisplayOrder = -1;
+ table->ColumnsAutoFitWidth = 0.0f;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if (!(table->VisibleMaskByDisplayOrder & ((ImU64)1 << order_n)))
continue;
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
@@ -8218,48 +8406,59 @@
if (table->Flags & ImGuiTableFlags_Sortable)
TableFixColumnSortDirection(column);
+ // Calculate "ideal" column width for nothing to be clipped.
+ // Combine width from regular rows + width from headers unless requested not to.
+ const float column_content_width_rows = (float)ImMax(column->ContentWidthRowsFrozen, column->ContentWidthRowsUnfrozen);
+ const float column_content_width_headers = (float)column->ContentWidthHeadersIdeal;
+ float column_width_ideal = column_content_width_rows;
+ if (!(table->Flags & ImGuiTableFlags_NoHeadersWidth) && !(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth))
+ column_width_ideal = ImMax(column_width_ideal, column_content_width_headers);
+ column_width_ideal = ImMax(column_width_ideal + padding_auto_x, min_column_width);
+ table->ColumnsAutoFitWidth += column_width_ideal;
+
if (column->Flags & (ImGuiTableColumnFlags_WidthAlwaysAutoResize | ImGuiTableColumnFlags_WidthFixed))
{
// Latch initial size for fixed columns
count_fixed += 1;
- const bool init_size = (column->AutoFitQueue != 0x00) || (column->Flags & ImGuiTableColumnFlags_WidthAlwaysAutoResize);
- if (init_size)
+ const bool auto_fit = (column->AutoFitQueue != 0x00) || (column->Flags & ImGuiTableColumnFlags_WidthAlwaysAutoResize);
+ if (auto_fit)
{
- // Combine width from regular rows + width from headers unless requested not to
- float width_request = (float)ImMax(column->ContentWidthRowsFrozen, column->ContentWidthRowsUnfrozen);
- if (!(table->Flags & ImGuiTableFlags_NoHeadersWidth) && !(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth))
- width_request = ImMax(width_request, (float)column->ContentWidthHeadersDesired);
- column->WidthRequested = ImMax(width_request + padding_auto_x, min_column_width);
+ column->WidthRequest = column_width_ideal;
- // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets (e.g. TextWrapped) too much.
- // Otherwise what tends to happen is that TextWrapped would output a very large height (= first frame scrollbar display very off + clipper would skip lots of items)
+ // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
+ // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very
+ // large height (= first frame scrollbar display very off + clipper would skip lots of items).
// This is merely making the side-effect less extreme, but doesn't properly fixes it.
+ // FIXME: Move this to ->WidthGiven to avoid temporary lossyless?
if (column->AutoFitQueue > 0x01 && table->IsInitializing)
- column->WidthRequested = ImMax(column->WidthRequested, min_column_width * 4.0f);
+ column->WidthRequest = ImMax(column->WidthRequest, min_column_width * 4.0f);
}
- width_fixed += column->WidthRequested;
+ sum_width_fixed_requests += column->WidthRequest;
}
else
{
IM_ASSERT(column->Flags & ImGuiTableColumnFlags_WidthStretch);
- const int init_size = (column->ResizeWeight < 0.0f);
+ const int init_size = (column->WidthStretchWeight < 0.0f);
if (init_size)
- column->ResizeWeight = 1.0f;
- total_weights += column->ResizeWeight;
+ column->WidthStretchWeight = 1.0f;
+ sum_weights_stretched += column->WidthStretchWeight;
if (table->LeftMostStretchedColumnDisplayOrder == -1)
table->LeftMostStretchedColumnDisplayOrder = (ImS8)column->DisplayOrder;
}
}
+ // CellSpacingX is >0.0f when there's no vertical border, in which case we add two extra CellSpacingX to make auto-fit look nice instead of cramped.
+ // We may want to expose this somehow.
+ table->ColumnsAutoFitWidth += spacing_auto_x * (table->ColumnsVisibleCount - 1);
+
// Layout
- // Remove -1.0f to cancel out the +1.0f we are doing in EndTable() to make last column line visible
- const float width_spacings = table->CellSpacingX * (table->ColumnsActiveCount - 1);
+ const float width_spacings = table->CellSpacingX * (table->ColumnsVisibleCount - 1);
float width_avail;
- if ((table->Flags & ImGuiTableFlags_ScrollX) && (table->InnerWidth == 0.0f))
- width_avail = table->InnerClipRect.GetWidth() - width_spacings - 1.0f;
+ if ((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f)
+ width_avail = table->InnerClipRect.GetWidth() - width_spacings;
else
- width_avail = work_rect.GetWidth() - width_spacings - 1.0f;
- const float width_avail_for_stretched_columns = width_avail - width_fixed;
+ width_avail = work_rect.GetWidth() - width_spacings;
+ const float width_avail_for_stretched_columns = width_avail - sum_width_fixed_requests;
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
// Apply final width based on requested widths
@@ -8268,7 +8467,7 @@
table->ColumnsTotalWidth = width_spacings;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if (!(table->VisibleMaskByDisplayOrder & ((ImU64)1 << order_n)))
continue;
ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];
@@ -8275,20 +8474,22 @@
// Allocate width for stretched/weighted columns
if (column->Flags & ImGuiTableColumnFlags_WidthStretch)
{
- float weight_ratio = column->ResizeWeight / total_weights;
- column->WidthRequested = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, min_column_width) + 0.01f);
- width_remaining_for_stretched_columns -= column->WidthRequested;
+ // WidthStretchWeight gets converted into WidthRequest
+ float weight_ratio = column->WidthStretchWeight / sum_weights_stretched;
+ column->WidthRequest = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, min_column_width) + 0.01f);
+ width_remaining_for_stretched_columns -= column->WidthRequest;
- // [Resize Rule 2] Resizing from right-side of a weighted column before a fixed column froward sizing to left-side of fixed column
- // We also need to copy the NoResize flag..
- if (column->NextActiveColumn != -1)
- if (ImGuiTableColumn* next_column = &table->Columns[column->NextActiveColumn])
+ // [Resize Rule 2] Resizing from right-side of a weighted column preceding a fixed column
+ // needs to forward resizing to left-side of fixed column. We also need to copy the NoResize flag..
+ if (column->NextVisibleColumn != -1)
+ if (ImGuiTableColumn* next_column = &table->Columns[column->NextVisibleColumn])
if (next_column->Flags & ImGuiTableColumnFlags_WidthFixed)
column->Flags |= (next_column->Flags & ImGuiTableColumnFlags_NoDirectResize_);
}
- // [Resize Rule 1] The right-most active column is not resizable if there is at least one Stretch column (see comments in TableResizeColumn().)
- if (column->NextActiveColumn == -1 && table->LeftMostStretchedColumnDisplayOrder != -1)
+ // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column
+ // (see comments in TableResizeColumn())
+ if (column->NextVisibleColumn == -1 && table->LeftMostStretchedColumnDisplayOrder != -1)
column->Flags |= ImGuiTableColumnFlags_NoDirectResize_;
if (!(column->Flags & ImGuiTableColumnFlags_NoResize))
@@ -8295,7 +8496,7 @@
count_resizable++;
// Assign final width, record width in case we will need to shrink
- column->WidthGiven = ImFloor(ImMax(column->WidthRequested, min_column_width));
+ column->WidthGiven = ImFloor(ImMax(column->WidthRequest, min_column_width));
table->ColumnsTotalWidth += column->WidthGiven;
}
@@ -8306,15 +8507,15 @@
// Shrink widths when the total does not fit
// FIXME-TABLE: This is working but confuses/conflicts with manual resizing.
// FIXME-TABLE: Policy to shrink down below below ideal/requested width if there's no room?
- g.ShrinkWidthBuffer.resize(table->ColumnsActiveCount);
- for (int order_n = 0, active_n = 0; order_n < table->ColumnsCount; order_n++)
+ g.ShrinkWidthBuffer.resize(table->ColumnsVisibleCount);
+ for (int order_n = 0, visible_n = 0; order_n < table->ColumnsCount; order_n++)
{
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if (!(table->VisibleMaskByDisplayOrder & ((ImU64)1 << order_n)))
continue;
const int column_n = table->DisplayOrder[order_n];
- g.ShrinkWidthBuffer[active_n].Index = column_n;
- g.ShrinkWidthBuffer[active_n].Width = table->Columns[column_n].WidthGiven;
- active_n++;
+ g.ShrinkWidthBuffer[visible_n].Index = column_n;
+ g.ShrinkWidthBuffer[visible_n].Width = table->Columns[column_n].WidthGiven;
+ visible_n++;
}
ShrinkWidths(g.ShrinkWidthBuffer.Data, g.ShrinkWidthBuffer.Size, width_excess);
for (int n = 0; n < g.ShrinkWidthBuffer.Size; n++)
@@ -8324,25 +8525,30 @@
else
#endif
- // Redistribute remainder width due to rounding (remainder width is < 1.0f * number of Stretch column)
- // Using right-to-left distribution (more likely to match resizing cursor), could be adjusted depending where the mouse cursor is and/or relative weights.
+ // Redistribute remainder width due to rounding (remainder width is < 1.0f * number of Stretch column).
+ // Using right-to-left distribution (more likely to match resizing cursor), could be adjusted depending
+ // on where the mouse cursor is and/or relative weights.
// FIXME-TABLE: May be simpler to store floating width and floor final positions only
// FIXME-TABLE: Make it optional? User might prefer to preserve pixel perfect same size?
if (width_remaining_for_stretched_columns >= 1.0f)
- for (int order_n = table->ColumnsCount - 1; total_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
+ for (int order_n = table->ColumnsCount - 1; sum_weights_stretched > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
{
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if (!(table->VisibleMaskByDisplayOrder & ((ImU64)1 << order_n)))
continue;
ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];
if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch))
continue;
- column->WidthRequested += 1.0f;
+ column->WidthRequest += 1.0f;
column->WidthGiven += 1.0f;
width_remaining_for_stretched_columns -= 1.0f;
}
+ // Detect hovered column
+ const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table->LastOuterHeight));
+ const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0);
+
// Setup final position, offset and clipping rectangles
- int active_n = 0;
+ int visible_n = 0;
float offset_x = (table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x;
ImRect host_clip_rect = table->InnerClipRect;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
@@ -8350,13 +8556,13 @@
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
- if (table->FreezeColumnsCount > 0 && table->FreezeColumnsCount == active_n)
+ if (table->FreezeColumnsCount > 0 && table->FreezeColumnsCount == visible_n)
offset_x += work_rect.Min.x - table->OuterRect.Min.x;
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if ((table->VisibleMaskByDisplayOrder & ((ImU64)1 << order_n)) == 0)
{
// Hidden column: clear a few fields and we are done with it for the remainder of the function.
- // We set a zero-width clip rect however we pay attention to set Min.y/Max.y properly to not interfere with the clipper.
+ // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.
column->MinX = column->MaxX = offset_x;
column->StartXRows = column->StartXHeaders = offset_x;
column->WidthGiven = 0.0f;
@@ -8378,10 +8584,11 @@
}
else
{
- // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make sure they are all visible.
- // Because of this we also know that all of the columns will always fit in table->WorkRect and therefore in table->InnerRect (because ScrollX is off)
+ // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make
+ // sure they are all visible. Because of this we also know that all of the columns will always fit in
+ // table->WorkRect and therefore in table->InnerRect (because ScrollX is off)
if (!(table->Flags & ImGuiTableFlags_NoKeepColumnsVisible))
- max_x = table->WorkRect.Max.x - (table->ColumnsActiveCount - (column->IndexWithinActiveSet + 1)) * min_column_width;
+ max_x = table->WorkRect.Max.x - (table->ColumnsVisibleCount - (column->IndexWithinVisibleSet + 1)) * min_column_width;
}
if (offset_x + column->WidthGiven > max_x)
column->WidthGiven = ImMax(max_x - offset_x, min_column_width);
@@ -8397,31 +8604,32 @@
column->ClipRect.ClipWithFull(host_clip_rect);
column->IsClipped = (column->ClipRect.Max.x <= column->ClipRect.Min.x) && (column->AutoFitQueue & 1) == 0 && (column->CannotSkipItemsQueue & 1) == 0;
- column->SkipItems = column->IsClipped || table->HostSkipItems;
if (column->IsClipped)
- {
- // Columns with the _WidthAlwaysAutoResize sizing policy will never be updated then.
- table->VisibleMaskByIndex &= ~((ImU64)1 << column_n);
- }
- else
- {
- // Starting cursor position
- column->StartXRows = column->StartXHeaders = column->MinX + table->CellPaddingX1;
+ table->VisibleUnclippedMaskByIndex &= ~((ImU64)1 << column_n); // Columns with the _WidthAlwaysAutoResize sizing policy will never be updated then.
- // Alignment
- // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in many cases.
- // (To be able to honor this we might be able to store a log of cells width, per row, for visible rows, but nav/programmatic scroll would have visible artifacts.)
- //if (column->Flags & ImGuiTableColumnFlags_AlignRight)
- // column->StartXRows = ImMax(column->StartXRows, column->MaxX - column->ContentWidthRowsUnfrozen);
- //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter)
- // column->StartXRows = ImLerp(column->StartXRows, ImMax(column->StartXRows, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
+ column->SkipItems = !column->IsVisible || table->HostSkipItems;
- // Reset content width variables
- const float initial_max_pos_x = column->MinX + table->CellPaddingX1;
- column->ContentMaxPosRowsFrozen = column->ContentMaxPosRowsUnfrozen = initial_max_pos_x;
- column->ContentMaxPosHeadersUsed = column->ContentMaxPosHeadersDesired = initial_max_pos_x;
- }
+ // Detect hovered column
+ if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x)
+ table->HoveredColumnBody = (ImS8)column_n;
+ // Starting cursor position
+ column->StartXRows = column->StartXHeaders = column->MinX + table->CellPaddingX1;
+
+ // Alignment
+ // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
+ // many cases (to be able to honor this we might be able to store a log of cells width, per row, for
+ // visible rows, but nav/programmatic scroll would have visible artifacts.)
+ //if (column->Flags & ImGuiTableColumnFlags_AlignRight)
+ // column->StartXRows = ImMax(column->StartXRows, column->MaxX - column->ContentWidthRowsUnfrozen);
+ //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter)
+ // column->StartXRows = ImLerp(column->StartXRows, ImMax(column->StartXRows, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
+
+ // Reset content width variables
+ const float initial_max_pos_x = column->MinX + table->CellPaddingX1;
+ column->ContentMaxPosRowsFrozen = column->ContentMaxPosRowsUnfrozen = initial_max_pos_x;
+ column->ContentMaxPosHeadersUsed = column->ContentMaxPosHeadersIdeal = initial_max_pos_x;
+
// Don't decrement auto-fit counters until container window got a chance to submit its items
if (table->HostSkipItems == false)
{
@@ -8429,14 +8637,25 @@
column->CannotSkipItemsQueue >>= 1;
}
- if (active_n < table->FreezeColumnsCount)
+ if (visible_n < table->FreezeColumnsCount)
host_clip_rect.Min.x = ImMax(host_clip_rect.Min.x, column->MaxX + 2.0f);
offset_x += column->WidthGiven + table->CellSpacingX;
- active_n++;
+ visible_n++;
}
- // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either because of using _WidthAlwaysAutoResize/_WidthStretch)
+ // Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
+ if (is_hovering_table && table->HoveredColumnBody == -1)
+ {
+ float unused_x1 = table->WorkRect.Min.x;
+ if (table->RightMostVisibleColumn != -1)
+ unused_x1 = ImMax(unused_x1, table->Columns[table->RightMostVisibleColumn].ClipRect.Max.x);
+ if (g.IO.MousePos.x >= unused_x1)
+ table->HoveredColumnBody = (ImS8)table->ColumnsCount;
+ }
+
+ // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag,
+ // either because of using _WidthAlwaysAutoResize/_WidthStretch).
// This will hide the resizing option from the context menu.
if (count_resizable == 0 && (table->Flags & ImGuiTableFlags_Resizable))
table->Flags &= ~ImGuiTableFlags_Resizable;
@@ -8454,11 +8673,12 @@
table->IsUsingHeaders = false;
// Context menu
- if (table->IsContextPopupOpen && table->InstanceNo == table->InstanceInteracted)
+ if (table->IsContextPopupOpen && table->InstanceCurrent == table->InstanceInteracted)
{
- if (BeginPopup("##TableContextMenu"))
+ const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID);
+ if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings))
{
- TableDrawContextMenu(table, table->ContextPopupColumn);
+ TableDrawContextMenu(table);
EndPopup();
}
else
@@ -8473,19 +8693,25 @@
table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 1);
else
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
+
+ // Sanitize and build sort specs before we have a change to use them for display.
+ // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change)
+ if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable))
+ TableSortSpecsBuild(table);
}
// Process interaction on resizing borders. Actual size change will be applied in EndTable()
// - Set table->HoveredColumnBorder with a short delay/timer to reduce feedback noise
-// - Submit ahead of table contents and header, use ImGuiButtonFlags_AllowItemOverlap to prioritize widgets overlapping the same area.
+// - Submit ahead of table contents and header, use ImGuiButtonFlags_AllowItemOverlap to prioritize widgets
+// overlapping the same area.
void ImGui::TableUpdateBorders(ImGuiTable* table)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(table->Flags & ImGuiTableFlags_Resizable);
- // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and use
- // the final height from last frame. Because this is only affecting _interaction_ with columns, it is not really problematic.
- // (whereas the actual visual will be displayed in EndTable() and using the current frame height)
+ // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and
+ // use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not
+ // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
// Actual columns highlight/render will be performed in EndTable() and not be affected.
const bool borders_full_height = (table->IsUsingHeaders == false) || (table->Flags & ImGuiTableFlags_BordersVFullHeight);
const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS;
@@ -8492,26 +8718,18 @@
const float hit_y1 = table->OuterRect.Min.y;
const float hit_y2_full = ImMax(table->OuterRect.Max.y, hit_y1 + table->LastOuterHeight);
const float hit_y2 = borders_full_height ? hit_y2_full : (hit_y1 + table->LastFirstRowHeight);
- const float mouse_x_hover_body = (g.IO.MousePos.y >= hit_y1 && g.IO.MousePos.y < hit_y2_full) ? g.IO.MousePos.x : FLT_MAX;
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if (!(table->VisibleMaskByDisplayOrder & ((ImU64)1 << order_n)))
continue;
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
-
- // Detect hovered column:
- // - we perform an unusually low-level check here.. not using IsMouseHoveringRect() to avoid touch padding.
- // - we don't care about the full set of IsItemHovered() feature either.
- if (mouse_x_hover_body >= column->MinX && mouse_x_hover_body < column->MaxX)
- table->HoveredColumnBody = (ImS8)column_n;
-
if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_))
continue;
- ImGuiID column_id = table->ID + (table->InstanceNo * table->ColumnsCount) + column_n;
+ ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent);
ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, hit_y2);
//GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));
KeepAliveID(column_id);
@@ -8528,7 +8746,7 @@
if (held)
{
table->ResizedColumn = (ImS8)column_n;
- table->InstanceInteracted = table->InstanceNo;
+ table->InstanceInteracted = table->InstanceCurrent;
}
if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held)
{
@@ -8544,8 +8762,8 @@
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!");
- // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some cases,
- // and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
+ // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
+ // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
//IM_ASSERT(table->IsLayoutLocked && "Table unused: never called TableNextRow(), is that the intent?");
// If the user never got to call TableNextRow() or TableNextCell(), we call layout ourselves to ensure all our
@@ -8578,8 +8796,19 @@
table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y);
table->LastOuterHeight = table->OuterRect.GetHeight();
- // Store content width reference for each column
- float max_pos_x = inner_window->DC.CursorMaxPos.x;
+ if (!(flags & ImGuiTableFlags_NoClipX))
+ inner_window->DrawList->PopClipRect();
+ inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back();
+
+ // Draw borders
+ if ((flags & ImGuiTableFlags_Borders) != 0)
+ TableDrawBorders(table);
+
+ // Store content width reference for each column (before attempting to merge draw calls)
+ const float backup_outer_cursor_pos_x = outer_window->DC.CursorPos.x;
+ const float backup_outer_max_pos_x = outer_window->DC.CursorMaxPos.x;
+ const float backup_inner_max_pos_x = inner_window->DC.CursorMaxPos.x;
+ float max_pos_x = backup_inner_max_pos_x;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
@@ -8591,26 +8820,18 @@
column->ContentWidthRowsFrozen = (ImS16)ImMax(0.0f, column->ContentMaxPosRowsFrozen - ref_x_rows);
column->ContentWidthRowsUnfrozen = (ImS16)ImMax(0.0f, column->ContentMaxPosRowsUnfrozen - ref_x_rows);
column->ContentWidthHeadersUsed = (ImS16)ImMax(0.0f, column->ContentMaxPosHeadersUsed - ref_x_headers);
- column->ContentWidthHeadersDesired = (ImS16)ImMax(0.0f, column->ContentMaxPosHeadersDesired - ref_x_headers);
+ column->ContentWidthHeadersIdeal = (ImS16)ImMax(0.0f, column->ContentMaxPosHeadersIdeal - ref_x_headers);
- if (table->ActiveMaskByIndex & ((ImU64)1 << column_n))
+ // Add an extra 1 pixel so we can see the last column vertical line if it lies on the right-most edge.
+ if (table->VisibleMaskByIndex & ((ImU64)1 << column_n))
max_pos_x = ImMax(max_pos_x, column->MaxX);
}
- // Add an extra 1 pixel so we can see the last column vertical line if it lies on the right-most edge.
- inner_window->DC.CursorMaxPos.x = max_pos_x + 1;
-
- if (!(flags & ImGuiTableFlags_NoClipX))
- inner_window->DrawList->PopClipRect();
- inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back();
-
- // Draw borders
- if ((flags & ImGuiTableFlags_Borders) != 0)
- TableDrawBorders(table);
-
// Flatten channels and merge draw calls
table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0);
- TableDrawMergeChannels(table);
+ if ((table->Flags & ImGuiTableFlags_NoClipX) == 0)
+ TableReorderDrawChannelsForMerge(table);
+ table->DrawSplitter.Merge(inner_window->DrawList);
// When releasing a column being resized, scroll to keep the resulting column in sight
const float min_column_width = TableGetMinColumnWidth();
@@ -8618,7 +8839,7 @@
{
inner_window->Scroll.x = 0.0f;
}
- else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceNo)
+ else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceCurrent)
{
ImGuiTableColumn* column = &table->Columns[table->LastResizedColumn];
if (column->MaxX < table->InnerClipRect.Min.x)
@@ -8637,6 +8858,8 @@
}
// Layout in outer window
+ IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!");
+ PopID();
inner_window->WorkRect = table->HostWorkRect;
inner_window->SkipItems = table->HostSkipItems;
outer_window->DC.CursorPos = table->OuterRect.Min;
@@ -8643,19 +8866,27 @@
outer_window->DC.ColumnsOffset.x = 0.0f;
if (inner_window != outer_window)
{
- // Override EndChild's ItemSize with our own to enable auto-resize on the X axis when possible
- float backup_outer_cursor_pos_x = outer_window->DC.CursorPos.x;
EndChild();
- outer_window->DC.CursorMaxPos.x = backup_outer_cursor_pos_x + table->ColumnsTotalWidth + 1.0f + inner_window->ScrollbarSizes.x;
}
else
{
- PopID();
ImVec2 item_size = table->OuterRect.GetSize();
item_size.x = table->ColumnsTotalWidth;
ItemSize(item_size);
}
+ // Override EndChild/ItemSize max extent with our own to enable auto-resize on the X axis when possible
+ // FIXME-TABLE: This can be improved (e.g. for Fixed columns we don't want to auto AutoFitWidth? or propagate window auto-fit to table?)
+ if (table->Flags & ImGuiTableFlags_ScrollX)
+ {
+ inner_window->DC.CursorMaxPos.x = max_pos_x; // Set contents width for scrolling
+ outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos_x, backup_outer_cursor_pos_x + table->ColumnsTotalWidth + inner_window->ScrollbarSizes.x); // For auto-fit
+ }
+ else
+ {
+ outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos_x, table->WorkRect.Min.x + table->ColumnsAutoFitWidth); // For auto-fit
+ }
+
// Save settings
if (table->IsSettingsDirty)
TableSaveSettings(table);
@@ -8680,6 +8911,7 @@
ImDrawList* outer_drawlist = outer_window->DrawList;
// Draw inner border and resizing feedback
+ const float border_size = TABLE_BORDER_SIZE;
const float draw_y1 = table->OuterRect.Min.y;
float draw_y2_base = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->LastFirstRowHeight;
float draw_y2_full = table->OuterRect.Max.y;
@@ -8695,22 +8927,22 @@
}
if ((table->Flags & ImGuiTableFlags_BordersVOuter) && (table->InnerWindow == table->OuterWindow))
- inner_drawlist->AddLine(ImVec2(table->OuterRect.Min.x, draw_y1), ImVec2(table->OuterRect.Min.x, draw_y2_base), border_base_col, 1.0f);
+ inner_drawlist->AddLine(ImVec2(table->OuterRect.Min.x, draw_y1), ImVec2(table->OuterRect.Min.x, draw_y2_base), border_base_col, border_size);
if (table->Flags & ImGuiTableFlags_BordersVInner)
{
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
{
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if (!(table->VisibleMaskByDisplayOrder & ((ImU64)1 << order_n)))
continue;
const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
const bool is_hovered = (table->HoveredColumnBorder == column_n);
- const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceNo);
+ const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);
const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0;
bool draw_right_border = (column->MaxX <= table->InnerClipRect.Max.x) || (is_resized || is_hovered);
- if (column->NextActiveColumn == -1 && !is_resizable)
+ if (column->NextVisibleColumn == -1 && !is_resizable)
draw_right_border = false;
if (draw_right_border && column->MaxX > column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size..
{
@@ -8724,7 +8956,7 @@
float draw_y2 = draw_y2_base;
if (is_hovered || is_resized || (table->FreezeColumnsCount != -1 && table->FreezeColumnsCount == order_n + 1))
draw_y2 = draw_y2_full;
- inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), col, 1.0f);
+ inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), col, border_size);
}
}
}
@@ -8733,9 +8965,9 @@
if (table->Flags & ImGuiTableFlags_BordersOuter)
{
// Display outer border offset by 1 which is a simple way to display it without adding an extra draw call
- // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their parent.
- // In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part of it in inner window,
- // and the part that's over scrollbars in the outer window..)
+ // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their
+ // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part
+ // of it in inner window, and the part that's over scrollbars in the outer window..)
// Either solution currently won't allow us to use a larger border size: the border would clipped.
ImRect outer_border = table->OuterRect;
const ImU32 outer_col = table->BorderColorStrong;
@@ -8742,16 +8974,18 @@
if (inner_window != outer_window)
outer_border.Expand(1.0f);
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
- outer_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col);
+ {
+ outer_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, ~0, border_size);
+ }
else if (table->Flags & ImGuiTableFlags_BordersVOuter)
{
- outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col);
- outer_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col);
+ outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size);
+ outer_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size);
}
else if (table->Flags & ImGuiTableFlags_BordersHOuter)
{
- outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col);
- outer_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col);
+ outer_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size);
+ outer_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size);
}
}
if ((table->Flags & ImGuiTableFlags_BordersHInner) && table->RowPosY2 < table->OuterRect.Max.y)
@@ -8759,7 +8993,7 @@
// Draw bottom-most row border
const float border_y = table->RowPosY2;
if (border_y >= table->BackgroundClipRect.Min.y && border_y < table->BackgroundClipRect.Max.y)
- inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight);
+ inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size);
}
}
@@ -8773,10 +9007,10 @@
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
- if (!column->IsActive || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
+ if (!column->IsVisible || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
continue;
- visible_weight += column->ResizeWeight;
- visible_width += column->WidthRequested;
+ visible_weight += column->WidthStretchWeight;
+ visible_width += column->WidthRequest;
}
IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f);
@@ -8784,9 +9018,9 @@
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
- if (!column->IsActive || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
+ if (!column->IsVisible || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
continue;
- column->ResizeWeight = (column->WidthRequested + 0.0f) / visible_width;
+ column->WidthStretchWeight = ((column->WidthRequest + 0.0f) / visible_width) * visible_weight;
}
}
@@ -8808,14 +9042,14 @@
float min_width = TableGetMinColumnWidth();
float max_width_0 = FLT_MAX;
if (!(table->Flags & ImGuiTableFlags_ScrollX))
- max_width_0 = (table->WorkRect.Max.x - column_0->MinX) - (table->ColumnsActiveCount - (column_0->IndexWithinActiveSet + 1)) * min_width;
+ max_width_0 = (table->WorkRect.Max.x - column_0->MinX) - (table->ColumnsVisibleCount - (column_0->IndexWithinVisibleSet + 1)) * min_width;
column_0_width = ImClamp(column_0_width, min_width, max_width_0);
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
- if (column_0->WidthGiven == column_0_width || column_0->WidthRequested == column_0_width)
+ if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return;
- ImGuiTableColumn* column_1 = (column_0->NextActiveColumn != -1) ? &table->Columns[column_0->NextActiveColumn] : NULL;
+ ImGuiTableColumn* column_1 = (column_0->NextVisibleColumn != -1) ? &table->Columns[column_0->NextVisibleColumn] : NULL;
// In this surprisingly not simple because of how we support mixing Fixed and Stretch columns.
// When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.
@@ -8837,25 +9071,25 @@
// Rules:
// - [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().
- // - [Resize Rule 2] Resizing from right-side of a Stretch column before a fixed column froward sizing to left-side of fixed column.
+ // - [Resize Rule 2] Resizing from right-side of a Stretch column before a fixed column forward sizing to left-side of fixed column.
// - [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure that our left border won't move.
-
+ table->IsSettingsDirty = true;
if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed)
{
- // [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to
- // ensure that our left border won't move, which we can do by making sure column_a/column_b resizes cancels each others.
+ // [Resize Rule 3] If we are are followed by a fixed column and we have a Stretch column before, we need to ensure
+ // that our left border won't move, which we can do by making sure column_a/column_b resizes cancels each others.
if (column_1 && (column_1->Flags & ImGuiTableColumnFlags_WidthFixed))
if (table->LeftMostStretchedColumnDisplayOrder != -1 && table->LeftMostStretchedColumnDisplayOrder < column_0->DisplayOrder)
{
// (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
- float column_1_width = ImMax(column_1->WidthRequested - (column_0_width - column_0->WidthRequested), min_width);
- column_0_width = column_0->WidthRequested + column_1->WidthRequested - column_1_width;
- column_1->WidthRequested = column_1_width;
+ float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);
+ column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;
+ column_1->WidthRequest = column_1_width;
}
// Apply
//IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthRequested, column_0_width);
- column_0->WidthRequested = column_0_width;
+ column_0->WidthRequest = column_0_width;
}
else if (column_0->Flags & ImGuiTableColumnFlags_WidthStretch)
{
@@ -8864,40 +9098,49 @@
{
float off = (column_0->WidthGiven - column_0_width);
float column_1_width = column_1->WidthGiven + off;
- column_1->WidthRequested = ImMax(min_width, column_1_width);
+ column_1->WidthRequest = ImMax(min_width, column_1_width);
return;
}
// (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
- float column_1_width = ImMax(column_1->WidthRequested - (column_0_width - column_0->WidthRequested), min_width);
- column_0_width = column_0->WidthRequested + column_1->WidthRequested - column_1_width;
- column_1->WidthRequested = column_1_width;
- column_0->WidthRequested = column_0_width;
+ float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);
+ column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;
+ column_1->WidthRequest = column_1_width;
+ column_0->WidthRequest = column_0_width;
TableUpdateColumnsWeightFromWidth(table);
}
- table->IsSettingsDirty = true;
}
-// Columns where the contents didn't stray off their local clip rectangle can be merged into a same draw command.
-// To achieve this we merge their clip rect and make them contiguous in the channel list so they can be merged.
-// So here we'll reorder the draw cmd which can be merged, by arranging them into a maximum of 4 distinct groups:
+// This function reorder draw channels based on matching clip rectangle, to facilitate merging them.
//
+// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve
+// this we merge their clip rect and make them contiguous in the channel list, so they can be merged
+// by the call to DrawSplitter.Merge() following to the call to this function.
+//
+// We reorder draw commands by arranging them into a maximum of 4 distinct groups:
+//
// 1 group: 2 groups: 2 groups: 4 groups:
-// [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 01 ] row+col freeze
-// [ .. ] or no scroll [ 1. ] and v-scroll [ .. ] and h-scroll [ 23 ] and v+h-scroll
+// [ 0. ] no freeze [ 0. ] row freeze [ 01 ] col freeze [ 02 ] row+col freeze
+// [ .. ] or no scroll [ 1. ] and v-scroll [ .. ] and h-scroll [ 13 ] and v+h-scroll
//
// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled).
// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group
// based on its position (within frozen rows/columns groups or not).
-// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect, and they will be merged by the DrawSplitter.Merge() call.
+// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect.
//
+// This function assume that each column are pointing to a distinct draw channel,
+// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask.
+//
// Column channels will not be merged into one of the 1-4 groups in the following cases:
// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value).
-// Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds matches, by e.g. calling SetCursorScreenPos().
-// - The channel uses more than one draw command itself. We drop all our merging stuff here.. we could do better but it's going to be rare.
+// Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds
+// matches, by e.g. calling SetCursorScreenPos().
+// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here..
+// we could do better but it's going to be rare and probably not worth the hassle.
+// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd.
//
// This function is particularly tricky to understand.. take a breath.
-void ImGui::TableDrawMergeChannels(ImGuiTable* table)
+void ImGui::TableReorderDrawChannelsForMerge(ImGuiTable* table)
{
ImGuiContext& g = *GImGui;
ImDrawListSplitter* splitter = &table->DrawSplitter;
@@ -8917,11 +9160,10 @@
bool merge_groups_all_fit_within_inner_rect = (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0;
// 1. Scan channels and take note of those which can be merged
- for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
+ for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
- if (!(table->ActiveMaskByDisplayOrder & ((ImU64)1 << order_n)))
+ if (!(table->VisibleUnclippedMaskByIndex & ((ImU64)1 << column_n)))
continue;
- const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n];
const int merge_group_sub_count = is_frozen_v ? 2 : 1;
@@ -8937,15 +9179,18 @@
continue;
// Find out the width of this merge group and check if it will fit in our column.
- float width_contents;
- if (merge_group_sub_count == 1) // No row freeze (same as testing !is_frozen_v)
- width_contents = ImMax(column->ContentWidthRowsUnfrozen, column->ContentWidthHeadersUsed);
- else if (merge_group_sub_n == 0) // Row freeze: use width before freeze
- width_contents = ImMax(column->ContentWidthRowsFrozen, column->ContentWidthHeadersUsed);
- else // Row freeze: use width after freeze
- width_contents = column->ContentWidthRowsUnfrozen;
- if (width_contents > column->WidthGiven && !(column->Flags & ImGuiTableColumnFlags_NoClipX))
- continue;
+ if (!(column->Flags & ImGuiTableColumnFlags_NoClipX))
+ {
+ float width_contents;
+ if (merge_group_sub_count == 1) // No row freeze (same as testing !is_frozen_v)
+ width_contents = ImMax(column->ContentWidthRowsUnfrozen, column->ContentWidthHeadersUsed);
+ else if (merge_group_sub_n == 0) // Row freeze: use width before freeze
+ width_contents = ImMax(column->ContentWidthRowsFrozen, column->ContentWidthHeadersUsed);
+ else // Row freeze: use width after freeze
+ width_contents = column->ContentWidthRowsUnfrozen;
+ if (width_contents > column->WidthGiven)
+ continue;
+ }
const int merge_group_dst_n = (is_frozen_h && column_n < table->FreezeColumnsCount ? 0 : 2) + (is_frozen_v ? merge_group_sub_n : 1);
IM_ASSERT(channel_no < IMGUI_TABLE_MAX_DRAW_CHANNELS);
@@ -8957,13 +9202,13 @@
merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);
merge_group_mask |= (1 << merge_group_dst_n);
- // If we end with a single group and hosted by the outer window, we'll attempt to merge our draw command with
- // the existing outer window command. But we can only do so if our columns all fit within the expected clip rect,
- // otherwise clipping will be incorrect when ScrollX is disabled.
+ // If we end with a single group and hosted by the outer window, we'll attempt to merge our draw command
+ // with the existing outer window command. But we can only do so if our columns all fit within the expected
+ // clip rect, otherwise clipping will be incorrect when ScrollX is disabled.
// FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column don't fit within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect
- // 2019/10/22: (1) This is breaking table_2_draw_calls but I cannot seem to repro what it is attempting to fix...
- // cf git fce2e8dc "Fixed issue with clipping when outerwindow==innerwindow / support ScrollH without ScrollV."
+ // 2019/10/22: (1) This is breaking table_2_draw_calls but I cannot seem to repro what it is attempting to
+ // fix... cf git fce2e8dc "Fixed issue with clipping when outerwindow==innerwindow / support ScrollH without ScrollV."
// 2019/10/22: (2) Clamping code in TableUpdateLayout() seemingly made this not necessary...
#if 0
if (column->MinX < table->InnerClipRect.Min.x || column->MaxX > table->InnerClipRect.Max.x)
@@ -8971,22 +9216,44 @@
#endif
}
- // Invalidate current draw channel (we don't clear DrawChannelBeforeRowFreeze/DrawChannelAfterRowFreeze solely to facilitate debugging)
+ // Invalidate current draw channel
+ // (we don't clear DrawChannelBeforeRowFreeze/DrawChannelAfterRowFreeze solely to facilitate debugging/later inspection of data)
column->DrawChannelCurrent = -1;
}
+ // [DEBUG] Display merge groups
+#if 0
+ if (g.IO.KeyShift)
+ for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)
+ {
+ MergeGroup* merge_group = &merge_groups[merge_group_n];
+ if (merge_group->ChannelsCount == 0)
+ continue;
+ char buf[32];
+ ImFormatString(buf, 32, "MG%d:%d", merge_group_n, merge_group->ChannelsCount);
+ ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4);
+ ImVec2 text_size = CalcTextSize(buf, NULL);
+ GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255));
+ GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL);
+ GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255));
+ }
+#endif
+
// 2. Rewrite channel list in our preferred order
if (merge_group_mask != 0)
{
+ // Conceptually we want to test if only 1 bit of merge_group_mask is set, but with no freezing we know it's always going to be group 3.
+ // We need to test for !is_frozen because any unfitting column will not be part of a merge group, so testing for merge_group_mask isn't enough.
+ const bool may_extend_clip_rect_to_host_rect = (merge_group_mask == (1 << 3)) && !is_frozen_v && !is_frozen_h;
+
// Use shared temporary storage so the allocation gets amortized
g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - 1);
ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
- ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> remaining_mask;
+ ImBitArray<IMGUI_TABLE_MAX_DRAW_CHANNELS> remaining_mask; // We need 130-bit of storage
remaining_mask.ClearBits();
remaining_mask.SetBitRange(1, splitter->_Count - 1); // Background channel 0 not part of the merge (see channel allocation in TableUpdateDrawChannels)
int remaining_count = splitter->_Count - 1;
- const bool may_extend_clip_rect_to_host_rect = ImIsPowerOfTwo(merge_group_mask);
- for (int merge_group_n = 0; merge_group_n < 4; merge_group_n++)
+ for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)
if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount)
{
MergeGroup* merge_group = &merge_groups[merge_group_n];
@@ -8993,6 +9260,7 @@
ImRect merge_clip_rect = merge_groups[merge_group_n].ClipRect;
if (may_extend_clip_rect_to_host_rect)
{
+ IM_ASSERT(merge_group_n == 3);
//GetOverlayDrawList()->AddRect(table->HostClipRect.Min, table->HostClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, ~0, 3.0f);
//GetOverlayDrawList()->AddRect(table->InnerClipRect.Min, table->InnerClipRect.Max, IM_COL32(0, 255, 0, 200), 0.0f, ~0, 1.0f);
//GetOverlayDrawList()->AddRect(merge_clip_rect.Min, merge_clip_rect.Max, IM_COL32(255, 0, 0, 200), 0.0f, ~0, 2.0f);
@@ -9029,9 +9297,6 @@
IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size);
memcpy(splitter->_Channels.Data + 1, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - 1) * sizeof(ImDrawChannel));
}
-
- // 3. Actually merge (channels using the same clip rect will be contiguous and naturally merged)
- splitter->Merge(table->InnerWindow->DrawList);
}
// We use a default parameter of 'init_width_or_weight == -1'
@@ -9051,8 +9316,9 @@
ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount];
table->DeclColumnsCount++;
- // When passing a width automatically enforce WidthFixed policy (vs TableFixColumnFlags would default to WidthAlwaysAutoResize)
- // (we write down to FlagsIn which is a little misleading, another solution would be to pass init_width_or_weight to TableFixColumnFlags)
+ // When passing a width automatically enforce WidthFixed policy
+ // (vs TableFixColumnFlags would default to WidthAlwaysAutoResize)
+ // (we write to FlagsIn which is a little misleading, another solution would be to pass init_width_or_weight to TableFixColumnFlags)
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0)
if ((table->Flags & ImGuiTableFlags_SizingPolicyFixedX) && (init_width_or_weight > 0.0f))
flags |= ImGuiTableColumnFlags_WidthFixed;
@@ -9064,31 +9330,31 @@
// Initialize defaults
// FIXME-TABLE: We don't restore widths/weight so let's avoid using IsSettingsLoaded for now
- if (table->IsInitializing && column->WidthRequested < 0.0f && column->ResizeWeight < 0.0f)// && !table->IsSettingsLoaded)
+ if (table->IsInitializing && column->WidthRequest < 0.0f && column->WidthStretchWeight < 0.0f)// && !table->IsSettingsLoaded)
{
// Init width or weight
// Disable auto-fit if a default fixed width has been specified
if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f)
{
- column->WidthRequested = init_width_or_weight;
+ column->WidthRequest = init_width_or_weight;
column->AutoFitQueue = 0x00;
}
if (flags & ImGuiTableColumnFlags_WidthStretch)
{
IM_ASSERT(init_width_or_weight < 0.0f || init_width_or_weight > 0.0f);
- column->ResizeWeight = (init_width_or_weight < 0.0f ? 1.0f : init_width_or_weight);
+ column->WidthStretchWeight = (init_width_or_weight < 0.0f ? 1.0f : init_width_or_weight);
}
else
{
- column->ResizeWeight = 1.0f;
+ column->WidthStretchWeight = 1.0f;
}
}
- if (table->IsInitializing && !table->IsSettingsLoaded)
+ if (table->IsInitializing)
{
// Init default visibility/sort state
- if (flags & ImGuiTableColumnFlags_DefaultHide)
- column->IsActive = column->IsActiveNextFrame = false;
- if (flags & ImGuiTableColumnFlags_DefaultSort)
+ if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0)
+ column->IsVisible = column->IsVisibleNextFrame = false;
+ if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0)
{
column->SortOrder = 0; // Multiple columns using _DefaultSort will be reordered when building the sort specs.
column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);
@@ -9170,8 +9436,8 @@
TableEndCell(table);
- // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation.
- // However it is likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding.
+ // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is
+ // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding.
window->DC.CursorPos.y = table->RowPosY2;
// Row background fill
@@ -9178,6 +9444,8 @@
const float bg_y1 = table->RowPosY1;
const float bg_y2 = table->RowPosY2;
+ const bool unfreeze_rows = (table->CurrentRow + 1 == table->FreezeRowsCount && table->FreezeRowsCount > 0);
+
if (table->CurrentRow == 0)
table->LastFirstRowHeight = bg_y2 - bg_y1;
@@ -9192,6 +9460,7 @@
// Decide of top border color
ImU32 border_col = 0;
+ const float border_size = TABLE_BORDER_SIZE;
if (table->CurrentRow != 0 || table->InnerWindow == table->OuterWindow)
{
if (table->Flags & ImGuiTableFlags_BordersHInner)
@@ -9209,8 +9478,14 @@
}
}
- if (bg_col != 0 || border_col != 0)
+ const bool draw_stong_bottom_border = unfreeze_rows;// || (table->RowFlags & ImGuiTableRowFlags_Headers);
+ if (bg_col != 0 || border_col != 0 || draw_stong_bottom_border)
+ {
+ // In theory we could call SetWindowClipRectBeforeChannelChange() but since we know TableEndRow() is
+ // always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
+ window->DrawList->_CmdHeader.ClipRect = table->HostClipRect.ToVec4();
table->DrawSplitter.SetCurrentChannel(window->DrawList, 0);
+ }
// Draw background
// We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle
@@ -9223,21 +9498,18 @@
}
// Draw top border
- const float border_y = bg_y1;
- if (border_col && border_y >= table->BackgroundClipRect.Min.y && border_y < table->BackgroundClipRect.Max.y)
- window->DrawList->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), border_col);
+ if (border_col && bg_y1 >= table->BackgroundClipRect.Min.y && bg_y1 < table->BackgroundClipRect.Max.y)
+ window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), border_col, border_size);
+
+ // Draw bottom border at the row unfreezing mark (always strong)
+ if (draw_stong_bottom_border)
+ if (bg_y2 >= table->BackgroundClipRect.Min.y && bg_y2 < table->BackgroundClipRect.Max.y)
+ window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size);
}
- const bool unfreeze_rows = (table->CurrentRow + 1 == table->FreezeRowsCount && table->FreezeRowsCount > 0);
-
- // Draw bottom border (always strong)
- const bool draw_separating_border = unfreeze_rows;// || (table->RowFlags & ImGuiTableRowFlags_Headers);
- if (draw_separating_border)
- if (bg_y2 >= table->BackgroundClipRect.Min.y && bg_y2 < table->BackgroundClipRect.Max.y)
- window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong);
-
// End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
- // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and get the new cursor position.
+ // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
+ // get the new cursor position.
if (unfreeze_rows)
{
IM_ASSERT(table->IsFreezeRowsPassed == false);
@@ -9293,7 +9565,7 @@
window->WorkRect.Max.x = column->MaxX - table->CellPaddingX2;
// To allow ImGuiListClipper to function we propagate our row height
- if (!column->IsActive)
+ if (!column->IsVisible)
window->DC.CursorPos.y = ImMax(window->DC.CursorPos.y, table->RowPosY2);
window->SkipItems = column->SkipItems;
@@ -9303,15 +9575,8 @@
}
else
{
+ SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
- //window->ClipRect = column->ClipRect;
- //IM_ASSERT(column->ClipRect.Max.x > column->ClipRect.Min.x && column->ClipRect.Max.y > column->ClipRect.Min.y);
- //window->DrawList->_ClipRectStack.back() = ImVec4(column->ClipRect.Min.x, column->ClipRect.Min.y, column->ClipRect.Max.x, column->ClipRect.Max.y);
- //window->DrawList->UpdateClipRect();
- window->DrawList->PopClipRect();
- window->DrawList->PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false);
- //IMGUI_DEBUG_LOG("%d (%.0f,%.0f)(%.0f,%.0f)\n", column_n, column->ClipRect.Min.x, column->ClipRect.Min.y, column->ClipRect.Max.x, column->ClipRect.Max.y);
- window->ClipRect = window->DrawList->_ClipRectStack.back();
}
}
@@ -9351,9 +9616,13 @@
{
TableNextRow();
}
-
int column_n = table->CurrentColumn;
- return (table->VisibleMaskByIndex & ((ImU64)1 << column_n)) != 0;
+
+ // FIXME-TABLE: Need to clarify if we want to allow IsItemHovered() here
+ //g.CurrentWindow->DC.LastItemStatusFlags = (column_n == table->HoveredColumn) ? ImGuiItemStatusFlags_HoveredRect : ImGuiItemStatusFlags_None;
+
+ // FIXME-TABLE: it is likely to alter layout if user skips a columns contents based on clipping.
+ return (table->VisibleUnclippedMaskByIndex & ((ImU64)1 << column_n)) != 0;
}
const char* ImGui::TableGetColumnName(int column_n)
@@ -9367,6 +9636,7 @@
return TableGetColumnName(table, column_n);
}
+// We expose "Visible and Unclipped" to the user, vs our internal "Visible" state which is !Hidden
bool ImGui::TableGetColumnIsVisible(int column_n)
{
ImGuiContext& g = *GImGui;
@@ -9375,7 +9645,7 @@
return false;
if (column_n < 0)
column_n = table->CurrentColumn;
- return (table->VisibleMaskByIndex & ((ImU64)1 << column_n)) != 0;
+ return (table->VisibleUnclippedMaskByIndex & ((ImU64)1 << column_n)) != 0;
}
int ImGui::TableGetColumnIndex()
@@ -9402,7 +9672,11 @@
TableBeginCell(table, column_idx);
}
- return (table->VisibleMaskByIndex & ((ImU64)1 << column_idx)) != 0;
+ // FIXME-TABLE: Need to clarify if we want to allow IsItemHovered() here
+ //g.CurrentWindow->DC.LastItemStatusFlags = (column_n == table->HoveredColumn) ? ImGuiItemStatusFlags_HoveredRect : ImGuiItemStatusFlags_None;
+
+ // FIXME-TABLE: it is likely to alter layout if user skips a columns contents based on clipping.
+ return (table->VisibleUnclippedMaskByIndex & ((ImU64)1 << column_idx)) != 0;
}
// Return the cell rectangle based on currently known height.
@@ -9416,14 +9690,22 @@
return ImRect(column->MinX, table->RowPosY1, column->MaxX, table->RowPosY2);
}
-const char* ImGui::TableGetColumnName(ImGuiTable* table, int column_n)
+const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n)
{
- ImGuiTableColumn* column = &table->Columns[column_n];
+ const ImGuiTableColumn* column = &table->Columns[column_n];
if (column->NameOffset == -1)
return NULL;
return &table->ColumnsNames.Buf[column->NameOffset];
}
+// Return the resizing ID for the right-side of the given column.
+ImGuiID ImGui::TableGetColumnResizeID(const ImGuiTable* table, int column_n, int instance_no)
+{
+ IM_ASSERT(column_n < table->ColumnsCount);
+ ImGuiID id = table->ID + (instance_no * table->ColumnsCount) + column_n;
+ return id;
+}
+
void ImGui::TableSetColumnAutofit(ImGuiTable* table, int column_n)
{
// Disable clipping then auto-fit, will take 2 frames
@@ -9438,8 +9720,11 @@
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable;
+
+ // Optimization: avoid SetCurrentChannel() + PushClipRect()
+ table->HostBackupClipRect = window->ClipRect;
+ SetWindowClipRectBeforeSetChannel(window, table->HostClipRect);
table->DrawSplitter.SetCurrentChannel(window->DrawList, 0);
- PushClipRect(table->HostClipRect.Min, table->HostClipRect.Max, false);
}
void ImGui::PopTableBackground()
@@ -9448,13 +9733,15 @@
ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable;
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
+
+ // Optimization: avoid PopClipRect() + SetCurrentChannel()
+ SetWindowClipRectBeforeSetChannel(window, table->HostBackupClipRect);
table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
- PopClipRect();
}
// Output context menu into current window (generally a popup)
// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?
-void ImGui::TableDrawContextMenu(ImGuiTable* table, int selected_column_n)
+void ImGui::TableDrawContextMenu(ImGuiTable* table)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
@@ -9462,7 +9749,7 @@
return;
bool want_separator = false;
- selected_column_n = ImClamp(selected_column_n, -1, table->ColumnsCount - 1);
+ const int selected_column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1;
// Sizing
if (table->Flags & ImGuiTableFlags_Resizable)
@@ -9469,7 +9756,7 @@
{
if (ImGuiTableColumn* selected_column = (selected_column_n != -1) ? &table->Columns[selected_column_n] : NULL)
{
- const bool can_resize = !(selected_column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthStretch)) && selected_column->IsActive;
+ const bool can_resize = !(selected_column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthStretch)) && selected_column->IsVisible;
if (MenuItem("Size column to fit", NULL, false, can_resize))
TableSetColumnAutofit(table, selected_column_n);
}
@@ -9479,7 +9766,7 @@
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
- if (column->IsActive)
+ if (column->IsVisible)
TableSetColumnAutofit(table, column_n);
}
}
@@ -9511,42 +9798,56 @@
// Make sure we can't hide the last active column
bool menu_item_active = (column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true;
- if (column->IsActive && table->ColumnsActiveCount <= 1)
+ if (column->IsVisible && table->ColumnsVisibleCount <= 1)
menu_item_active = false;
- if (MenuItem(name, NULL, column->IsActive, menu_item_active))
- column->IsActiveNextFrame = !column->IsActive;
+ if (MenuItem(name, NULL, column->IsVisible, menu_item_active))
+ column->IsVisibleNextFrame = !column->IsVisible;
}
PopItemFlag();
}
}
+// Use -1 to open menu not specific to a given column.
+void ImGui::TableOpenContextMenu(ImGuiTable* table, int column_n)
+{
+ IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount);
+ if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
+ {
+ table->IsContextPopupOpen = true;
+ table->ContextPopupColumn = (ImS8)column_n;
+ table->InstanceInteracted = table->InstanceCurrent;
+ const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID);
+ OpenPopupEx(context_menu_id, ImGuiPopupFlags_None);
+ }
+}
+
// This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
-// The intent is that advanced users willing to create customized headers would not need to use this helper and may create their own.
-// However presently this function uses too many internal structures/calls.
+// The intent is that advanced users willing to create customized headers would not need to use this helper
+// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
void ImGui::TableAutoHeaders()
{
- ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
+ ImGuiStyle& style = ImGui::GetStyle();
+ ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableAutoHeaders() after BeginTable()!");
const int columns_count = table->ColumnsCount;
// Calculate row height (for the unlikely case that labels may be are multi-line)
+ float row_y1 = GetCursorScreenPos().y;
float row_height = GetTextLineHeight();
for (int column_n = 0; column_n < columns_count; column_n++)
if (TableGetColumnIsVisible(column_n))
row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y);
- row_height += g.Style.CellPadding.y * 2.0f;
+ row_height += style.CellPadding.y * 2.0f;
// Open row
TableNextRow(ImGuiTableRowFlags_Headers, row_height);
- if (table->HostSkipItems) // Merely an optimization
+ if (table->HostSkipItems) // Merely an optimization, you may skip in your own code.
return;
// This for loop is constructed to not make use of internal functions,
// as this is intended to be a base template to copy and build from.
- int open_context_popup = INT_MAX;
for (int column_n = 0; column_n < columns_count; column_n++)
{
if (!TableSetColumnIndex(column_n))
@@ -9564,64 +9865,29 @@
Checkbox("##", &b[column_n]);
PopStyleVar();
PopID();
- SameLine(0.0f, g.Style.ItemInnerSpacing.x);
+ SameLine(0.0f, style.ItemInnerSpacing.x);
}
#endif
- // [DEBUG]
- //if (g.IO.KeyCtrl) { static char buf[32]; name = buf; ImGuiTableColumn* c = &table->Columns[column_n]; if (c->Flags & ImGuiTableColumnFlags_WidthStretch) ImFormatString(buf, 32, "%.3f>%.1f", c->ResizeWeight, c->WidthGiven); else ImFormatString(buf, 32, "%.1f", c->WidthGiven); }
-
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
- PushID(table->InstanceNo * table->ColumnsCount + column_n);
+ // - in your own code you may omit the PushID/PopID all-together, provided you know you know they won't collide
+ // - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier.
+ PushID(table->InstanceCurrent * table->ColumnsCount + column_n);
TableHeader(name);
PopID();
-
- // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
- if (IsMouseReleased(1) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
- open_context_popup = column_n;
}
- // FIXME-TABLE: This is not user-land code any more + need to explain WHY this is here!
- window->SkipItems = table->HostSkipItems;
+ // FIXME-TABLE: This is not user-land code any more + need to explain WHY this is here! (added in fa88f023)
+ //window->SkipItems = table->HostSkipItems;
- // Allow opening popup from the right-most section after the last column
- // FIXME-TABLE: This is not user-land code any more... perhaps instead we should expose hovered column.
- // and allow some sort of row-centric IsItemHovered() for full flexibility?
- float unused_x1 = table->WorkRect.Min.x;
- if (table->RightMostActiveColumn != -1)
- unused_x1 = ImMax(unused_x1, table->Columns[table->RightMostActiveColumn].MaxX);
- if (unused_x1 < table->WorkRect.Max.x)
- {
- // FIXME: We inherit ClipRect/SkipItem from last submitted column (active or not), let's temporarily override it.
- // Because we don't perform any rendering here we just overwrite window->ClipRect used by logic.
- window->ClipRect = table->InnerClipRect;
-
- ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
- window->DC.CursorPos = ImVec2(unused_x1, table->RowPosY1);
- ImVec2 size = ImVec2(table->WorkRect.Max.x - window->DC.CursorPos.x, table->RowPosY2 - table->RowPosY1);
- if (size.x > 0.0f && size.y > 0.0f)
- {
- InvisibleButton("##RemainingSpace", size);
- window->DC.CursorPos.y -= g.Style.ItemSpacing.y;
- window->DC.CursorMaxPos = backup_cursor_max_pos; // Don't feed back into the width of the Header row
-
- // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
- if (IsMouseReleased(1) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
- open_context_popup = -1;
- }
-
- window->ClipRect = window->DrawList->_ClipRectStack.back();
- }
-
- // Open Context Menu
- if (open_context_popup != INT_MAX)
- if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
- {
- table->IsContextPopupOpen = true;
- table->ContextPopupColumn = (ImS8)open_context_popup;
- table->InstanceInteracted = table->InstanceNo;
- OpenPopup("##TableContextMenu");
- }
+ // Allow opening popup from the right-most section after the last column.
+ // (We don't actually need to use ImGuiHoveredFlags_AllowWhenBlockedByPopup because in reality this is generally
+ // not required anymore.. because popup opening code tends to be reacting on IsMouseReleased() and the click would
+ // already have closed any other popups!)
+ // FIXME-TABLE: TableOpenContextMenu() is not public yet.
+ if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)
+ if (g.IO.MousePos.y >= row_y1 && g.IO.MousePos.y < row_y1 + row_height)
+ TableOpenContextMenu(table, -1); // Will open a non-column-specific popup.
}
// Emit a column header (text + optional sort order)
@@ -9651,24 +9917,27 @@
// If we already got a row height, there's use that.
ImRect cell_r = TableGetCellRect();
+ cell_r.Min.x -= table->CellSpacingX; // FIXME-TABLE: TableGetCellRect() is misleading.
float label_height = ImMax(label_size.y, table->RowMinHeight - g.Style.CellPadding.y * 2.0f);
//GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
- ImRect work_r = cell_r;
- work_r.Min.x = window->DC.CursorPos.x;
- work_r.Max.y = work_r.Min.y + label_height;
- float ellipsis_max = work_r.Max.x;
- // Selectable
- PushID(label);
+ // Keep header highlighted when context menu is open.
+ // (FIXME-TABLE: however we cannot assume the ID of said popup if it has been created by the user...)
+ const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent);
+ ImGuiID id = window->GetID(label);
+ ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
+ if (!ItemAdd(bb, id))
+ return;
- // FIXME-TABLE: Fix when padding are disabled.
- //window->DC.CursorPos.x = column->MinX + table->CellPadding.x;
-
- // Keep header highlighted when context menu is open. (FIXME-TABLE: however we cannot assume the ID of said popup if it has been created by the user...)
- const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceNo);
- const bool pressed = Selectable("", selected, ImGuiSelectableFlags_DrawHoveredWhenHeld | ImGuiSelectableFlags_DontClosePopups, ImVec2(0.0f, label_height));
- const bool held = IsItemActive();
+ bool hovered, held;
+ bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
+ if (hovered || selected)
+ {
+ const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
+ RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
+ RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
+ }
if (held)
table->HeldHeaderColumn = (ImS8)column_n;
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
@@ -9679,18 +9948,18 @@
{
// While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x
table->ReorderColumn = (ImS8)column_n;
- table->InstanceInteracted = table->InstanceNo;
+ table->InstanceInteracted = table->InstanceCurrent;
// We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.
if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)
- if (ImGuiTableColumn* prev_column = (column->PrevActiveColumn != -1) ? &table->Columns[column->PrevActiveColumn] : NULL)
+ if (ImGuiTableColumn* prev_column = (column->PrevVisibleColumn != -1) ? &table->Columns[column->PrevVisibleColumn] : NULL)
if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))
- if ((column->IndexWithinActiveSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinActiveSet < table->FreezeColumnsRequest))
+ if ((column->IndexWithinVisibleSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinVisibleSet < table->FreezeColumnsRequest))
table->ReorderColumnDir = -1;
if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)
- if (ImGuiTableColumn* next_column = (column->NextActiveColumn != -1) ? &table->Columns[column->NextActiveColumn] : NULL)
+ if (ImGuiTableColumn* next_column = (column->NextVisibleColumn != -1) ? &table->Columns[column->NextVisibleColumn] : NULL)
if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))
- if ((column->IndexWithinActiveSet < table->FreezeColumnsRequest) == (next_column->IndexWithinActiveSet < table->FreezeColumnsRequest))
+ if ((column->IndexWithinVisibleSet < table->FreezeColumnsRequest) == (next_column->IndexWithinVisibleSet < table->FreezeColumnsRequest))
table->ReorderColumnDir = +1;
}
@@ -9697,6 +9966,7 @@
// Sort order arrow
float w_arrow = 0.0f;
float w_sort_text = 0.0f;
+ float ellipsis_max = cell_r.Max.x;
if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
{
const float ARROW_SCALE = 0.65f;
@@ -9712,7 +9982,7 @@
w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
}
- float x = ImMax(cell_r.Min.x, work_r.Max.x - w_arrow - w_sort_text);
+ float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);
ellipsis_max -= w_arrow + w_sort_text;
float y = label_pos.y;
@@ -9732,18 +10002,21 @@
TableSortSpecsClickColumn(table, column, g.IO.KeyShift);
}
- // Render clipped label
- // Clipping here ensure that in the majority of situations, all our header cells will be merged into a single draw call.
+ // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
+ // be merged into a single draw call.
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
- // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging.
+ // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering
+ // for merging.
// FIXME-TABLE: Clarify policies of how label width and potential decorations (arrows) fit into auto-resize of the column
float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
- column->ContentMaxPosHeadersUsed = ImMax(column->ContentMaxPosHeadersUsed, work_r.Max.x);// ImMin(max_pos_x, work_r.Max.x));
- column->ContentMaxPosHeadersDesired = ImMax(column->ContentMaxPosHeadersDesired, max_pos_x);
+ column->ContentMaxPosHeadersUsed = ImMax(column->ContentMaxPosHeadersUsed, cell_r.Max.x);// ImMin(max_pos_x, cell_r.Max.x));
+ column->ContentMaxPosHeadersIdeal = ImMax(column->ContentMaxPosHeadersIdeal, max_pos_x);
- PopID();
+ // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
+ if (IsMouseReleased(1) && IsItemHovered())
+ TableOpenContextMenu(table, column_n);
}
void ImGui::TableSortSpecsClickColumn(ImGuiTable* table, ImGuiTableColumn* clicked_column, bool add_to_existing_sort_orders)
@@ -9773,10 +10046,9 @@
if (column->SortOrder == -1 || !add_to_existing_sort_orders)
column->SortOrder = add_to_existing_sort_orders ? sort_order_max + 1 : 0;
}
- else
+ else if (!add_to_existing_sort_orders)
{
- if (!add_to_existing_sort_orders)
- column->SortOrder = -1;
+ column->SortOrder = -1;
}
TableFixColumnSortDirection(column);
}
@@ -9784,8 +10056,9 @@
table->IsSortSpecsDirty = true;
}
-// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set)
-// You can sort your data again when 'SpecsChanged == true'. It will be true with sorting specs have changed since last call, or the first time.
+// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set)
+// You can sort your data again when 'SpecsChanged == true'. It will be true with sorting specs have changed since
+// last call, or the first time.
// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()!
const ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
{
@@ -9796,34 +10069,11 @@
if (!(table->Flags & ImGuiTableFlags_Sortable))
return NULL;
- // Flatten sort specs into user facing data
- const bool was_dirty = table->IsSortSpecsDirty;
- if (was_dirty)
- {
- TableSortSpecsSanitize(table);
+ if (table->IsSortSpecsDirty)
+ TableSortSpecsBuild(table);
- // Write output
- table->SortSpecsData.resize(table->SortSpecsCount);
- table->SortSpecs.ColumnsMask = 0x00;
- for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
- {
- ImGuiTableColumn* column = &table->Columns[column_n];
- if (column->SortOrder == -1)
- continue;
- ImGuiTableSortSpecsColumn* sort_spec = &table->SortSpecsData[column->SortOrder];
- sort_spec->ColumnUserID = column->UserID;
- sort_spec->ColumnIndex = (ImU8)column_n;
- sort_spec->SortOrder = (ImU8)column->SortOrder;
- sort_spec->SortDirection = column->SortDirection;
- table->SortSpecs.ColumnsMask |= (ImU64)1 << column_n;
- }
- }
-
- // User facing data
- table->SortSpecs.Specs = table->SortSpecsData.Data;
- table->SortSpecs.SpecsCount = table->SortSpecsData.Size;
- table->SortSpecs.SpecsChanged = was_dirty;
- table->IsSortSpecsDirty = false;
+ table->SortSpecs.SpecsChanged = table->IsSortSpecsChangedForUser;
+ table->IsSortSpecsChangedForUser = false;
return table->SortSpecs.SpecsCount ? &table->SortSpecs : NULL;
}
@@ -9839,8 +10089,19 @@
return (column->SortOrder != -1);
}
+int ImGui::TableGetHoveredColumn()
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiTable* table = g.CurrentTable;
+ if (!table)
+ return -1;
+ return (int)table->HoveredColumnBody;
+}
+
void ImGui::TableSortSpecsSanitize(ImGuiTable* table)
{
+ IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);
+
// Clear SortOrder from hidden column and verify that there's no gap or duplicate.
int sort_order_count = 0;
ImU64 sort_order_mask = 0x00;
@@ -9847,7 +10108,7 @@
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
- if (column->SortOrder != -1 && !column->IsActive)
+ if (column->SortOrder != -1 && !column->IsVisible)
column->SortOrder = -1;
if (column->SortOrder == -1)
continue;
@@ -9877,6 +10138,7 @@
// Fix: Make sure only one column has a SortOrder if ImGuiTableFlags_MultiSortable is not set.
if (need_fix_single_sort_order)
{
+ sort_order_count = 1;
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
if (column_n != column_with_smallest_sort_order)
table->Columns[column_n].SortOrder = -1;
@@ -9885,15 +10147,16 @@
}
}
- // Fallback default sort order (if no column has the ImGuiTableColumnFlags_DefaultSort flag)
- if (sort_order_count == 0 && table->IsInitializing)
+ // Fallback default sort order (if no column had the ImGuiTableColumnFlags_DefaultSort flag)
+ if (sort_order_count == 0)
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
- if (!(column->Flags & ImGuiTableColumnFlags_NoSort) && column->IsActive)
+ if (column->IsVisible && !(column->Flags & ImGuiTableColumnFlags_NoSort))
{
sort_order_count = 1;
column->SortOrder = 0;
+ TableFixColumnSortDirection(column);
break;
}
}
@@ -9901,6 +10164,33 @@
table->SortSpecsCount = (ImS8)sort_order_count;
}
+void ImGui::TableSortSpecsBuild(ImGuiTable* table)
+{
+ IM_ASSERT(table->IsSortSpecsDirty);
+ TableSortSpecsSanitize(table);
+
+ // Write output
+ table->SortSpecsData.resize(table->SortSpecsCount);
+ table->SortSpecs.ColumnsMask = 0x00;
+ for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
+ {
+ ImGuiTableColumn* column = &table->Columns[column_n];
+ if (column->SortOrder == -1)
+ continue;
+ ImGuiTableSortSpecsColumn* sort_spec = &table->SortSpecsData[column->SortOrder];
+ sort_spec->ColumnUserID = column->UserID;
+ sort_spec->ColumnIndex = (ImU8)column_n;
+ sort_spec->SortOrder = (ImU8)column->SortOrder;
+ sort_spec->SortDirection = column->SortDirection;
+ table->SortSpecs.ColumnsMask |= (ImU64)1 << column_n;
+ }
+ table->SortSpecs.Specs = table->SortSpecsData.Data;
+ table->SortSpecs.SpecsCount = table->SortSpecsData.Size;
+
+ table->IsSortSpecsDirty = false;
+ table->IsSortSpecsChangedForUser = true;
+}
+
//-------------------------------------------------------------------------
// TABLE - .ini settings
//-------------------------------------------------------------------------
@@ -9910,20 +10200,29 @@
// [Main] 4: TableSettingsHandler_WriteAll() When .ini file is dirty (which can come from other source), save TableSettings into .ini file.
//-------------------------------------------------------------------------
-static ImGuiTableSettings* CreateTableSettings(ImGuiID id, int columns_count)
+// Clear and initialize empty settings instance
+static void InitTableSettings(ImGuiTableSettings* settings, ImGuiID id, int columns_count, int columns_count_max)
{
- ImGuiContext& g = *GImGui;
- ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings));
IM_PLACEMENT_NEW(settings) ImGuiTableSettings();
ImGuiTableColumnSettings* settings_column = settings->GetColumnSettings();
- for (int n = 0; n < columns_count; n++, settings_column++)
+ for (int n = 0; n < columns_count_max; n++, settings_column++)
IM_PLACEMENT_NEW(settings_column) ImGuiTableColumnSettings();
settings->ID = id;
- settings->ColumnsCount = settings->ColumnsCountMax = (ImS8)columns_count;
+ settings->ColumnsCount = (ImS8)columns_count;
+ settings->ColumnsCountMax = (ImS8)columns_count_max;
+ settings->WantApply = true;
+}
+
+ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings));
+ InitTableSettings(settings, id, columns_count, columns_count);
return settings;
}
-static ImGuiTableSettings* FindTableSettingsByID(ImGuiID id)
+// Find existing settings
+ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id)
{
// FIXME-OPT: Might want to store a lookup map for this?
ImGuiContext& g = *GImGui;
@@ -9933,20 +10232,25 @@
return NULL;
}
-ImGuiTableSettings* ImGui::TableFindSettings(ImGuiTable* table)
+void ImGui::TableSettingsClearByID(ImGuiID id)
{
- if (table->SettingsOffset == -1)
- return NULL;
+ if (ImGuiTableSettings* settings = TableSettingsFindByID(id))
+ settings->ID = 0;
+}
- ImGuiContext& g = *GImGui;
- ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
- IM_ASSERT(settings->ID == table->ID);
- if (settings->ColumnsCountMax < table->ColumnsCount)
+// Get settings for a given table, NULL if none
+ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table)
+{
+ if (table->SettingsOffset != -1)
{
- settings->ID = 0; // Ditch storage if we won't fit because of a count change
- return NULL;
+ ImGuiContext& g = *GImGui;
+ ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
+ IM_ASSERT(settings->ID == table->ID);
+ if (settings->ColumnsCountMax >= table->ColumnsCount)
+ return settings; // OK
+ settings->ID = 0; // Invalidate storage, we won't fit because of a count change
}
- return settings;
+ return NULL;
}
void ImGui::TableSaveSettings(ImGuiTable* table)
@@ -9957,15 +10261,15 @@
// Bind or create settings data
ImGuiContext& g = *GImGui;
- ImGuiTableSettings* settings = TableFindSettings(table);
+ ImGuiTableSettings* settings = TableGetBoundSettings(table);
if (settings == NULL)
{
- settings = CreateTableSettings(table->ID, table->ColumnsCount);
+ settings = TableSettingsCreate(table->ID, table->ColumnsCount);
table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);
}
settings->ColumnsCount = (ImS8)table->ColumnsCount;
- // Serialize ImGuiTableSettings/ImGuiTableColumnSettings --> ImGuiTable/ImGuiTableColumn
+ // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings
IM_ASSERT(settings->ID == table->ID);
IM_ASSERT(settings->ColumnsCount == table->ColumnsCount && settings->ColumnsCountMax >= settings->ColumnsCount);
ImGuiTableColumn* column = table->Columns.Data;
@@ -9972,26 +10276,31 @@
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
// FIXME-TABLE: Logic to avoid saving default widths?
+ bool save_ref_scale = false;
settings->SaveFlags = ImGuiTableFlags_Resizable;
for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++)
{
- //column_settings->WidthOrWeight = column->WidthRequested; // FIXME-WIP
+ column_settings->WidthOrWeight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->WidthStretchWeight : column->WidthRequest;
column_settings->Index = (ImS8)n;
column_settings->DisplayOrder = column->DisplayOrder;
column_settings->SortOrder = column->SortOrder;
column_settings->SortDirection = column->SortDirection;
- column_settings->Visible = column->IsActive;
+ column_settings->IsVisible = column->IsVisible;
+ column_settings->IsWeighted = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0;
+ if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0)
+ save_ref_scale = true;
// We skip saving some data in the .ini file when they are unnecessary to restore our state
- // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet.
+ // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present.
if (column->DisplayOrder != n)
settings->SaveFlags |= ImGuiTableFlags_Reorderable;
- if (column_settings->SortOrder != -1)
+ if (column->SortOrder != -1)
settings->SaveFlags |= ImGuiTableFlags_Sortable;
- if (column_settings->Visible != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0))
+ if (column->IsVisible != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0))
settings->SaveFlags |= ImGuiTableFlags_Hideable;
}
settings->SaveFlags &= table->Flags;
+ settings->RefScale = save_ref_scale ? table->RefScale : 0.0f;
MarkIniSettingsDirty();
}
@@ -10007,7 +10316,7 @@
ImGuiTableSettings* settings;
if (table->SettingsOffset == -1)
{
- settings = FindTableSettingsByID(table->ID);
+ settings = TableSettingsFindByID(table->ID);
if (settings == NULL)
return;
table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);
@@ -10014,12 +10323,13 @@
}
else
{
- settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
+ settings = TableGetBoundSettings(table);
}
- table->IsSettingsLoaded = true;
- settings->SaveFlags = table->Flags;
+ table->SettingsLoadedFlags = settings->SaveFlags;
+ table->RefScale = settings->RefScale;
+ IM_ASSERT(settings->ColumnsCount == table->ColumnsCount);
- // Serialize ImGuiTable/ImGuiTableColumn --> ImGuiTableSettings/ImGuiTableColumnSettings
+ // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++)
{
@@ -10026,16 +10336,23 @@
int column_n = column_settings->Index;
if (column_n < 0 || column_n >= table->ColumnsCount)
continue;
+
ImGuiTableColumn* column = &table->Columns[column_n];
- //column->WidthRequested = column_settings->WidthOrWeight; // FIXME-WIP
- if (column_settings->DisplayOrder != -1)
- column->DisplayOrder = column_settings->DisplayOrder;
- if (column_settings->SortOrder != -1)
+ if (settings->SaveFlags & ImGuiTableFlags_Resizable)
{
- column->SortOrder = column_settings->SortOrder;
- column->SortDirection = column_settings->SortDirection;
+ if (column_settings->IsWeighted)
+ column->WidthStretchWeight = column_settings->WidthOrWeight;
+ else
+ column->WidthRequest = column_settings->WidthOrWeight;
+ column->AutoFitQueue = 0x00;
}
- column->IsActive = column->IsActiveNextFrame = column_settings->Visible;
+ if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
+ column->DisplayOrder = column_settings->DisplayOrder;
+ else
+ column->DisplayOrder = (ImS8)column_n;
+ column->IsVisible = column->IsVisibleNextFrame = column_settings->IsVisible;
+ column->SortOrder = column_settings->SortOrder;
+ column->SortDirection = column_settings->SortDirection;
}
// FIXME-TABLE: Need to validate .ini data
@@ -10043,35 +10360,72 @@
table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImS8)column_n;
}
-void* ImGui::TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
+static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
{
+ ImGuiContext& g = *ctx;
+ for (int i = 0; i != g.Tables.GetSize(); i++)
+ g.Tables.GetByIndex(i)->SettingsOffset = -1;
+ g.SettingsTables.clear();
+}
+
+// Apply to existing windows (if any)
+static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
+{
+ ImGuiContext& g = *ctx;
+ for (int i = 0; i != g.Tables.GetSize(); i++)
+ {
+ ImGuiTable* table = g.Tables.GetByIndex(i);
+ table->IsSettingsRequestLoad = true;
+ table->SettingsOffset = -1;
+ }
+}
+
+static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
+{
ImGuiID id = 0;
int columns_count = 0;
if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2)
return NULL;
- return CreateTableSettings(id, columns_count);
+
+ if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id))
+ {
+ if (settings->ColumnsCountMax >= columns_count)
+ {
+ InitTableSettings(settings, id, columns_count, settings->ColumnsCountMax); // Recycle
+ return settings;
+ }
+ settings->ID = 0; // Invalidate storage if we won't fit because of a count change
+ }
+ return ImGui::TableSettingsCreate(id, columns_count);
}
-void ImGui::TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
+static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
{
// "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
ImGuiTableSettings* settings = (ImGuiTableSettings*)entry;
+ float f = 0.0f;
int column_n = 0, r = 0, n = 0;
- if (sscanf(line, "Column %d%n", &column_n, &r) == 1) { line = ImStrSkipBlank(line + r); } else { return; }
- if (column_n < 0 || column_n >= settings->ColumnsCount)
- return;
- char c = 0;
- ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n;
- column->Index = (ImS8)column_n;
- if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r) == 1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; }
- if (sscanf(line, "Width=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); /* .. */ settings->SaveFlags |= ImGuiTableFlags_Resizable; }
- if (sscanf(line, "Visible=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->Visible = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; }
- if (sscanf(line, "Order=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImS8)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; }
- if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2) { line = ImStrSkipBlank(line + r); column->SortOrder = (ImS8)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; }
+ if (sscanf(line, "RefScale=%f", &f) == 1) { settings->RefScale = f; return; }
+
+ if (sscanf(line, "Column %d%n", &column_n, &r) == 1)
+ {
+ if (column_n < 0 || column_n >= settings->ColumnsCount)
+ return;
+ line = ImStrSkipBlank(line + r);
+ char c = 0;
+ ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n;
+ column->Index = (ImS8)column_n;
+ if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; }
+ if (sscanf(line, "Width=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsWeighted = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; }
+ if (sscanf(line, "Weight=%f%n", &f, &r) == 1) { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsWeighted = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; }
+ if (sscanf(line, "Visible=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->IsVisible = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; }
+ if (sscanf(line, "Order=%d%n", &n, &r) == 1) { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImS8)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; }
+ if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2) { line = ImStrSkipBlank(line + r); column->SortOrder = (ImS8)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; }
+ }
}
-void ImGui::TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
+static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
{
ImGuiContext& g = *ctx;
for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
@@ -10079,7 +10433,8 @@
if (settings->ID == 0) // Skip ditched settings
continue;
- // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped (e.g. Order was unchanged)
+ // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped
+ // (e.g. Order was unchanged)
const bool save_size = (settings->SaveFlags & ImGuiTableFlags_Resizable) != 0;
const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;
const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
@@ -10089,16 +10444,17 @@
buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount);
+ if (settings->RefScale != 0.0f)
+ buf->appendf("RefScale=%g\n", settings->RefScale);
ImGuiTableColumnSettings* column = settings->GetColumnSettings();
for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++)
{
// "Column 0 UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
- if (column->UserID != 0)
- buf->appendf("Column %-2d UserID=%08X", column_n, column->UserID);
- else
- buf->appendf("Column %-2d", column_n);
- if (save_size) buf->appendf(" Width=%d", 0);// (int)settings_column->WidthOrWeight); // FIXME-TABLE
- if (save_visible) buf->appendf(" Visible=%d", column->Visible);
+ buf->appendf("Column %-2d", column_n);
+ if (column->UserID != 0) buf->appendf(" UserID=%08X", column->UserID);
+ if (save_size && column->IsWeighted) buf->appendf(" Weight=%.4f", column->WidthOrWeight);
+ if (save_size && !column->IsWeighted) buf->appendf(" Width=%d", (int)column->WidthOrWeight);
+ if (save_visible) buf->appendf(" Visible=%d", column->IsVisible);
if (save_order) buf->appendf(" Order=%d", column->DisplayOrder);
if (save_sort && column->SortOrder != -1) buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^');
buf->append("\n");
@@ -10105,6 +10461,20 @@
}
buf->append("\n");
}
+}
+
+void ImGui::TableSettingsInstallHandler(ImGuiContext* context)
+{
+ ImGuiContext& g = *context;
+ ImGuiSettingsHandler ini_handler;
+ ini_handler.TypeName = "Table";
+ ini_handler.TypeHash = ImHashStr("Table");
+ ini_handler.ClearAllFn = TableSettingsHandler_ClearAll;
+ ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
+ ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
+ ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll;
+ ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
+ g.SettingsHandlers.push_back(ini_handler);
}
//-------------------------------------------------------------------------