Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/wolfpld/tracy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/imgui
diff options
context:
space:
mode:
authorBartosz Taudul <wolf.pld@gmail.com>2018-05-22 21:49:05 +0300
committerBartosz Taudul <wolf.pld@gmail.com>2018-05-22 21:49:05 +0300
commitf7e2683cf114fb73095881ed797ff0f956ad6803 (patch)
tree3b1e69a77aef4fb8d61548c5d9ba43d0c7024a8c /imgui
parent312c20b0bc974e7daaa6ff4ae780a6d082aa72da (diff)
Update imgui to 1.61.
Diffstat (limited to 'imgui')
-rw-r--r--imgui/LICENSE.txt2
-rw-r--r--imgui/imconfig.h21
-rw-r--r--imgui/imgui.cpp1975
-rw-r--r--imgui/imgui.h212
-rw-r--r--imgui/imgui_demo.cpp211
-rw-r--r--imgui/imgui_draw.cpp59
-rw-r--r--imgui/imgui_internal.h163
-rw-r--r--imgui/stb_textedit.h2
8 files changed, 1535 insertions, 1110 deletions
diff --git a/imgui/LICENSE.txt b/imgui/LICENSE.txt
index 5a9b98b8..21b6ee7e 100644
--- a/imgui/LICENSE.txt
+++ b/imgui/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2014-2017 Omar Cornut and ImGui contributors
+Copyright (c) 2014-2018 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/imgui/imconfig.h b/imgui/imconfig.h
index 5ab10e96..b181691b 100644
--- a/imgui/imconfig.h
+++ b/imgui/imconfig.h
@@ -5,32 +5,35 @@
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating imgui, or maintain a patch/branch with your modifications to imconfig.h)
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
-// C) Many compile-time options have an effect on data structures. They need defined consistently _everywhere_ imgui.h is included,
-// not only for the imgui*.cpp compilation units. Defining those options in imconfig.h will ensure they correctly get used everywhere.
+// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ dear imgui is used, which include
+// the imgui*.cpp files but also _any_ of your code that uses imgui. This is because some compile-time options have an affect on data structures.
+// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
+// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
+//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
-//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names
+//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-//---- Don't implement default handlers for Windows (so as not to link with certain functions)
-//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // Don't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
-//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // Don't use and link with ImmGetContext/ImmSetCompositionWindow.
-
//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty)
//---- It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp.
//#define IMGUI_DISABLE_DEMO_WINDOWS
-//---- Don't implement ImFormatString(), ImFormatStringV() so you can reimplement them yourself.
-//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
+//---- Don't implement some functions to reduce linkage requirements.
+//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
+//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
+//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
+//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
+//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free(). You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
diff --git a/imgui/imgui.cpp b/imgui/imgui.cpp
index 33b35193..0e573294 100644
--- a/imgui/imgui.cpp
+++ b/imgui/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.60
+// dear imgui, v1.61
// (main code and documentation)
// Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
@@ -124,7 +124,7 @@
- Call ImGui::NewFrame() to begin the frame
- You can use any ImGui function you want between NewFrame() and Render()
- Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler.
- (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwhise some features will break)
+ (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwise some features will break)
- All rendering information are stored into command-lists until ImGui::Render() is called.
- Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
- Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases
@@ -142,7 +142,7 @@
// Load texture atlas (there is a default font so you don't need to care about choosing a font yet)
unsigned char* pixels;
int width, height;
- io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height);
+ io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// TODO: At this points you've got the texture data and you need to upload that your your graphic system:
MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA)
// TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer.
@@ -216,7 +216,7 @@
- The examples/ folders contains many functional implementation of the pseudo-code above.
- When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
They tell you if ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application.
- However, in both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags.
+ In both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags.
- Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues!
USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS [BETA]
@@ -236,7 +236,7 @@
to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
- Keyboard:
- Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
- NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays.
+ NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
- When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
will be set. For more advanced uses, you may want to read from:
- io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
@@ -258,20 +258,28 @@
API BREAKING CHANGES
====================
- Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
- Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
- Also read releases logs https://github.com/ocornut/imgui/releases for more details.
-
- - 2018/03/20 (1.60) - Renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
- - 2018/03/12 (1.60) - Removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
- - 2018/03/08 (1.60) - Changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
- - 2018/03/03 (1.60) - Renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
+ Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
+ Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.
+ 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.
+
+ - 2018/05/03 (1.61) - DragInt(): The default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more.
+ If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
+ To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.
+ If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them.
+ - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format",
+ consistent with other functions. Kept redirection functions (will obsolete).
+ - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.
+ - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
+ - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
+ - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
+ - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
- 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.
- 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.
- 2018/02/07 (1.60) - reorganized context handling to be more explicit,
- YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
- removed Shutdown() function, as DestroyContext() serve this purpose.
- - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwhise CreateContext() will create its own font atlas instance.
+ - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
- removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
- removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
- 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.
@@ -325,7 +333,7 @@
- 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal.
- 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
- However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
+ If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color.
ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
{
@@ -432,7 +440,7 @@
perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to NewFrameUpdateHoveredWindowAndCaptureFlags().
Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
- were targetted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
+ were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
Q: How can I display an image? What is ImTextureID, how does it works?
A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
@@ -661,7 +669,6 @@
#include "imgui_internal.h"
#include <ctype.h> // toupper, isprint
-#include <stdlib.h> // NULL, malloc, free, qsort, atoi
#include <stdio.h> // vsnprintf, sscanf, printf
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
@@ -707,6 +714,15 @@
#define IMGUI_CDECL
#endif
+static const ImS32 IM_S32_MIN = 0x80000000; // INT_MIN;
+static const ImS32 IM_S32_MAX = 0x7FFFFFFF; // INT_MAX;
+static const ImU32 IM_U32_MIN = 0;
+static const ImU32 IM_U32_MAX = 0xFFFFFFFF;
+static const ImS64 IM_S64_MIN = -9223372036854775807ll - 1ll;
+static const ImS64 IM_S64_MAX = 9223372036854775807ll;
+static const ImU64 IM_U64_MIN = 0;
+static const ImU64 IM_U64_MAX = 0xFFFFFFFFFFFFFFFFull;
+
//-------------------------------------------------------------------------
// Forward Declarations
//-------------------------------------------------------------------------
@@ -723,7 +739,7 @@ static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed,
static ImGuiWindow* FindHoveredWindow();
static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
static void CheckStacksSize(ImGuiWindow* window, bool write);
-static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
+static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
@@ -731,25 +747,17 @@ static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sort
static ImGuiWindowSettings* AddWindowSettings(const char* name);
-static void LoadIniSettingsFromDisk(const char* ini_filename);
-static void LoadIniSettingsFromMemory(const char* buf);
-static void SaveIniSettingsToDisk(const char* ini_filename);
-static void SaveIniSettingsToMemory(ImVector<char>& out_buf);
-static void MarkIniSettingsDirty(ImGuiWindow* window);
-
static ImRect GetViewportRect();
static void ClosePopupToLevel(int remaining);
-static ImGuiWindow* GetFrontMostModalRootWindow();
static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data);
static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);
static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);
-static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size);
-static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size);
+static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format);
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2);
-static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
+static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format);
namespace ImGui
{
@@ -757,10 +765,17 @@ static void NavUpdate();
static void NavUpdateWindowing();
static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
-static void NewFrameUpdateMovingWindow();
-static void NewFrameUpdateMouseInputs();
+static void UpdateMovingWindow();
+static void UpdateMouseInputs();
static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window);
+
+// Template widget behaviors
+template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
+static bool DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power);
+
+template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
+static bool 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);
}
//-----------------------------------------------------------------------------
@@ -775,7 +790,7 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
// Context
//-----------------------------------------------------------------------------
-// Current context pointer. Implicitely used by all ImGui functions. Always assumed to be != NULL.
+// Current context pointer. Implicitly used by all ImGui functions. Always assumed to be != NULL.
// CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
// If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
// ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can:
@@ -830,8 +845,8 @@ ImGuiStyle::ImGuiStyle()
GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
- DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
- DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
+ DisplayWindowPadding = ImVec2(20,20); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
+ DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
@@ -949,6 +964,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
// HELPERS
//-----------------------------------------------------------------------------
+#define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1]
#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose
#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
@@ -1072,12 +1088,29 @@ const char* ImStristr(const char* haystack, const char* haystack_end, const char
return NULL;
}
-static const char* ImAtoi(const char* src, int* output)
+// Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
+void ImStrTrimBlanks(char* buf)
+{
+ char* p = buf;
+ while (p[0] == ' ' || p[0] == '\t') // Leading blanks
+ p++;
+ char* p_start = p;
+ while (*p != 0) // Find end of string
+ p++;
+ while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
+ p--;
+ if (p_start != buf) // Copy memory if we had leading blanks
+ memmove(buf, p_start, p - p_start);
+ buf[p - p_start] = 0; // Zero terminate
+}
+
+template<typename TYPE>
+static const char* ImAtoi(const char* src, TYPE* output)
{
int negative = 0;
if (*src == '-') { negative = 1; src++; }
if (*src == '+') { src++; }
- int v = 0;
+ TYPE v = 0;
while (*src >= '0' && *src <= '9')
v = (v * 10) + (*src++ - '0');
*output = negative ? -v : v;
@@ -1403,7 +1436,7 @@ void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float&
}
const float chroma = r - (g < b ? g : b);
- out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));
+ out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
out_s = chroma / (r + 1e-20f);
out_v = r;
}
@@ -1419,7 +1452,7 @@ void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float&
return;
}
- h = fmodf(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);
@@ -1455,7 +1488,7 @@ FILE* ImFileOpen(const char* filename, const char* mode)
// Load file content into memory
// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
-void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes)
+void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
{
IM_ASSERT(filename && file_open_mode);
if (out_file_size)
@@ -1472,14 +1505,14 @@ void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int*
return NULL;
}
- int file_size = (int)file_size_signed;
- void* file_data = ImGui::MemAlloc((size_t)(file_size + padding_bytes));
+ size_t file_size = (size_t)file_size_signed;
+ void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
if (file_data == NULL)
{
fclose(f);
return NULL;
}
- if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size)
+ if (fread(file_data, 1, file_size, f) != file_size)
{
fclose(f);
ImGui::MemFree(file_data);
@@ -1762,7 +1795,10 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
if (len <= 0)
+ {
+ va_end(args_copy);
return;
+ }
const int write_off = Buf.Size;
const int needed_sz = write_off + len;
@@ -1774,6 +1810,7 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
Buf.resize(needed_sz);
ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
+ va_end(args_copy);
}
void ImGuiTextBuffer::appendf(const char* fmt, ...)
@@ -1918,12 +1955,13 @@ bool ImGuiListClipper::Step()
//-----------------------------------------------------------------------------
ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
+ : DrawListInst(&context->DrawListSharedData)
{
Name = ImStrdup(name);
ID = ImHash(name, 0);
IDStack.push_back(ID);
Flags = 0;
- PosFloat = Pos = ImVec2(0.0f, 0.0f);
+ Pos = ImVec2(0.0f, 0.0f);
Size = SizeFull = ImVec2(0.0f, 0.0f);
SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
WindowPadding = ImVec2(0.0f, 0.0f);
@@ -1934,8 +1972,8 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
Scroll = ImVec2(0.0f, 0.0f);
ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
- ScrollbarX = ScrollbarY = false;
ScrollbarSizes = ImVec2(0.0f, 0.0f);
+ ScrollbarX = ScrollbarY = false;
Active = WasActive = false;
WriteAccessed = false;
Collapsed = false;
@@ -1959,7 +1997,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
ItemWidthDefault = 0.0f;
FontWindowScale = 1.0f;
- DrawList = IM_NEW(ImDrawList)(&context->DrawListSharedData);
+ DrawList = &DrawListInst;
DrawList->_OwnerName = Name;
ParentWindow = NULL;
RootWindow = NULL;
@@ -1978,7 +2016,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
ImGuiWindow::~ImGuiWindow()
{
- IM_DELETE(DrawList);
+ IM_ASSERT(DrawList == &DrawListInst);
IM_DELETE(Name);
for (int i = 0; i != ColumnsStorage.Size; i++)
ColumnsStorage[i].~ImGuiColumnsSet();
@@ -2170,14 +2208,14 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
ItemSize(bb.GetSize(), text_offset_y);
}
-static ImGuiDir NavScoreItemGetQuadrant(float dx, float dy)
+static ImGuiDir inline NavScoreItemGetQuadrant(float dx, float dy)
{
- if (fabsf(dx) > fabsf(dy))
+ if (ImFabs(dx) > ImFabs(dy))
return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
}
-static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
+static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
{
if (a1 < b0)
return a1 - b0;
@@ -2186,6 +2224,20 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
return 0.0f;
}
+static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
+{
+ if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
+ {
+ r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
+ r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
+ }
+ else
+ {
+ r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
+ r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
+ }
+}
+
// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
{
@@ -2197,17 +2249,9 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
g.NavScoringCount++;
- // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
- if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
- {
- cand.Min.y = ImClamp(cand.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y);
- cand.Max.y = ImClamp(cand.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y);
- }
- else
- {
- cand.Min.x = ImClamp(cand.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
- cand.Max.x = ImClamp(cand.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
- }
+ // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
+ // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
+ NavClampRectToVisibleAreaForMoveDir(g.NavMoveDir, cand, window->ClipRect);
// Compute distance between boxes
// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
@@ -2215,12 +2259,12 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
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);
- float dist_box = fabsf(dbx) + fabsf(dby);
+ 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)
float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
- float dist_center = fabsf(dcx) + fabsf(dcy); // L1 metric (need this for our connectedness guarantee)
+ float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
// Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
ImGuiDir quadrant;
@@ -2252,10 +2296,11 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
{
ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]);
- g.OverlayDrawList.AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
- g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
- g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
- g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
+ ImDrawList* draw_list = ImGui::GetOverlayDrawList();
+ 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+ImGui::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.
{
@@ -2263,8 +2308,9 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
if (quadrant == g.NavMoveDir)
{
ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
- g.OverlayDrawList.AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
- g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
+ ImDrawList* draw_list = ImGui::GetOverlayDrawList();
+ draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
+ draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
}
}
#endif
@@ -2303,7 +2349,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
// are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
// This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
// 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 however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
+ // 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.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))
@@ -2388,6 +2434,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con
}
// Scoring for navigation
+ // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav))
{
ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
@@ -2593,10 +2640,10 @@ float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
//-----------------------------------------------------------------------------
-void* ImGui::MemAlloc(size_t sz)
+void* ImGui::MemAlloc(size_t size)
{
GImAllocatorActiveAllocationsCount++;
- return GImAllocatorAllocFunc(sz, GImAllocatorUserData);
+ return GImAllocatorAllocFunc(size, GImAllocatorUserData);
}
void ImGui::MemFree(void* ptr)
@@ -2637,6 +2684,20 @@ void ImGui::SetCurrentContext(ImGuiContext* ctx)
#endif
}
+// Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
+// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic.
+bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert)
+{
+ bool error = false;
+ if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatch version string!"); }
+ if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
+ if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
+ if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
+ if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
+ if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
+ return !error;
+}
+
void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
{
GImAllocatorAllocFunc = alloc_func;
@@ -2726,13 +2787,14 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
}
}
-static ImVec2 NavCalcPreferredMousePos()
+static ImVec2 NavCalcPreferredRefPos()
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.NavWindow;
- if (!window)
- return g.IO.MousePos;
- const ImRect& rect_rel = window->NavRectRel[g.NavLayer];
+ if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
+ return ImFloor(g.IO.MousePos);
+
+ // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item
+ const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
ImRect visible_rect = GetViewportRect();
return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
@@ -2899,8 +2961,8 @@ static void ImGui::NavUpdateWindowing()
if (move_delta.x != 0.0f || move_delta.y != 0.0f)
{
const float NAV_MOVE_SPEED = 800.0f;
- const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
- g.NavWindowingTarget->PosFloat += move_delta * move_speed;
+ const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
+ g.NavWindowingTarget->Pos += move_delta * move_speed;
g.NavDisableMouseHover = true;
MarkIniSettingsDirty(g.NavWindowingTarget);
}
@@ -2946,7 +3008,7 @@ static void ImGui::NavUpdateWindowing()
static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_rel)
{
// Scroll to keep newly navigated item fully into view
- 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->InnerMainRect.Min - window->Pos - ImVec2(1, 1), window->InnerMainRect.Max - window->Pos + ImVec2(1, 1));
//g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG]
if (window_rect_rel.Contains(item_rect_rel))
return;
@@ -2973,8 +3035,8 @@ static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_
window->ScrollTargetCenterRatio.y = 1.0f;
}
- // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block)
- ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
+ // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately after in NavUpdate()
+ ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
item_rect_rel.Translate(window->Scroll - next_scroll);
}
@@ -3063,10 +3125,11 @@ static void ImGui::NavUpdate()
// Apply application mouse position movement, after we had a chance to process move request result.
if (g.NavMousePosDirty && g.NavIdIsAlive)
{
- // Set mouse position given our knowledge of the nav widget position from last frame
+ // Set mouse position given our knowledge of the navigated item position from last frame
if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
{
- g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos();
+ IM_ASSERT(!g.NavDisableHighlight && g.NavDisableMouseHover);
+ g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
g.IO.WantSetMousePos = true;
}
g.NavMousePosDirty = false;
@@ -3228,7 +3291,7 @@ static void ImGui::NavUpdate()
if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
{
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->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
{
float pad = window->CalcFontSize() * 0.5f;
@@ -3244,32 +3307,32 @@ static void ImGui::NavUpdate()
g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
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 fabsf() calls in NavScoreItem().
+ 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().
//g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
g.NavScoringCount = 0;
#if IMGUI_DEBUG_NAV_RECTS
- if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
- if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
+ if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList()->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
+ if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
#endif
}
-static void ImGui::NewFrameUpdateMovingWindow()
+static void ImGui::UpdateMovingWindow()
{
ImGuiContext& g = *GImGui;
- if (g.MovingWindow && g.MovingWindow->MoveId == g.ActiveId && g.ActiveIdSource == ImGuiInputSource_Mouse)
+ if (g.MovingWindow != NULL)
{
// We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
- // We track it to preserve Focus and so that ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
+ // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
KeepAliveID(g.ActiveId);
IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
- if (g.IO.MouseDown[0])
+ if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
{
ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
- if (moving_window->PosFloat.x != pos.x || moving_window->PosFloat.y != pos.y)
+ if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
{
MarkIniSettingsDirty(moving_window);
- moving_window->PosFloat = pos;
+ SetWindowPos(moving_window, pos, ImGuiCond_Always);
}
FocusWindow(g.MovingWindow);
}
@@ -3288,16 +3351,15 @@ static void ImGui::NewFrameUpdateMovingWindow()
if (!g.IO.MouseDown[0])
ClearActiveID();
}
- g.MovingWindow = NULL;
}
}
-static void ImGui::NewFrameUpdateMouseInputs()
+static void ImGui::UpdateMouseInputs()
{
ImGuiContext& g = *GImGui;
// If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta
- if (ImGui::IsMousePosValid(&g.IO.MousePos) && ImGui::IsMousePosValid(&g.IO.MousePosPrev))
+ if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
else
g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
@@ -3353,7 +3415,7 @@ void ImGui::NewFrameUpdateHoveredWindowAndCaptureFlags()
g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
// Modal windows prevents cursor from hovering behind them.
- ImGuiWindow* modal_window = GetFrontMostModalRootWindow();
+ ImGuiWindow* modal_window = GetFrontMostPopupModal();
if (modal_window)
if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
g.HoveredRootWindow = g.HoveredWindow = NULL;
@@ -3397,7 +3459,7 @@ void ImGui::NewFrameUpdateHoveredWindowAndCaptureFlags()
g.IO.WantCaptureKeyboard = true;
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
- g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0;
+ g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
}
void ImGui::NewFrame()
@@ -3406,7 +3468,7 @@ void ImGui::NewFrame()
ImGuiContext& g = *GImGui;
// Check user data
- // (We pass an error message in the assert expression as a trick to get it visible to programmers who are not using a debugger, as most assert handlers display their argument)
+ // (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);
IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value");
@@ -3418,24 +3480,31 @@ void ImGui::NewFrame()
for (int n = 0; n < ImGuiKey_COUNT; n++)
IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");
- // Do a simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was super recently added in 1.60 WIP)
+ // Perform simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
- // Load settings on first frame
+ // Load settings on first frame (if not explicitly loaded manually before)
if (!g.SettingsLoaded)
{
IM_ASSERT(g.SettingsWindows.empty());
- LoadIniSettingsFromDisk(g.IO.IniFilename);
+ if (g.IO.IniFilename)
+ LoadIniSettingsFromDisk(g.IO.IniFilename);
g.SettingsLoaded = true;
}
- // Save settings (with a delay so we don't spam disk too much)
+ // Save settings (with a delay after the last modification, so we don't spam disk too much)
if (g.SettingsDirtyTimer > 0.0f)
{
g.SettingsDirtyTimer -= g.IO.DeltaTime;
if (g.SettingsDirtyTimer <= 0.0f)
- SaveIniSettingsToDisk(g.IO.IniFilename);
+ {
+ if (g.IO.IniFilename != NULL)
+ SaveIniSettingsToDisk(g.IO.IniFilename);
+ else
+ g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
+ g.SettingsDirtyTimer = 0.0f;
+ }
}
g.Time += g.IO.DeltaTime;
@@ -3492,26 +3561,26 @@ void ImGui::NewFrame()
NavUpdate();
// Update mouse input state
- NewFrameUpdateMouseInputs();
+ UpdateMouseInputs();
// Calculate frame-rate for the user, as a purely luxurious feature
g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
- g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
+ g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
// Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
- NewFrameUpdateMovingWindow();
+ UpdateMovingWindow();
NewFrameUpdateHoveredWindowAndCaptureFlags();
- if (GetFrontMostModalRootWindow() != NULL)
+ if (GetFrontMostPopupModal() != NULL)
g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
else
g.ModalWindowDarkeningRatio = 0.0f;
g.MouseCursor = ImGuiMouseCursor_Arrow;
g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
- g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
+ g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
// Mouse wheel scrolling, scale
if (g.HoveredWindow && !g.HoveredWindow->Collapsed && (g.IO.MouseWheel != 0.0f || g.IO.MouseWheelH != 0.0f))
@@ -3534,7 +3603,6 @@ void ImGui::NewFrame()
const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
window->Pos += offset;
- window->PosFloat += offset;
window->Size *= scale;
window->SizeFull *= scale;
}
@@ -3648,7 +3716,6 @@ void ImGui::Initialize(ImGuiContext* context)
{
ImGuiContext& g = *context;
IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
- g.LogClipboard = IM_NEW(ImGuiTextBuffer)();
// Add .ini handle for ImGuiWindow type
ImGuiSettingsHandler ini_handler;
@@ -3665,17 +3732,19 @@ void ImGui::Initialize(ImGuiContext* context)
// This function is merely here to free heap allocations.
void ImGui::Shutdown(ImGuiContext* context)
{
- ImGuiContext& g = *context;
-
// The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
+ ImGuiContext& g = *context;
if (g.IO.Fonts && g.FontAtlasOwnedByContext)
IM_DELETE(g.IO.Fonts);
+ g.IO.Fonts = NULL;
- // Cleanup of other data are conditional on actually having initialize ImGui.
+ // Cleanup of other data are conditional on actually having initialized ImGui.
if (!g.Initialized)
return;
- SaveIniSettingsToDisk(g.IO.IniFilename);
+ // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
+ if (g.SettingsLoaded && g.IO.IniFilename != NULL)
+ SaveIniSettingsToDisk(g.IO.IniFilename);
// Clear everything else
for (int i = 0; i < g.Windows.Size; i++)
@@ -3690,8 +3759,6 @@ void ImGui::Shutdown(ImGuiContext* context)
g.HoveredRootWindow = NULL;
g.ActiveIdWindow = NULL;
g.MovingWindow = NULL;
- for (int i = 0; i < g.SettingsWindows.Size; i++)
- IM_DELETE(g.SettingsWindows[i].Name);
g.ColorModifiers.clear();
g.StyleModifiers.clear();
g.FontStack.clear();
@@ -3704,6 +3771,8 @@ void ImGui::Shutdown(ImGuiContext* context)
g.InputTextState.InitialText.clear();
g.InputTextState.TempTextBuffer.clear();
+ for (int i = 0; i < g.SettingsWindows.Size; i++)
+ IM_DELETE(g.SettingsWindows[i].Name);
g.SettingsWindows.clear();
g.SettingsHandlers.clear();
@@ -3712,8 +3781,7 @@ void ImGui::Shutdown(ImGuiContext* context)
fclose(g.LogFile);
g.LogFile = NULL;
}
- if (g.LogClipboard)
- IM_DELETE(g.LogClipboard);
+ g.LogClipboard.clear();
g.Initialized = false;
}
@@ -3737,14 +3805,13 @@ static ImGuiWindowSettings* AddWindowSettings(const char* name)
return settings;
}
-static void LoadIniSettingsFromDisk(const char* ini_filename)
+void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
{
- if (!ini_filename)
- return;
- char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", NULL, +1);
+ size_t file_data_size = 0;
+ char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
if (!file_data)
return;
- LoadIniSettingsFromMemory(file_data);
+ LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
ImGui::MemFree(file_data);
}
@@ -3759,13 +3826,21 @@ ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
}
// Zero-tolerance, no error reporting, cheap .ini parsing
-static void LoadIniSettingsFromMemory(const char* buf_readonly)
+void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
{
- // For convenience and to make the code simpler, we'll write zero terminators inside the buffer. So let's create a writable copy.
- char* buf = ImStrdup(buf_readonly);
- char* buf_end = buf + strlen(buf);
-
ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.Initialized);
+ 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*)ImGui::MemAlloc(ini_size + 1);
+ char* buf_end = buf + ini_size;
+ memcpy(buf, ini_data, ini_size);
+ buf[ini_size] = 0;
+
void* entry_data = NULL;
ImGuiSettingsHandler* entry_handler = NULL;
@@ -3798,7 +3873,7 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly)
*type_end = 0; // Overwrite first ']'
name_start++; // Skip second '['
}
- entry_handler = ImGui::FindSettingsHandler(type_start);
+ entry_handler = FindSettingsHandler(type_start);
entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
}
else if (entry_handler != NULL && entry_data != NULL)
@@ -3811,37 +3886,37 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly)
g.SettingsLoaded = true;
}
-static void SaveIniSettingsToDisk(const char* ini_filename)
+void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
{
ImGuiContext& g = *GImGui;
g.SettingsDirtyTimer = 0.0f;
if (!ini_filename)
return;
- ImVector<char> buf;
- SaveIniSettingsToMemory(buf);
-
+ size_t ini_data_size = 0;
+ const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
FILE* f = ImFileOpen(ini_filename, "wt");
if (!f)
return;
- fwrite(buf.Data, sizeof(char), (size_t)buf.Size, f);
+ fwrite(ini_data, sizeof(char), ini_data_size, f);
fclose(f);
}
-static void SaveIniSettingsToMemory(ImVector<char>& out_buf)
+// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
+const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
{
ImGuiContext& g = *GImGui;
g.SettingsDirtyTimer = 0.0f;
-
- ImGuiTextBuffer buf;
+ g.SettingsIniData.Buf.resize(0);
+ g.SettingsIniData.Buf.push_back(0);
for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
{
ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
- handler->WriteAllFn(&g, handler, &buf);
+ handler->WriteAllFn(&g, handler, &g.SettingsIniData);
}
-
- buf.Buf.pop_back(); // Remove extra zero-terminator used by ImGuiTextBuffer
- out_buf.swap(buf.Buf);
+ if (out_size)
+ *out_size = (size_t)g.SettingsIniData.size();
+ return g.SettingsIniData.c_str();
}
void ImGui::MarkIniSettingsDirty()
@@ -3851,7 +3926,7 @@ void ImGui::MarkIniSettingsDirty()
g.SettingsDirtyTimer = g.IO.IniSavingRate;
}
-static void MarkIniSettingsDirty(ImGuiWindow* window)
+void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
@@ -3888,7 +3963,7 @@ static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows,
}
}
-static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_render_list, ImDrawList* draw_list)
+static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
{
if (draw_list->CmdBuffer.empty())
return;
@@ -3918,7 +3993,7 @@ static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_render_list, ImDraw
if (sizeof(ImDrawIdx) == 2)
IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
- out_render_list->push_back(draw_list);
+ out_list->push_back(draw_list);
}
static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
@@ -3997,10 +4072,10 @@ void ImGui::EndFrame()
return;
// Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
- if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f)
+ if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)
{
- g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y);
- g.OsImePosSet = g.OsImePosRequest;
+ g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
+ g.PlatformImeLastPos = g.PlatformImePos;
}
// Hide implicit "Debug" window if it hasn't been used
@@ -4026,7 +4101,7 @@ void ImGui::EndFrame()
if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove))
g.MovingWindow = g.HoveredWindow;
}
- else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL)
+ else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
{
// Clicking on void disable focus
FocusWindow(NULL);
@@ -4039,7 +4114,7 @@ void ImGui::EndFrame()
{
// Find the top-most window between HoveredWindow and the front most Modal Window.
// This is where we can trim the popup stack.
- ImGuiWindow* modal = GetFrontMostModalRootWindow();
+ ImGuiWindow* modal = GetFrontMostPopupModal();
bool hovered_window_above_modal = false;
if (modal == NULL)
hovered_window_above_modal = true;
@@ -4152,13 +4227,9 @@ void ImGui::LogText(const char* fmt, ...)
va_list args;
va_start(args, fmt);
if (g.LogFile)
- {
vfprintf(g.LogFile, fmt, args);
- }
else
- {
- g.LogClipboard->appendfv(fmt, args);
- }
+ g.LogClipboard.appendfv(fmt, args);
va_end(args);
}
@@ -4234,8 +4305,7 @@ void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool
text_display_end = text_end;
}
- const int text_len = (int)(text_display_end - text);
- if (text_len > 0)
+ if (text != text_display_end)
{
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
if (g.LogEnabled)
@@ -4251,8 +4321,7 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end
if (!text_end)
text_end = text + strlen(text); // FIXME-OPT
- const int text_len = (int)(text_end - text);
- if (text_len > 0)
+ if (text != text_end)
{
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
if (g.LogEnabled)
@@ -4498,7 +4567,7 @@ static ImGuiWindow* FindHoveredWindow()
continue;
// Using the clipped AABB, a child window will typically be clipped by its parent (not always)
- ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
+ ImRect bb(window->OuterRectClipped.Min - g.Style.TouchExtraPadding, window->OuterRectClipped.Max + g.Style.TouchExtraPadding);
if (bb.Contains(g.IO.MousePos))
return window;
}
@@ -4511,12 +4580,11 @@ static ImGuiWindow* FindHoveredWindow()
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
// Clip
ImRect rect_clipped(r_min, r_max);
if (clip)
- rect_clipped.ClipWith(window->ClipRect);
+ rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
// Expand for touch input
const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
@@ -4535,7 +4603,7 @@ int ImGui::GetKeyIndex(ImGuiKey imgui_key)
return GImGui->IO.KeyMap[imgui_key];
}
-// Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your back-end/engine stored them into KeyDown[]!
+// Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
bool ImGui::IsKeyDown(int user_key_index)
{
if (user_key_index < 0) return false;
@@ -4610,7 +4678,7 @@ bool ImGui::IsMouseClicked(int button, bool repeat)
if (repeat && t > g.IO.KeyRepeatDelay)
{
float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
- if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
+ if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
return true;
}
@@ -4848,7 +4916,7 @@ void ImGui::OpenPopupEx(ImGuiID id)
popup_ref.OpenFrameCount = g.FrameCount;
popup_ref.OpenParentId = parent_window->IDStack.back();
popup_ref.OpenMousePos = g.IO.MousePos;
- popup_ref.OpenPopupPos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos;
+ popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
//printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
if (g.OpenPopupStack.Size < current_stack_size + 1)
@@ -4913,7 +4981,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
ClosePopupToLevel(n);
}
-static ImGuiWindow* GetFrontMostModalRootWindow()
+ImGuiWindow* ImGui::GetFrontMostPopupModal()
{
ImGuiContext& g = *GImGui;
for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
@@ -5058,7 +5126,7 @@ bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
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); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
+ 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);
return true;
}
@@ -5072,7 +5140,7 @@ bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
{
ImGuiWindow* window = GImGui->CurrentWindow;
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); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
+ IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
OpenPopupEx(id);
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
@@ -5168,8 +5236,7 @@ void ImGui::EndChild()
}
else
{
- // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting.
- ImVec2 sz = GetWindowSize();
+ ImVec2 sz = window->Size;
if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
sz.x = ImMax(4.0f, sz.x);
if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
@@ -5224,9 +5291,10 @@ static void CheckStacksSize(ImGuiWindow* window, bool write)
{ int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
{ int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
{ int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
- { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
- { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
- { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
+ // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
+ { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
+ { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
+ { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
}
@@ -5236,15 +5304,18 @@ enum ImGuiPopupPositionPolicy
ImGuiPopupPositionPolicy_ComboBox
};
-static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default)
+static ImRect FindAllowedExtentRectForWindow(ImGuiWindow*)
+{
+ ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
+ ImRect r_screen = GetViewportRect();
+ r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
+ return r_screen;
+}
+
+// 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.)
+// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
+static ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default)
{
- const ImGuiStyle& style = GImGui->Style;
-
- // 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.)
- // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
- ImVec2 safe_padding = style.DisplaySafeAreaPadding;
- ImRect r_outer(GetViewportRect());
- r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? -safe_padding.y : 0.0f));
ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
//GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
//GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
@@ -5296,6 +5367,49 @@ static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& siz
return pos;
}
+static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+
+ ImRect r_outer = FindAllowedExtentRectForWindow(window);
+ if (window->Flags & ImGuiWindowFlags_ChildMenu)
+ {
+ // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds.
+ // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
+ IM_ASSERT(g.CurrentWindow == window);
+ ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
+ float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
+ ImRect r_avoid;
+ if (parent_window->DC.MenuBarAppending)
+ r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
+ else
+ r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
+ return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
+ }
+ if (window->Flags & ImGuiWindowFlags_Popup)
+ {
+ ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
+ return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
+ }
+ if (window->Flags & ImGuiWindowFlags_Tooltip)
+ {
+ // Position tooltip (always follows mouse)
+ float sc = g.Style.MouseCursorScale;
+ ImVec2 ref_pos = NavCalcPreferredRefPos();
+ ImRect r_avoid;
+ if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
+ r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
+ else
+ r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
+ ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
+ if (window->AutoPosLastDirection == ImGuiDir_None)
+ pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
+ return pos;
+ }
+ IM_ASSERT(0);
+ return window->Pos;
+}
+
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
{
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
@@ -5319,21 +5433,20 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
window->Flags = flags;
g.WindowsById.SetVoidPtr(window->ID, window);
+ // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
+ window->Pos = ImVec2(60, 60);
+
// User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
if (!(flags & ImGuiWindowFlags_NoSavedSettings))
{
// Retrieve settings from .ini file
- // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
- window->Pos = window->PosFloat = ImVec2(60, 60);
-
if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
{
SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
- window->PosFloat = settings->Pos;
- window->Pos = ImFloor(window->PosFloat);
+ window->Pos = ImFloor(settings->Pos);
window->Collapsed = settings->Collapsed;
if (ImLengthSqr(settings->Size) > 0.00001f)
- size = settings->Size;
+ size = ImFloor(settings->Size);
}
}
window->Size = window->SizeFull = window->SizeFullAtLastBegin = size;
@@ -5401,24 +5514,22 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
{
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
- ImGuiWindowFlags flags = window->Flags;
- ImVec2 size_auto_fit;
- if ((flags & ImGuiWindowFlags_Tooltip) != 0)
+ if (window->Flags & ImGuiWindowFlags_Tooltip)
{
- // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
- size_auto_fit = size_contents;
+ // Tooltip always resize
+ return size_contents;
}
else
{
// When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
- size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
+ ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding * 2.0f));
ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
- if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
+ if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
size_auto_fit.y += style.ScrollbarSize;
- if (size_auto_fit_after_constraint.y < size_contents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
+ if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
size_auto_fit.x += style.ScrollbarSize;
+ return size_auto_fit;
}
- return size_auto_fit;
}
static float GetScrollMaxX(ImGuiWindow* window)
@@ -5431,15 +5542,26 @@ static float GetScrollMaxY(ImGuiWindow* window)
return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
}
-static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
+static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
{
+ ImGuiContext& g = *GImGui;
ImVec2 scroll = window->Scroll;
- float cr_x = window->ScrollTargetCenterRatio.x;
- float cr_y = window->ScrollTargetCenterRatio.y;
if (window->ScrollTarget.x < FLT_MAX)
+ {
+ float cr_x = window->ScrollTargetCenterRatio.x;
scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
+ }
if (window->ScrollTarget.y < FLT_MAX)
- scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
+ {
+ // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
+ float cr_y = window->ScrollTargetCenterRatio.y;
+ float target_y = window->ScrollTarget.y;
+ if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
+ target_y = 0.0f;
+ if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
+ target_y = window->SizeContents.y;
+ scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
+ }
scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
if (!window->Collapsed && !window->SkipItems)
{
@@ -5474,9 +5596,9 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co
struct ImGuiResizeGripDef
{
- ImVec2 CornerPos;
- ImVec2 InnerDir;
- int AngleMin12, AngleMax12;
+ ImVec2 CornerPos;
+ ImVec2 InnerDir;
+ int AngleMin12, AngleMax12;
};
const ImGuiResizeGripDef resize_grip_def[4] =
@@ -5523,7 +5645,8 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au
// Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size);
- resize_rect.FixInverted();
+ if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
+ if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
bool hovered, held;
ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
if (hovered || held)
@@ -5598,7 +5721,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au
}
if (pos_target.x != FLT_MAX)
{
- window->Pos = window->PosFloat = ImFloor(pos_target);
+ window->Pos = ImFloor(pos_target);
MarkIniSettingsDirty(window);
}
@@ -5695,14 +5818,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
}
- g.NextWindowData.PosCond = 0;
}
if (g.NextWindowData.SizeCond)
{
window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
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);
- g.NextWindowData.SizeCond = 0;
}
if (g.NextWindowData.ContentSizeCond)
{
@@ -5710,22 +5831,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
if (window->SizeContentsExplicit.y != 0.0f)
window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
- g.NextWindowData.ContentSizeCond = 0;
}
else if (first_begin_of_the_frame)
{
window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
}
if (g.NextWindowData.CollapsedCond)
- {
SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
- g.NextWindowData.CollapsedCond = 0;
- }
if (g.NextWindowData.FocusCond)
- {
- SetWindowFocus();
- g.NextWindowData.FocusCond = 0;
- }
+ FocusWindow(window);
if (window->Appearing)
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
@@ -5752,12 +5866,41 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->LastFrameActive = current_frame;
window->IDStack.resize(1);
- // Lock window rounding, border size and rounding so that altering the border sizes for children doesn't have side-effects.
- window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
+ // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
+
+ // Update contents size from last frame for auto-fitting (or use explicit size)
+ window->SizeContents = CalcSizeContents(window);
+ if (window->HiddenFrames > 0)
+ window->HiddenFrames--;
+
+ // Hide new windows for one frame until they calculate their size
+ if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
+ window->HiddenFrames = 1;
+
+ // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
+ // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
+ if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
+ {
+ window->HiddenFrames = 1;
+ if (flags & ImGuiWindowFlags_AlwaysAutoResize)
+ {
+ if (!window_size_x_set_by_api)
+ window->Size.x = window->SizeFull.x = 0.f;
+ if (!window_size_y_set_by_api)
+ window->Size.y = window->SizeFull.y = 0.f;
+ window->SizeContents = ImVec2(0.f, 0.f);
+ }
+ }
+
+ SetCurrentWindow(window);
+
+ // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
window->WindowPadding = style.WindowPadding;
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
+ window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
+ window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
// Collapse window by double-clicking on title bar
// At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
@@ -5779,29 +5922,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// SIZE
- // Update contents size from last frame for auto-fitting (unless explicitly specified)
- window->SizeContents = CalcSizeContents(window);
-
- // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
- if (window->HiddenFrames > 0)
- window->HiddenFrames--;
- if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
- {
- window->HiddenFrames = 1;
- if (flags & ImGuiWindowFlags_AlwaysAutoResize)
- {
- if (!window_size_x_set_by_api)
- window->Size.x = window->SizeFull.x = 0.f;
- if (!window_size_y_set_by_api)
- window->Size.y = window->SizeFull.y = 0.f;
- window->SizeContents = ImVec2(0.f, 0.f);
- }
- }
-
- // Hide new windows for one frame until they calculate their size
- if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
- window->HiddenFrames = 1;
-
// Calculate auto-fit size, handle automatic resize
const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
@@ -5851,7 +5971,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
window->AutoPosLastDirection = ImGuiDir_None;
if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
- window->Pos = window->PosFloat = g.CurrentPopupStack.back().OpenPopupPos;
+ window->Pos = g.CurrentPopupStack.back().OpenPopupPos;
}
// Position child window
@@ -5860,67 +5980,33 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
parent_window->DC.ChildWindows.push_back(window);
if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
- window->Pos = window->PosFloat = parent_window->DC.CursorPos;
+ window->Pos = parent_window->DC.CursorPos;
}
const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0);
if (window_pos_with_pivot)
- {
- // Position given a pivot (e.g. for centering)
- SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0);
- }
- else if (flags & ImGuiWindowFlags_ChildMenu)
- {
- // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds.
- // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
- IM_ASSERT(window_pos_set_by_api);
- float horizontal_overlap = style.ItemSpacing.x; // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value).
- ImGuiWindow* parent_menu = parent_window_in_stack;
- ImRect rect_to_avoid;
- if (parent_menu->DC.MenuBarAppending)
- rect_to_avoid = ImRect(-FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight(), FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight() + parent_menu->MenuBarHeight());
- else
- rect_to_avoid = ImRect(parent_menu->Pos.x + horizontal_overlap, -FLT_MAX, parent_menu->Pos.x + parent_menu->Size.x - horizontal_overlap - parent_menu->ScrollbarSizes.x, FLT_MAX);
- window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
- }
+ SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
+ else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
+ window->Pos = FindBestWindowPosForPopup(window);
else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
- {
- ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
- window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
- }
-
- // Position tooltip (always follows mouse)
- if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
- {
- float sc = g.Style.MouseCursorScale;
- ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos;
- ImRect rect_to_avoid;
- if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
- rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
- else
- rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
- window->PosFloat = FindBestWindowPosForPopup(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
- if (window->AutoPosLastDirection == ImGuiDir_None)
- window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
- }
+ window->Pos = FindBestWindowPosForPopup(window);
+ else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
+ window->Pos = FindBestWindowPosForPopup(window);
// Clamp position so it stays visible
- if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
+ if (!(flags & ImGuiWindowFlags_ChildWindow))
{
if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
{
ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
- window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size;
- window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding);
+ window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size;
+ window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
}
}
- window->Pos = ImFloor(window->PosFloat);
+ window->Pos = ImFloor(window->Pos);
- // Default item width. Make it proportional to window size if window manually resizes
- if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
- window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
- else
- window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
+ // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
+ window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
// Prepare for focus requests
window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
@@ -5929,7 +6015,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
// Apply scrolling
- window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
+ window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
// Apply focus, new windows appears in front
@@ -5946,6 +6032,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (!window->Collapsed)
UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
+ // Default item width. Make it proportional to window size if window manually resizes
+ if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
+ window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
+ else
+ window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
+
// DRAWING
// Setup draw list and outer clipping rectangle
@@ -5959,7 +6051,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
// Draw modal window background (darkens what is behind them)
- if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
+ if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostPopupModal())
window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
// Draw navigation selection/windowing rectangle background
@@ -6033,7 +6125,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Borders
if (window_border_size > 0.0f)
- window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
+ window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
if (border_held != -1)
{
ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f);
@@ -6060,11 +6152,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
window->SizeFullAtLastBegin = window->SizeFull;
- // Update ContentsRegionMax. All the variable it depends on are set above in this function.
- window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x;
- window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
- window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
- window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
+ // Update various regions. Variables they depends on are set above in this function.
+ // FIXME: window->ContentsRegion.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
+ window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
+ window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
+ window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
+ window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
// Setup drawing context
// (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
@@ -6082,7 +6175,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
window->DC.NavLayerActiveMaskNext = 0x00;
window->DC.MenuBarAppending = false;
- window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x);
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
window->DC.ChildWindows.resize(0);
window->DC.LayoutType = ImGuiLayoutType_Vertical;
@@ -6132,7 +6224,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
ImGuiID id = window->GetID("#COLLAPSE");
ImRect bb(window->Pos + style.FramePadding + ImVec2(1,1), window->Pos + style.FramePadding + ImVec2(g.FontSize,g.FontSize) - ImVec2(1,1));
- ItemAdd(bb, id); // To allow navigation
+ ItemAdd(bb, id);
if (ButtonBehavior(bb, id, NULL, NULL))
window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function
RenderNavHighlight(bb, id);
@@ -6152,12 +6244,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DC.NavLayerCurrentMask >>= 1;
window->DC.ItemFlags = item_flags_backup;
- // Title text (FIXME: refactor text alignment facilities along with RenderText helpers)
+ // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.)
ImVec2 text_size = CalcTextSize(name, NULL, true);
ImRect text_r = title_bar_rect;
- float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
- float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : style.FramePadding.x;
- if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
+ float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
+ float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
+ if (style.WindowTitleAlign.x > 0.0f)
+ pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
text_r.Min.x += pad_left;
text_r.Max.x -= pad_right;
ImRect clip_rect = text_r;
@@ -6166,8 +6259,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
// Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
- window->WindowRectClipped = window->Rect();
- window->WindowRectClipped.ClipWith(window->ClipRect);
+ window->OuterRectClipped = window->Rect();
+ window->OuterRectClipped.ClipWith(window->ClipRect);
// Pressing CTRL+C while holding on a window copy its content to the clipboard
// This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
@@ -6180,21 +6273,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Inner rectangle
// We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
- // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
- window->InnerRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
- window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
- window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
- window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
+ // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
+ window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
+ window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
+ window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
+ window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
//window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
// Inner clipping rectangle
// Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
- window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
- window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y);
- window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
- window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y);
+ window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
+ window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
+ window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
+ window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
- // After Begin() we fill the last item / hovered data using the title bar data. Make that a standard behavior (to allow usage of context menus on title bar only, etc.).
+ // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.).
window->DC.LastItemId = window->MoveId;
window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
window->DC.LastItemRect = title_bar_rect;
@@ -6207,17 +6300,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->WriteAccessed = false;
window->BeginCount++;
- g.NextWindowData.SizeConstraintCond = 0;
+ g.NextWindowData.Clear();
// Child window can be out of sight and have "negative" clip windows.
- // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar).
+ // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
if (flags & ImGuiWindowFlags_ChildWindow)
{
IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
window->Collapsed = parent_window && parent_window->Collapsed;
if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
- window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y);
+ window->Collapsed |= (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y);
// We also hide the window from rendering because we've already added its border to the command list.
// (we could perform the check earlier in the function but it is simpler at this point)
@@ -6883,7 +6976,6 @@ static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
// Set
const ImVec2 old_pos = window->Pos;
- window->PosFloat = pos;
window->Pos = ImFloor(pos);
window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
@@ -7060,7 +7152,7 @@ void ImGui::SetNextWindowBgAlpha(float alpha)
ImVec2 ImGui::GetContentRegionMax()
{
ImGuiWindow* window = GetCurrentWindowRead();
- ImVec2 mx = window->ContentsRegionRect.Max;
+ ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
if (window->DC.ColumnsSet)
mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
return mx;
@@ -7081,19 +7173,19 @@ float ImGui::GetContentRegionAvailWidth()
ImVec2 ImGui::GetWindowContentRegionMin()
{
ImGuiWindow* window = GetCurrentWindowRead();
- return window->ContentsRegionRect.Min;
+ return window->ContentsRegionRect.Min - window->Pos;
}
ImVec2 ImGui::GetWindowContentRegionMax()
{
ImGuiWindow* window = GetCurrentWindowRead();
- return window->ContentsRegionRect.Max;
+ return window->ContentsRegionRect.Max - window->Pos;
}
float ImGui::GetWindowContentRegionWidth()
{
ImGuiWindow* window = GetCurrentWindowRead();
- return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x;
+ return window->ContentsRegionRect.GetWidth();
}
float ImGui::GetTextLineHeight()
@@ -7250,12 +7342,6 @@ void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
window->ScrollTargetCenterRatio.y = center_y_ratio;
-
- // Minor hack to to make scrolling to top/bottom of window take account of WindowPadding, it looks more right to the user this way
- if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y)
- window->ScrollTarget.y = 0.0f;
- else if (center_y_ratio >= 1.0f && window->ScrollTarget.y >= window->SizeContents.y - window->WindowPadding.y + GImGui->Style.ItemSpacing.y)
- window->ScrollTarget.y = window->SizeContents.y;
}
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
@@ -7942,10 +8028,10 @@ void ImGui::LogFinish()
fclose(g.LogFile);
g.LogFile = NULL;
}
- if (g.LogClipboard->size() > 1)
+ if (g.LogClipboard.size() > 1)
{
- SetClipboardText(g.LogClipboard->begin());
- g.LogClipboard->clear();
+ SetClipboardText(g.LogClipboard.begin());
+ g.LogClipboard.clear();
}
g.LogEnabled = false;
}
@@ -7980,7 +8066,7 @@ bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
if (flags & ImGuiTreeNodeFlags_Leaf)
return true;
- // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions)
+ // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions)
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiStorage* storage = window->DC.StateStorage;
@@ -8410,73 +8496,83 @@ void ImGui::BulletText(const char* fmt, ...)
va_end(args);
}
-static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size)
-{
- if (data_type == ImGuiDataType_Int)
- ImFormatString(buf, buf_size, display_format, *(int*)data_ptr);
- else if (data_type == ImGuiDataType_Float)
- ImFormatString(buf, buf_size, display_format, *(float*)data_ptr);
- else if (data_type == ImGuiDataType_Double)
- ImFormatString(buf, buf_size, display_format, *(double*)data_ptr);
-}
-
-static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size)
+static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format)
{
- if (data_type == ImGuiDataType_Int)
- {
- if (decimal_precision < 0)
- ImFormatString(buf, buf_size, "%d", *(int*)data_ptr);
- else
- ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr);
- }
- else if (data_type == ImGuiDataType_Float)
- {
- if (decimal_precision < 0)
- ImFormatString(buf, buf_size, "%f", *(float*)data_ptr); // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits?
- else
- ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr);
- }
- else if (data_type == ImGuiDataType_Double)
- {
- if (decimal_precision < 0)
- ImFormatString(buf, buf_size, "%f", *(double*)data_ptr);
- else
- ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(double*)data_ptr);
- }
+ if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument
+ return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr);
+ if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) // Signedness doesn't matter when pushing the argument
+ return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr);
+ if (data_type == ImGuiDataType_Float)
+ return ImFormatString(buf, buf_size, format, *(const float*)data_ptr);
+ if (data_type == ImGuiDataType_Double)
+ return ImFormatString(buf, buf_size, format, *(const double*)data_ptr);
+ IM_ASSERT(0);
+ return 0;
}
+// FIXME: Adding support for clamping on boundaries of the data type would be nice.
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2)
{
IM_ASSERT(op == '+' || op == '-');
- if (data_type == ImGuiDataType_Int)
- {
- if (op == '+') *(int*)output = *(int*)arg1 + *(const int*)arg2;
- else if (op == '-') *(int*)output = *(int*)arg1 - *(const int*)arg2;
- }
- else if (data_type == ImGuiDataType_Float)
+ switch (data_type)
{
- if (op == '+') *(float*)output = *(float*)arg1 + *(const float*)arg2;
- else if (op == '-') *(float*)output = *(float*)arg1 - *(const float*)arg2;
- }
- else if (data_type == ImGuiDataType_Double)
- {
- if (op == '+') *(double*)output = *(double*)arg1 + *(const double*)arg2;
- else if (op == '-') *(double*)output = *(double*)arg1 - *(const double*)arg2;
+ case ImGuiDataType_S32:
+ if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2;
+ else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2;
+ return;
+ case ImGuiDataType_U32:
+ if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2;
+ else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2;
+ return;
+ case ImGuiDataType_S64:
+ if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2;
+ else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2;
+ return;
+ case ImGuiDataType_U64:
+ if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2;
+ else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2;
+ return;
+ case ImGuiDataType_Float:
+ if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2;
+ else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2;
+ return;
+ case ImGuiDataType_Double:
+ if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2;
+ else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2;
+ return;
+ case ImGuiDataType_COUNT: break;
}
+ IM_ASSERT(0);
}
-static size_t GDataTypeSize[ImGuiDataType_COUNT] =
+struct ImGuiDataTypeInfo
{
- sizeof(int),
- sizeof(float),
- sizeof(double)
+ size_t Size;
+ const char* PrintFmt; // Unused
+ const char* ScanFmt;
};
+static const ImGuiDataTypeInfo GDataTypeInfo[] =
+{
+ { sizeof(int), "%d", "%d" },
+ { sizeof(unsigned int), "%u", "%u" },
+#ifdef _MSC_VER
+ { sizeof(ImS64), "%I64d","%I64d" },
+ { sizeof(ImU64), "%I64u","%I64u" },
+#else
+ { sizeof(ImS64), "%lld", "%lld" },
+ { sizeof(ImU64), "%llu", "%llu" },
+#endif
+ { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg
+ { sizeof(double), "%f", "%lf" },
+};
+IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT);
+
// User can input math operators (e.g. +100) to edit a numerical values.
-// NB: This is _not_ a full expression evaluator. We should probably add one though..
-static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format)
+// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
+static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format)
{
- while (ImCharIsSpace((unsigned int)*buf))
+ while (ImCharIsBlankA(*buf))
buf++;
// We don't support '-' op because it would conflict with inputing negative value.
@@ -8485,7 +8581,7 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
if (op == '+' || op == '*' || op == '/')
{
buf++;
- while (ImCharIsSpace((unsigned int)*buf))
+ while (ImCharIsBlankA(*buf))
buf++;
}
else
@@ -8495,35 +8591,44 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
if (!buf[0])
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];
- IM_ASSERT(GDataTypeSize[data_type] <= sizeof(data_backup));
- memcpy(data_backup, data_ptr, GDataTypeSize[data_type]);
+ IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup));
+ memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size);
- if (data_type == ImGuiDataType_Int)
+ if (format == NULL)
+ format = GDataTypeInfo[data_type].ScanFmt;
+
+ int arg1i = 0;
+ if (data_type == ImGuiDataType_S32)
{
- if (!scalar_format)
- scalar_format = "%d";
int* v = (int*)data_ptr;
int arg0i = *v;
- if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1)
+ float arg1f = 0.0f;
+ if (op && sscanf(initial_value_buf, format, &arg0i) < 1)
return false;
// Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
- float arg1f = 0.0f;
- if (op == '+') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i + arg1f); } // Add (use "+-" to subtract)
- else if (op == '*') { if (sscanf(buf, "%f", &arg1f) == 1) *v = (int)(arg0i * arg1f); } // Multiply
- else if (op == '/') { if (sscanf(buf, "%f", &arg1f) == 1 && arg1f != 0.0f) *v = (int)(arg0i / arg1f); }// Divide
- else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign integer constant
+ if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract)
+ else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply
+ else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide
+ else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant
+ }
+ else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)
+ {
+ // Assign constant
+ // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future.
+ sscanf(buf, format, data_ptr);
}
else if (data_type == ImGuiDataType_Float)
{
// For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
- scalar_format = "%f";
+ format = "%f";
float* v = (float*)data_ptr;
float arg0f = *v, arg1f = 0.0f;
- if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
+ if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
return false;
- if (sscanf(buf, scalar_format, &arg1f) < 1)
+ if (sscanf(buf, format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
else if (op == '*') { *v = arg0f * arg1f; } // Multiply
@@ -8532,24 +8637,24 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
}
else if (data_type == ImGuiDataType_Double)
{
- scalar_format = "%lf";
+ format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis
double* v = (double*)data_ptr;
- double arg0f = *v, arg1f = 0.0f;
- if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1)
+ double arg0f = *v, arg1f = 0.0;
+ if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
return false;
- if (sscanf(buf, scalar_format, &arg1f) < 1)
+ if (sscanf(buf, format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
else if (op == '*') { *v = arg0f * arg1f; } // Multiply
else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
else { *v = arg1f; } // Assign constant
}
- return memcmp(data_backup, data_ptr, GDataTypeSize[data_type]) != 0;
+ return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0;
}
// Create text input in place of a slider (when CTRL+Clicking on slider)
// FIXME: Logic is messy and confusing.
-bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision)
+bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@@ -8561,93 +8666,151 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label
SetHoveredID(0);
FocusableItemUnregister(window);
- char buf[32];
- DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf));
- bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll);
+ char fmt_buf[32];
+ char data_buf[32];
+ format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf));
+ DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format);
+ ImStrTrimBlanks(data_buf);
+ ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal);
+ bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags);
if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget
{
- IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible)
+ IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID
g.ScalarAsInputTextId = g.ActiveId;
SetHoveredID(id);
}
- if (text_value_changed)
- return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
+ if (value_changed)
+ return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
return false;
}
-// Parse display precision back from the display format string
-int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
+// We don't use strchr() because our strings are usually very short and often start with '%'
+const char* ImParseFormatFindStart(const char* fmt)
{
- int precision = default_precision;
- while ((fmt = strchr(fmt, '%')) != NULL)
+ while (char c = fmt[0])
{
- fmt++;
- if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%"
- while (*fmt >= '0' && *fmt <= '9')
+ if (c == '%' && fmt[1] != '%')
+ return fmt;
+ else if (c == '%')
fmt++;
- if (*fmt == '.')
- {
- fmt = ImAtoi(fmt + 1, &precision);
- if (precision < 0 || precision > 10)
- precision = default_precision;
- }
- if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
- precision = -1;
- break;
+ fmt++;
}
- return precision;
+ return fmt;
}
-static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
+const char* ImParseFormatFindEnd(const char* fmt)
{
- 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 };
- return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision);
+ // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.
+ if (fmt[0] != '%')
+ return fmt;
+ const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A'));
+ const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));
+ for (char c; (c = *fmt) != 0; fmt++)
+ {
+ if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0)
+ return fmt + 1;
+ if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0)
+ return fmt + 1;
+ }
+ return fmt;
+}
+
+// Extract the format out of a format string with leading or trailing decorations
+// fmt = "blah blah" -> return fmt
+// fmt = "%.3f" -> return fmt
+// fmt = "hello %.3f" -> return fmt + 6
+// fmt = "%.3f hello" -> return buf written with "%.3f"
+const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size)
+{
+ const char* fmt_start = ImParseFormatFindStart(fmt);
+ if (fmt_start[0] != '%')
+ return fmt;
+ const char* fmt_end = ImParseFormatFindEnd(fmt_start);
+ if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data.
+ return fmt_start;
+ ImStrncpy(buf, fmt_start, ImMin((int)(fmt_end + 1 - fmt_start), buf_size));
+ return buf;
}
-float ImGui::RoundScalar(float value, int decimal_precision)
+// Parse display precision back from the display format string
+// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed.
+int ImParseFormatPrecision(const char* fmt, int default_precision)
+{
+ fmt = ImParseFormatFindStart(fmt);
+ if (fmt[0] != '%')
+ return default_precision;
+ fmt++;
+ while (*fmt >= '0' && *fmt <= '9')
+ fmt++;
+ int precision = INT_MAX;
+ if (*fmt == '.')
+ {
+ fmt = ImAtoi<int>(fmt + 1, &precision);
+ if (precision < 0 || precision > 99)
+ precision = default_precision;
+ }
+ if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
+ precision = -1;
+ if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX)
+ precision = -1;
+ return (precision == INT_MAX) ? default_precision : precision;
+}
+
+static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
{
- // Round past decimal precision
- // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
- // FIXME: Investigate better rounding methods
+ 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 };
if (decimal_precision < 0)
- return value;
- const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision);
- bool negative = value < 0.0f;
- value = fabsf(value);
- float remainder = fmodf(value, min_step);
- if (remainder <= min_step*0.5f)
- value -= remainder;
+ return FLT_MIN;
+ return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision);
+}
+
+template<typename TYPE, typename SIGNEDTYPE>
+static inline TYPE RoundScalarWithFormat(const char* format, ImGuiDataType data_type, TYPE v)
+{
+ const char* fmt_start = ImParseFormatFindStart(format);
+ if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string
+ return v;
+ char v_str[64];
+ ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v);
+ const char* p = v_str;
+ while (*p == ' ')
+ p++;
+ if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
+ v = (TYPE)ImAtof(p);
else
- value += (min_step - remainder);
- return negative ? -value : value;
+ ImAtoi(p, (SIGNEDTYPE*)&v);
+ return v;
}
-static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos)
+template<typename TYPE, typename FLOATTYPE>
+static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos)
{
if (v_min == v_max)
return 0.0f;
- const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
- const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
- if (is_non_linear)
+ const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
+ const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
+ if (is_power)
{
if (v_clamped < 0.0f)
{
- const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min);
- return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos;
+ 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;
}
else
{
- const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min));
- return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos);
+ 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);
}
}
// Linear slider
- return (v_clamped - v_min) / (v_max - v_min);
+ return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min));
}
-bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags)
+// FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc.
+template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
+static 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)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
@@ -8655,31 +8818,32 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v
// Draw frame
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
- RenderNavHighlight(frame_bb, id);
- RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
+ RenderNavHighlight(bb, id);
+ RenderFrame(bb.Min, bb.Max, frame_col, true, style.FrameRounding);
- const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f);
const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0;
+ const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
+ const bool is_power = (power != 1.0f) && is_decimal;
const float grab_padding = 2.0f;
- const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
- float grab_sz;
- if (decimal_precision != 0)
- grab_sz = ImMin(style.GrabMinSize, slider_sz);
- else
- grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit
+ const float slider_sz = is_horizontal ? (bb.GetWidth() - grab_padding * 2.0f) : (bb.GetHeight() - grab_padding * 2.0f);
+ float grab_sz = style.GrabMinSize;
+ SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
+ if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows
+ grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit
+ grab_sz = ImMin(grab_sz, slider_sz);
const float slider_usable_sz = slider_sz - grab_sz;
- const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f;
- const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f;
+ const float slider_usable_pos_min = (is_horizontal ? bb.Min.x : bb.Min.y) + grab_padding + grab_sz*0.5f;
+ const float slider_usable_pos_max = (is_horizontal ? bb.Max.x : bb.Max.y) - grab_padding - grab_sz*0.5f;
- // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f
- float linear_zero_pos = 0.0f; // 0.0->1.0f
- if (v_min * v_max < 0.0f)
+ // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f
+ float linear_zero_pos; // 0.0->1.0f
+ if (is_power && v_min * v_max < 0.0f)
{
// Different sign
- const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power);
- const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power);
- linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0);
+ const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power);
+ const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power);
+ linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0));
}
else
{
@@ -8718,20 +8882,20 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v
}
else if (delta != 0.0f)
{
- clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
- if (decimal_precision == 0 && !is_non_linear)
- {
- if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_TweakSlow))
- delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps
- else
- delta /= 100.0f;
- }
- else
+ clicked_t = SliderBehaviorCalcRatioFromValue<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
+ if (is_decimal || is_power)
{
delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds
if (IsNavInputDown(ImGuiNavInput_TweakSlow))
delta /= 10.0f;
}
+ else
+ {
+ if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow))
+ delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps
+ else
+ delta /= 100.0f;
+ }
if (IsNavInputDown(ImGuiNavInput_TweakFast))
delta *= 10.0f;
set_new_value = true;
@@ -8744,66 +8908,133 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v
if (set_new_value)
{
- float new_value;
- if (is_non_linear)
+ TYPE v_new;
+ if (is_power)
{
- // Account for logarithmic scale on both sides of the zero
+ // Account for power curve scale on both sides of the zero
if (clicked_t < linear_zero_pos)
{
// Negative: rescale to the negative range before powering
float a = 1.0f - (clicked_t / linear_zero_pos);
- a = powf(a, power);
- new_value = ImLerp(ImMin(v_max,0.0f), v_min, a);
+ a = ImPow(a, power);
+ v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a);
}
else
{
// Positive: rescale to the positive range before powering
float a;
- if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f)
+ if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f)
a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
else
a = clicked_t;
- a = powf(a, power);
- new_value = ImLerp(ImMax(v_min,0.0f), v_max, a);
+ a = ImPow(a, power);
+ v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a);
}
}
else
{
// Linear slider
- new_value = ImLerp(v_min, v_max, clicked_t);
+ if (is_decimal)
+ {
+ v_new = ImLerp(v_min, v_max, clicked_t);
+ }
+ else
+ {
+ // For integer values we want the clicking position to match the grab box so we round above
+ // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
+ FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t;
+ TYPE v_new_off_floor = (TYPE)(v_new_off_f);
+ TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);
+ if (!is_decimal && v_new_off_floor < v_new_off_round)
+ v_new = v_min + v_new_off_round;
+ else
+ v_new = v_min + v_new_off_floor;
+ }
}
- // Round past decimal precision
- new_value = RoundScalar(new_value, decimal_precision);
- if (*v != new_value)
+ // Round to user desired precision based on format string
+ v_new = RoundScalarWithFormat<TYPE,SIGNEDTYPE>(format, data_type, v_new);
+
+ // Apply result
+ if (*v != v_new)
{
- *v = new_value;
+ *v = v_new;
value_changed = true;
}
}
}
// Draw
- float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos);
+ float grab_t = SliderBehaviorCalcRatioFromValue<TYPE,FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
if (!is_horizontal)
grab_t = 1.0f - grab_t;
const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
ImRect grab_bb;
if (is_horizontal)
- grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding));
+ grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding);
else
- grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f));
+ grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f);
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
return value_changed;
}
-// Use power!=1.0 for logarithmic sliders.
-// Adjust display_format to decorate the value with a prefix or a suffix.
-// "%.3f" 1.234
-// "%5.2f secs" 01.23 secs
-// "Gold: %.0f" Gold: 1
-bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power)
+// For 32-bits and larger types, slider bounds are limited to half the natural type range.
+// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2.
+// It would be possible to life that limitation with some work but it doesn't seem to be work it for sliders.
+bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags)
+{
+ switch (data_type)
+ {
+ case ImGuiDataType_S32:
+ IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2);
+ return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags);
+ case ImGuiDataType_U32:
+ IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2);
+ return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags);
+ case ImGuiDataType_S64:
+ IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2);
+ return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags);
+ case ImGuiDataType_U64:
+ IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2);
+ return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags);
+ case ImGuiDataType_Float:
+ IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f);
+ return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags);
+ case ImGuiDataType_Double:
+ IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f);
+ return SliderBehaviorT<double,double,double>(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags);
+ case ImGuiDataType_COUNT: break;
+ }
+ IM_ASSERT(0);
+ return false;
+}
+
+// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f".
+// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls.
+// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?!
+static const char* PatchFormatStringFloatToInt(const char* fmt)
+{
+ if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case.
+ return "%d";
+ const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%)
+ const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user).
+ if (fmt_end > fmt_start && fmt_end[-1] == 'f')
+ {
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+ if (fmt_start == fmt && fmt_end[0] == 0)
+ return "%d";
+ ImGuiContext& g = *GImGui;
+ ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision.
+ return g.TempBuffer;
+#else
+ IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d"
+#endif
+ }
+ return fmt;
+}
+
+bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -8824,15 +9055,19 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
ItemSize(total_bb, style.FramePadding.y);
return false;
}
- const bool hovered = ItemHoverable(frame_bb, id);
- if (!display_format)
- display_format = "%.3f";
- int decimal_precision = ParseFormatPrecision(display_format, 3);
+ // Default format string when passing NULL
+ // Patch old "%.0f" format string to use "%d", read function comments for more details.
+ IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
+ if (format == NULL)
+ format = GDataTypeInfo[data_type].PrintFmt;
+ else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
+ format = PatchFormatStringFloatToInt(format);
// Tabbing or CTRL-clicking on Slider turns it into an input box
bool start_text_input = false;
const bool tab_focus_requested = FocusableItemRegister(window, id);
+ const bool hovered = ItemHoverable(frame_bb, id);
if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id))
{
SetActiveID(id, window);
@@ -8846,15 +9081,15 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
}
}
if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
- return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
+ return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
// Actual slider behavior + render grab
ItemSize(total_bb, style.FramePadding.y);
- const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
+ const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power);
// 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 + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
+ const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
if (label_size.x > 0.0f)
@@ -8863,7 +9098,12 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
return value_changed;
}
-bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power)
+bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power)
+{
+ return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
+}
+
+bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -8880,12 +9120,16 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float
ItemSize(bb, style.FramePadding.y);
if (!ItemAdd(frame_bb, id))
return false;
- const bool hovered = ItemHoverable(frame_bb, id);
- if (!display_format)
- display_format = "%.3f";
- int decimal_precision = ParseFormatPrecision(display_format, 3);
+ // Default format string when passing NULL
+ // Patch old "%.0f" format string to use "%d", read function comments for more details.
+ IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
+ if (format == NULL)
+ format = GDataTypeInfo[data_type].PrintFmt;
+ else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
+ format = PatchFormatStringFloatToInt(format);
+ const bool hovered = ItemHoverable(frame_bb, id);
if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id)
{
SetActiveID(id, window);
@@ -8895,12 +9139,12 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float
}
// Actual slider behavior + render grab
- bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical);
+ bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
// For the vertical slider we allow centered text to overlap the frame padding
char value_buf[64];
- char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
+ const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, 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));
if (label_size.x > 0.0f)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@@ -8916,28 +9160,23 @@ bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, fl
return value_changed;
}
-bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format)
+bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format)
{
- if (!display_format)
- display_format = "%.0f";
- float v_f = (float)*v;
- bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
- *v = (int)v_f;
- return value_changed;
+ return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format);
}
-bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format)
+bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power)
{
- if (!display_format)
- display_format = "%.0f";
- float v_f = (float)*v;
- bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f);
- *v = (int)v_f;
- return value_changed;
+ return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
+}
+
+bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format)
+{
+ return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format);
}
// Add multiple sliders on 1 line for compact edition of multiple components
-bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power)
+bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -8948,173 +9187,188 @@ bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_mi
BeginGroup();
PushID(label);
PushMultiItemsWidths(components);
+ size_t type_size = GDataTypeInfo[data_type].Size;
for (int i = 0; i < components; i++)
{
PushID(i);
- value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power);
+ value_changed |= SliderScalar("##v", data_type, v, v_min, v_max, format, power);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopItemWidth();
+ v = (void*)((char*)v + type_size);
}
PopID();
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
-
return value_changed;
}
-bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power)
+bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power)
{
- return SliderFloatN(label, v, 2, v_min, v_max, display_format, power);
+ return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power);
}
-bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power)
+bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power)
{
- return SliderFloatN(label, v, 3, v_min, v_max, display_format, power);
+ return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power);
}
-bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power)
+bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power)
{
- return SliderFloatN(label, v, 4, v_min, v_max, display_format, power);
-}
-
-bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- ImGuiContext& g = *GImGui;
- bool value_changed = false;
- BeginGroup();
- PushID(label);
- PushMultiItemsWidths(components);
- for (int i = 0; i < components; i++)
- {
- PushID(i);
- value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format);
- SameLine(0, g.Style.ItemInnerSpacing.x);
- PopID();
- PopItemWidth();
- }
- PopID();
-
- TextUnformatted(label, FindRenderedTextEnd(label));
- EndGroup();
-
- return value_changed;
+ return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power);
}
-bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format)
+bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format)
{
- return SliderIntN(label, v, 2, v_min, v_max, display_format);
+ return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format);
}
-bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format)
+bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format)
{
- return SliderIntN(label, v, 3, v_min, v_max, display_format);
+ return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format);
}
-bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format)
+bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format)
{
- return SliderIntN(label, v, 4, v_min, v_max, display_format);
+ return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format);
}
-bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power)
+// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)
+template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
+static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power)
{
ImGuiContext& g = *GImGui;
- const ImGuiStyle& style = g.Style;
- // Draw frame
- const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
- RenderNavHighlight(frame_bb, id);
- RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
+ // Default tweak speed
+ bool has_min_max = (v_min != v_max) && (v_max - v_max < FLT_MAX);
+ if (v_speed == 0.0f && has_min_max)
+ v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio);
- bool value_changed = false;
+ // 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)
+ {
+ adjust_delta = g.IO.MouseDelta.x;
+ if (g.IO.KeyAlt)
+ adjust_delta *= 1.0f/100.0f;
+ if (g.IO.KeyShift)
+ adjust_delta *= 10.0f;
+ }
+ if (g.ActiveIdSource == ImGuiInputSource_Nav)
+ {
+ int decimal_precision = (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImParseFormatPrecision(format, 3) : 0;
+ adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x;
+ v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
+ }
+ adjust_delta *= v_speed;
- // Process interacting with the drag
- if (g.ActiveId == id)
+ // Clear current value on activation
+ // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300.
+ bool is_just_activated = g.ActiveIdIsJustActivated;
+ bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));
+ if (is_just_activated || is_already_past_limits_and_pushing_outward)
{
- if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
- ClearActiveID();
- else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
- ClearActiveID();
+ g.DragCurrentAccum = 0.0f;
+ g.DragCurrentAccumDirty = false;
}
- if (g.ActiveId == id)
+ else if (adjust_delta != 0.0f)
{
- if (g.ActiveIdIsJustActivated)
- {
- // Lock current value on click
- g.DragCurrentValue = *v;
- g.DragLastMouseDelta = ImVec2(0.f, 0.f);
- }
+ g.DragCurrentAccum += adjust_delta;
+ g.DragCurrentAccumDirty = true;
+ }
- if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
- v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
+ if (!g.DragCurrentAccumDirty)
+ return false;
- float v_cur = g.DragCurrentValue;
- const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
- float adjust_delta = 0.0f;
- if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid())
- {
- adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x;
- if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
- adjust_delta *= g.DragSpeedScaleFast;
- if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
- adjust_delta *= g.DragSpeedScaleSlow;
- g.DragLastMouseDelta.x = mouse_drag_delta.x;
- }
- if (g.ActiveIdSource == ImGuiInputSource_Nav)
- {
- adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x;
- if (v_min < v_max && ((v_cur >= v_max && adjust_delta > 0.0f) || (v_cur <= v_min && adjust_delta < 0.0f))) // This is to avoid applying the saturation when already past the limits
- adjust_delta = 0.0f;
- v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
- }
- adjust_delta *= v_speed;
+ TYPE v_cur = *v;
+ FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;
- if (fabsf(adjust_delta) > 0.0f)
- {
- if (fabsf(power - 1.0f) > 0.001f)
- {
- // Logarithmic curve on both side of 0.0
- float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur;
- float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f;
- float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign);
- float v1_abs = v1 >= 0.0f ? v1 : -v1;
- float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line
- v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign
- }
- else
- {
- v_cur += adjust_delta;
- }
+ const bool is_power = (power != 1.0f && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) && has_min_max);
+ if (is_power)
+ {
+ // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range
+ FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
+ FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min));
+ v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min);
+ v_old_ref_for_accum_remainder = v_old_norm_curved;
+ }
+ else
+ {
+ v_cur += (TYPE)g.DragCurrentAccum;
+ }
- // Clamp
- if (v_min < v_max)
- v_cur = ImClamp(v_cur, v_min, v_max);
- g.DragCurrentValue = v_cur;
- }
+ // Round to user desired precision based on format string
+ v_cur = RoundScalarWithFormat<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
- // Round to user desired precision, then apply
- v_cur = RoundScalar(v_cur, decimal_precision);
- if (*v != v_cur)
- {
- *v = v_cur;
- value_changed = true;
- }
+ // Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
+ g.DragCurrentAccumDirty = false;
+ if (is_power)
+ {
+ FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
+ g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder);
+ }
+ else
+ {
+ g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);
}
+
+ // Lose zero sign for float/double
+ if (v_cur == (TYPE)-0)
+ v_cur = (TYPE)0;
- return value_changed;
+ // Clamp values (handle overflow/wrap-around)
+ if (*v != v_cur && has_min_max)
+ {
+ if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f))
+ v_cur = v_min;
+ if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f))
+ v_cur = v_max;
+ }
+
+ // Apply result
+ if (*v == v_cur)
+ return false;
+ *v = v_cur;
+ return true;
+}
+
+bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power)
+{
+ ImGuiContext& g = *GImGui;
+ if (g.ActiveId == id)
+ {
+ if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
+ ClearActiveID();
+ else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
+ ClearActiveID();
+ }
+ if (g.ActiveId != id)
+ return false;
+
+ switch (data_type)
+ {
+ case ImGuiDataType_S32: return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power);
+ case ImGuiDataType_U32: return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power);
+ case ImGuiDataType_S64: return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power);
+ case ImGuiDataType_U64: return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power);
+ case ImGuiDataType_Float: return DragBehaviorT<float, float, float >(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power);
+ case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power);
+ case ImGuiDataType_COUNT: break;
+ }
+ IM_ASSERT(0);
+ return false;
}
-bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power)
+bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
+ if (power != 1.0f)
+ IM_ASSERT(v_min != NULL && v_max != NULL); // When using a power curve the drag needs to have known bounds
+
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
@@ -9133,9 +9387,13 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
}
const bool hovered = ItemHoverable(frame_bb, id);
- if (!display_format)
- display_format = "%.3f";
- int decimal_precision = ParseFormatPrecision(display_format, 3);
+ // Default format string when passing NULL
+ // Patch old "%.0f" format string to use "%d", read function comments for more details.
+ IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
+ if (format == NULL)
+ format = GDataTypeInfo[data_type].PrintFmt;
+ else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0)
+ format = PatchFormatStringFloatToInt(format);
// Tabbing or CTRL-clicking on Drag turns it into an input box
bool start_text_input = false;
@@ -9153,16 +9411,21 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
}
}
if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
- return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
+ return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format);
// Actual drag behavior
ItemSize(total_bb, style.FramePadding.y);
- const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power);
+ const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power);
+
+ // Draw frame
+ const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
+ RenderNavHighlight(frame_bb, id);
+ RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
// 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 + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v);
- RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f));
+ const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format);
+ 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, inner_bb.Min.y), label);
@@ -9170,7 +9433,7 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
return value_changed;
}
-bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
+bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min, const void* v_max, const char* format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -9181,38 +9444,44 @@ bool ImGui::DragFloatN(const char* label, float* v, int components, float v_spee
BeginGroup();
PushID(label);
PushMultiItemsWidths(components);
+ size_t type_size = GDataTypeInfo[data_type].Size;
for (int i = 0; i < components; i++)
{
PushID(i);
- value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
+ value_changed |= DragScalar("##v", data_type, v, v_speed, v_min, v_max, format, power);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopItemWidth();
+ v = (void*)((char*)v + type_size);
}
PopID();
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
-
return value_changed;
}
-bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power)
+bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power)
{
- return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power);
+ return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power);
}
-bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power)
+bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power)
{
- return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power);
+ return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power);
}
-bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power)
+bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power)
{
- return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power);
+ return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power);
}
-bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power)
+bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power)
+{
+ return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power);
+}
+
+bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -9223,10 +9492,10 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu
BeginGroup();
PushMultiItemsWidths(2);
- bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power);
+ bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
- value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power);
+ value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
@@ -9238,59 +9507,27 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu
}
// NB: v_speed is float to allow adjusting the drag speed with more precision
-bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format)
+bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format)
{
- if (!display_format)
- display_format = "%.0f";
- float v_f = (float)*v;
- bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format);
- *v = (int)v_f;
- return value_changed;
+ return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format);
}
-bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format)
+bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format)
{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- ImGuiContext& g = *GImGui;
- bool value_changed = false;
- BeginGroup();
- PushID(label);
- PushMultiItemsWidths(components);
- for (int i = 0; i < components; i++)
- {
- PushID(i);
- value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format);
- SameLine(0, g.Style.ItemInnerSpacing.x);
- PopID();
- PopItemWidth();
- }
- PopID();
-
- TextUnformatted(label, FindRenderedTextEnd(label));
- EndGroup();
-
- return value_changed;
+ return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format);
}
-bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format)
+bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format)
{
- return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format);
+ return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format);
}
-bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format)
+bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format)
{
- return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format);
+ return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format);
}
-bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format)
-{
- return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format);
-}
-
-bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max)
+bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -9301,10 +9538,10 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_
BeginGroup();
PushMultiItemsWidths(2);
- bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format);
+ bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
- value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format);
+ value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
@@ -9710,7 +9947,7 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob
r->num_chars = (int)(text_remaining - (text + line_start_idx));
}
-static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
+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->Text[idx-1] ) && !is_separator( obj->Text[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
@@ -9864,7 +10101,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f
*p_char = (c += (unsigned int)('A'-'a'));
if (flags & ImGuiInputTextFlags_CharsNoBlank)
- if (ImCharIsSpace(c))
+ if (ImCharIsBlankW(c))
return false;
}
@@ -10045,15 +10282,15 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f));
- const bool osx_double_click_selects_words = io.OptMacOSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text
- if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0]))
+ const bool is_osx = io.OptMacOSXBehaviors;
+ if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0]))
{
edit_state.SelectAll();
edit_state.SelectedAllMouseLock = true;
}
- else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0])
+ else if (hovered && is_osx && io.MouseDoubleClicked[0])
{
- // Select a word only, OS X style (by simulating keystrokes)
+ // Double-click select a word only, OS X style (by simulating keystrokes)
edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
}
@@ -10078,7 +10315,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
{
// Process text input (before we check for Return because using some IME will effectively send a Return?)
// We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
- if (!(io.KeyCtrl && !io.KeyAlt) && is_editable && !user_nav_input_start)
+ bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
+ if (!ignore_inputs && is_editable && !user_nav_input_start)
for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++)
{
// Insert character if they pass filtering
@@ -10097,15 +10335,19 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
{
// Handle key-presses
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
- const bool is_shortcut_key_only = (io.OptMacOSXBehaviors ? (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_wordmove_key_down = io.OptMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
- const bool is_startend_key_down = io.OptMacOSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
+ const bool is_osx = io.OptMacOSXBehaviors;
+ 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_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_cut = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection());
- const bool is_copy = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection());
- const bool is_paste = ((is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable;
+ const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection());
+ const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection());
+ const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable;
+ const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable);
+ const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable;
if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
@@ -10119,7 +10361,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (!edit_state.HasSelection())
{
if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT);
- else if (io.OptMacOSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
+ else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT);
}
edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
}
@@ -10143,10 +10385,20 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (InputTextFilterCharacter(&c, flags, callback, user_data))
edit_state.OnKeyPressed((int)c);
}
- else if (IsKeyPressedMap(ImGuiKey_Escape)) { clear_active_id = cancel_edit = true; }
- else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable && is_undoable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
- else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable && is_undoable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
- else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; }
+ else if (IsKeyPressedMap(ImGuiKey_Escape))
+ {
+ clear_active_id = cancel_edit = true;
+ }
+ else if (is_undo || is_redo)
+ {
+ edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);
+ edit_state.ClearSelection();
+ }
+ else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A))
+ {
+ edit_state.SelectAll();
+ edit_state.CursorFollow = true;
+ }
else if (is_cut || is_copy)
{
// Cut, Copy
@@ -10158,7 +10410,6 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie);
SetClipboardText(edit_state.TempTextBuffer.Data);
}
-
if (is_cut)
{
if (!edit_state.HasSelection())
@@ -10169,7 +10420,6 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
}
else if (is_paste)
{
- // Paste
if (const char* clipboard = GetClipboardText())
{
// Filter pasted buffer
@@ -10201,8 +10451,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
{
if (cancel_edit)
{
- // Restore initial value
- if (is_editable)
+ // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
+ if (is_editable && strncmp(buf, edit_state.InitialText.Data, buf_size) != 0)
{
ImStrncpy(buf, edit_state.InitialText.Data, buf_size);
value_changed = true;
@@ -10442,7 +10692,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect);
// Draw blinking cursor
- bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
+ bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll;
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f);
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
@@ -10450,7 +10700,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
if (is_editable)
- g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
+ g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize);
}
else
{
@@ -10495,8 +10745,8 @@ bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, co
return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
}
-// NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
-bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags)
+// NB: format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "format" argument)
+bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -10504,84 +10754,78 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- const ImVec2 label_size = CalcTextSize(label, NULL, true);
- BeginGroup();
- PushID(label);
- const ImVec2 button_sz = ImVec2(GetFrameHeight(), GetFrameHeight());
- if (step_ptr)
- PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
+ IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
+ if (format == NULL)
+ format = GDataTypeInfo[data_type].PrintFmt;
char buf[64];
- DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf));
+ DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format);
bool value_changed = false;
if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)
extra_flags |= ImGuiInputTextFlags_CharsDecimal;
extra_flags |= ImGuiInputTextFlags_AutoSelectAll;
- if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
- value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format);
- // Step buttons
- if (step_ptr)
+ if (step != NULL)
{
+ const float button_size = GetFrameHeight();
+
+ BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()
+ PushID(label);
+ PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
+ if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view
+ value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
PopItemWidth();
+
+ // Step buttons
SameLine(0, style.ItemInnerSpacing.x);
- if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
+ if (ButtonEx("-", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
{
- DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
+ DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
value_changed = true;
}
SameLine(0, style.ItemInnerSpacing.x);
- if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
+ if (ButtonEx("+", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups))
{
- DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr);
+ DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step);
value_changed = true;
}
- }
- PopID();
+ SameLine(0, style.ItemInnerSpacing.x);
+ TextUnformatted(label, FindRenderedTextEnd(label));
- if (label_size.x > 0)
+ PopID();
+ EndGroup();
+ }
+ else
{
- SameLine(0, style.ItemInnerSpacing.x);
- RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label);
- ItemSize(label_size, style.FramePadding.y);
+ if (InputText(label, buf, IM_ARRAYSIZE(buf), extra_flags))
+ value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format);
}
- EndGroup();
return value_changed;
}
-bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
+bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags extra_flags)
{
extra_flags |= ImGuiInputTextFlags_CharsScientific;
- if (decimal_precision < 0)
- {
- // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1
- return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), "%f", extra_flags);
- }
- else
- {
- char display_format[16];
- ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision);
- return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags);
- }
+ return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, extra_flags);
}
-bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* display_format, ImGuiInputTextFlags extra_flags)
+bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags extra_flags)
{
extra_flags |= ImGuiInputTextFlags_CharsScientific;
- return InputScalarEx(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), display_format, extra_flags);
+ return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, extra_flags);
}
bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags)
{
// Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
- const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
- return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), scalar_format, extra_flags);
+ const char* format = (extra_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, extra_flags);
}
-bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags)
+bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -10592,13 +10836,15 @@ bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal
BeginGroup();
PushID(label);
PushMultiItemsWidths(components);
+ size_t type_size = GDataTypeInfo[data_type].Size;
for (int i = 0; i < components; i++)
{
PushID(i);
- value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags);
+ value_changed |= InputScalar("##v", data_type, v, step, step_fast, format, extra_flags);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopItemWidth();
+ v = (void*)((char*)v + type_size);
}
PopID();
@@ -10608,61 +10854,69 @@ bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal
return value_changed;
}
-bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
+bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags extra_flags)
{
- return InputFloatN(label, v, 2, decimal_precision, extra_flags);
+ return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags);
}
-bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
+bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags extra_flags)
{
- return InputFloatN(label, v, 3, decimal_precision, extra_flags);
+ return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags);
}
-bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
+bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags extra_flags)
{
- return InputFloatN(label, v, 4, decimal_precision, extra_flags);
+ return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags);
}
-bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags)
+// Prefer using "const char* format" directly, which is more flexible and consistent with other API.
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags)
{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
+ char format[16] = "%f";
+ if (decimal_precision >= 0)
+ ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
+ return InputFloat(label, v, step, step_fast, format, extra_flags);
+}
- ImGuiContext& g = *GImGui;
- bool value_changed = false;
- BeginGroup();
- PushID(label);
- PushMultiItemsWidths(components);
- for (int i = 0; i < components; i++)
- {
- PushID(i);
- value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags);
- SameLine(0, g.Style.ItemInnerSpacing.x);
- PopID();
- PopItemWidth();
- }
- PopID();
+bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags)
+{
+ char format[16] = "%f";
+ if (decimal_precision >= 0)
+ ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
+ return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags);
+}
- TextUnformatted(label, FindRenderedTextEnd(label));
- EndGroup();
+bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags)
+{
+ char format[16] = "%f";
+ if (decimal_precision >= 0)
+ ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
+ return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags);
+}
- return value_changed;
+bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags)
+{
+ char format[16] = "%f";
+ if (decimal_precision >= 0)
+ ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision);
+ return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags);
}
+#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags)
{
- return InputIntN(label, v, 2, extra_flags);
+ return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", extra_flags);
}
bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags)
{
- return InputIntN(label, v, 3, extra_flags);
+ return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", extra_flags);
}
bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags)
{
- return InputIntN(label, v, 4, extra_flags);
+ return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags);
}
static float CalcMaxPopupHeightFromItemCount(int items_count)
@@ -10757,31 +11011,29 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents));
if (flags & ImGuiComboFlags_PopupAlignLeft)
popup_window->AutoPosLastDirection = ImGuiDir_Left;
- ImVec2 pos = FindBestWindowPosForPopup(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
+ ImRect r_outer = FindAllowedExtentRectForWindow(popup_window);
+ ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
SetNextWindowPos(pos);
}
+ // Horizontally align ourselves with the framed text
+ PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y));
+
ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
if (!Begin(name, NULL, window_flags))
{
EndPopup();
+ PopStyleVar();
IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
return false;
}
-
- // Horizontally align ourselves with the framed text
- if (style.FramePadding.x != style.WindowPadding.x)
- Indent(style.FramePadding.x - style.WindowPadding.x);
-
return true;
}
void ImGui::EndCombo()
{
- const ImGuiStyle& style = GImGui->Style;
- if (style.FramePadding.x != style.WindowPadding.x)
- Unindent(style.FramePadding.x - style.WindowPadding.x);
EndPopup();
+ PopStyleVar();
}
// Old API, prefer using BeginCombo() nowadays if you can.
@@ -10793,7 +11045,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
if (*current_item >= 0 && *current_item < items_count)
items_getter(data, *current_item, &preview_text);
- // The old Combo() API exposed "popup_max_height_in_items", however the new more general BeginCombo() API doesn't, so we emulate it here.
+ // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't, so we emulate it here.
if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond)
{
float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
@@ -10934,8 +11186,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
selected = false;
// Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
- if (pressed || hovered)// && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f))
- if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerActiveMask)
+ if (pressed || hovered)
+ if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
{
g.NavDisableHighlight = true;
SetNavID(id, window->DC.NavLayerCurrent);
@@ -11005,7 +11257,7 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
{
// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
- // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
+ // We don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
// I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
if (height_in_items < 0)
height_in_items = ImMin(items_count, 7);
@@ -11123,21 +11375,24 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected,
return false;
}
+// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
bool ImGui::BeginMainMenuBar()
{
ImGuiContext& g = *GImGui;
+ g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
SetNextWindowPos(ImVec2(0.0f, 0.0f));
- SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f));
+ SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y));
PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0));
- if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar)
- || !BeginMenuBar())
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
+ bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar();
+ g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
+ if (!is_open)
{
End();
PopStyleVar(2);
return false;
}
- g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x;
return true;
}
@@ -11166,14 +11421,14 @@ bool ImGui::BeginMenuBar()
BeginGroup(); // Save position
PushID("##menubar");
- // We don't clip with regular window clipping rectangle as it is already set to the area below. However we clip with window full rect.
- // We remove 1 worth of rounding to Max.x to that text in long menus don't tend to display over the lower-right rounded area, which looks particularly glitchy.
+ // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
+ // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.
ImRect bar_rect = window->MenuBarRect();
ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f));
- clip_rect.ClipWith(window->WindowRectClipped);
+ clip_rect.ClipWith(window->OuterRectClipped);
PushClipRect(clip_rect.Min, clip_rect.Max, false);
- window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffsetX, bar_rect.Min.y);// + g.Style.FramePadding.y);
+ window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
window->DC.LayoutType = ImGuiLayoutType_Horizontal;
window->DC.NavLayerCurrent++;
window->DC.NavLayerCurrentMask <<= 1;
@@ -11213,7 +11468,7 @@ void ImGui::EndMenuBar()
IM_ASSERT(window->DC.MenuBarAppending);
PopClipRect();
PopID();
- window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x;
+ window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
window->DC.GroupStack.back().AdvanceCursor = false;
EndGroup();
window->DC.LayoutType = ImGuiLayoutType_Vertical;
@@ -11241,13 +11496,13 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
if (menuset_is_open)
g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
- // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos).
+ // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup).
ImVec2 popup_pos, pos = window->DC.CursorPos;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
// Menu inside an horizontal menu bar
// Selectable extend their highlight by half ItemSpacing in each direction.
- // For ChildMenu, the popup position will be overwritten by the call to FindBestPopupWindowPos() in Begin()
+ // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
@@ -11285,7 +11540,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
- float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
+ float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
@@ -11345,8 +11600,9 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
if (menu_is_open)
{
+ // 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)
SetNextWindowPos(popup_pos, ImGuiCond_Always);
- ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
+ ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
}
@@ -11357,7 +11613,7 @@ void ImGui::EndMenu()
{
// Nav: When a left move request _within our child menu_ failed, close the menu.
// A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
- // However it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
+ // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction.
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical)
@@ -11544,15 +11800,15 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
ImGuiColorEditFlags opts = g.ColorEditOptions;
if (allow_opt_inputs)
{
- if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
- if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
- if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
+ if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
+ if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
+ if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
}
if (allow_opt_datatype)
{
if (allow_opt_inputs) Separator();
- if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
- if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
+ if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
+ if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
}
if (allow_opt_inputs || allow_opt_datatype)
@@ -11636,15 +11892,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
const float w_items_all = CalcItemWidth() - w_extra;
const char* label_display_end = FindRenderedTextEnd(label);
- const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
- const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
- const int components = alpha ? 4 : 3;
- const ImGuiColorEditFlags flags_untouched = flags;
-
BeginGroup();
PushID(label);
// If we're not showing any slider there's no point in doing any HSV conversions
+ const ImGuiColorEditFlags flags_untouched = flags;
if (flags & ImGuiColorEditFlags_NoInputs)
flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions;
@@ -11661,6 +11913,10 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask);
flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask));
+ const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
+ const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
+ const int components = alpha ? 4 : 3;
+
// Convert to the formats we need
float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
if (flags & ImGuiColorEditFlags_HSV)
@@ -11680,9 +11936,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
const char* fmt_table_int[3][4] =
{
- { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, // Short display
- { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, // Long display for RGBA
- { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } // Long display for HSVA
+ { "%3d", "%3d", "%3d", "%3d" }, // Short display
+ { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA
+ { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA
};
const char* fmt_table_float[3][4] =
{
@@ -11722,7 +11978,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
{
value_changed = true;
char* p = buf;
- while (*p == '#' || ImCharIsSpace((unsigned int)*p))
+ while (*p == '#' || ImCharIsBlankA(*p))
p++;
i[0] = i[1] = i[2] = i[3] = 0;
if (alpha)
@@ -11924,13 +12180,13 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
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 = atan2f(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;
}
- float cos_hue_angle = cosf(-H * 2.0f * IM_PI);
- float sin_hue_angle = sinf(-H * 2.0f * IM_PI);
+ float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);
+ float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);
if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
{
// Interacting with SV triangle
@@ -12076,14 +12332,14 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
const int vert_end_idx = draw_list->VtxBuffer.Size;
// Paint colors over existing vertices
- ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner);
- ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner);
+ 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->VtxBuffer.Data + vert_start_idx, draw_list->VtxBuffer.Data + vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
}
// Render Cursor + preview on Hue Wheel
- float cos_hue_angle = cosf(H * 2.0f * IM_PI);
- float sin_hue_angle = sinf(H * 2.0f * IM_PI);
+ 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);
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);
@@ -12646,7 +12902,7 @@ void ImGui::EndColumns()
columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
window->DC.CursorPos.y = columns->LineMaxY;
if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
- window->DC.CursorMaxPos.x = ImMax(columns->StartMaxPosX, columns->MaxX); // Restore cursor max pos, as columns don't grow parent
+ window->DC.CursorMaxPos.x = columns->StartMaxPosX; // Restore cursor max pos, as columns don't grow parent
// Draw columns borders and handle resize
bool is_being_resized = false;
@@ -12950,14 +13206,14 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s
// Store in heap
g.DragDropPayloadBufHeap.resize((int)data_size);
payload.Data = g.DragDropPayloadBufHeap.Data;
- memcpy((void*)(intptr_t)payload.Data, data, data_size);
+ memcpy(payload.Data, data, data_size);
}
else if (data_size > 0)
{
// Store locally
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
payload.Data = g.DragDropPayloadBufLocal;
- memcpy((void*)(intptr_t)payload.Data, data, data_size);
+ memcpy(payload.Data, data, data_size);
}
else
{
@@ -13185,19 +13441,19 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
#endif
//-----------------------------------------------------------------------------
-// HELP
+// HELP, METRICS
//-----------------------------------------------------------------------------
void ImGui::ShowMetricsWindow(bool* p_open)
{
if (ImGui::Begin("ImGui Metrics", p_open))
{
+ static bool show_draw_cmd_clip_rects = true;
ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount);
- static bool show_clip_rects = true;
- ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_clip_rects);
+ ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
ImGui::Separator();
struct Funcs
@@ -13213,8 +13469,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
return;
}
- ImDrawList* overlay_draw_list = ImGui::GetOverlayDrawList(); // Render additional visuals into the top-most draw list
- if (window && ImGui::IsItemHovered())
+ ImDrawList* overlay_draw_list = GetOverlayDrawList(); // Render additional visuals into the top-most draw list
+ if (window && IsItemHovered())
overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
if (!node_open)
return;
@@ -13231,7 +13487,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
- if (show_clip_rects && ImGui::IsItemHovered())
+ if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
{
ImRect clip_rect = pcmd->ClipRect;
ImRect vtxs_rect;
@@ -13294,11 +13550,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed);
ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
- if (window->NavRectRel[0].IsInverted())
+ if (!window->NavRectRel[0].IsInverted())
ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
else
ImGui::BulletText("NavRectRel[0]: <None>");
if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
+ if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
{
diff --git a/imgui/imgui.h b/imgui/imgui.h
index d1174bd3..f4cbbec7 100644
--- a/imgui/imgui.h
+++ b/imgui/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.60
+// dear imgui, v1.61
// (headers)
// See imgui.cpp file for documentation.
@@ -16,25 +16,25 @@
#include "imconfig.h"
#endif
-#include <float.h> // FLT_MAX
-#include <stdarg.h> // va_list
-#include <stddef.h> // ptrdiff_t, NULL
-#include <string.h> // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp
+#include <float.h> // FLT_MAX
+#include <stdarg.h> // va_list
+#include <stddef.h> // ptrdiff_t, NULL
+#include <string.h> // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp
-#define IMGUI_VERSION "1.60"
+// Version
+#define IMGUI_VERSION "1.61"
+#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert))
-// Define attributes of all API symbols declarations, e.g. for DLL under Windows.
+// Define attributes of all API symbols declarations (e.g. for DLL under Windows)
#ifndef IMGUI_API
#define IMGUI_API
#endif
-// Define assertion handler.
+// Helpers
#ifndef IM_ASSERT
#include <assert.h>
-#define IM_ASSERT(_EXPR) assert(_EXPR)
+#define IM_ASSERT(_EXPR) assert(_EXPR)
#endif
-
-// Helpers
#if defined(__clang__) || defined(__GNUC__)
#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // Apply printf-style warnings to user functions.
#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0)))
@@ -60,7 +60,7 @@ struct ImDrawVert; // A single vertex (20 bytes by default, ove
struct ImFont; // Runtime data for a single font within a parent ImFontAtlas
struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader
struct ImFontConfig; // Configuration data when adding a font or merging fonts
-struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4
+struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*obsolete* please avoid using)
struct ImGuiIO; // Main configuration and I/O between your application and ImGui
struct ImGuiOnceUponAFrame; // Simple helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro
struct ImGuiStorage; // Simple custom key value storage
@@ -72,16 +72,15 @@ struct ImGuiSizeCallbackData; // Structure used to constraint window size
struct ImGuiListClipper; // Helper to manually clip large list of items
struct ImGuiPayload; // User data payload for drag and drop operations
struct ImGuiContext; // ImGui context (opaque)
-
#ifndef ImTextureID
typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp)
#endif
// Typedefs and Enumerations (declared as int for compatibility with old C++ and to not pollute the top of this file)
-typedef unsigned int ImU32; // 32-bit unsigned integer (typically used to store packed colors)
typedef unsigned int ImGuiID; // Unique ID used by widgets (typically hashed from a stack of string)
typedef unsigned short ImWchar; // Character for keyboard input/display
typedef int ImGuiCol; // enum: a color identifier for styling // enum ImGuiCol_
+typedef int ImGuiDataType; // enum: a primary data type // enum ImGuiDataType_
typedef int ImGuiDir; // enum: a cardinal direction // enum ImGuiDir_
typedef int ImGuiCond; // enum: a condition for Set*() // enum ImGuiCond_
typedef int ImGuiKey; // enum: a key identifier (ImGui-side enum) // enum ImGuiKey_
@@ -95,8 +94,8 @@ typedef int ImGuiBackendFlags; // flags: for io.BackendFlags
typedef int ImGuiColorEditFlags; // flags: for ColorEdit*(), ColorPicker*() // enum ImGuiColorEditFlags_
typedef int ImGuiColumnsFlags; // flags: for *Columns*() // enum ImGuiColumnsFlags_
typedef int ImGuiConfigFlags; // flags: for io.ConfigFlags // enum ImGuiConfigFlags_
-typedef int ImGuiDragDropFlags; // flags: for *DragDrop*() // enum ImGuiDragDropFlags_
typedef int ImGuiComboFlags; // flags: for BeginCombo() // enum ImGuiComboFlags_
+typedef int ImGuiDragDropFlags; // flags: for *DragDrop*() // enum ImGuiDragDropFlags_
typedef int ImGuiFocusedFlags; // flags: for IsWindowFocused() // enum ImGuiFocusedFlags_
typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() etc. // enum ImGuiHoveredFlags_
typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_
@@ -105,35 +104,43 @@ typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()
typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_
typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data);
typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);
+
+// Scalar data types
+typedef signed int ImS32; // 32-bit signed integer == int
+typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors)
#if defined(_MSC_VER) && !defined(__clang__)
-typedef unsigned __int64 ImU64; // 64-bit unsigned integer
+typedef signed __int64 ImS64; // 64-bit signed integer
+typedef unsigned __int64 ImU64; // 64-bit unsigned integer
#else
-typedef unsigned long long ImU64; // 64-bit unsigned integer
+typedef signed long long ImS64; // 64-bit signed integer
+typedef unsigned long long ImU64; // 64-bit unsigned integer
#endif
+// 2d vector
struct ImVec2
{
- float x, y;
- ImVec2() { x = y = 0.0f; }
+ float x, y;
+ ImVec2() { x = y = 0.0f; }
ImVec2(float _x, float _y) { x = _x; y = _y; }
- float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine.
-#ifdef IM_VEC2_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2.
- IM_VEC2_CLASS_EXTRA
+ float operator[] (size_t i) const { IM_ASSERT(i <= 1); return (&x)[i]; } // We very rarely use this [] operator, the assert overhead is fine.
+#ifdef IM_VEC2_CLASS_EXTRA
+ IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2.
#endif
};
+// 4d vector (often used to store floating-point colors)
struct ImVec4
{
- float x, y, z, w;
- ImVec4() { x = y = z = w = 0.0f; }
+ float x, y, z, w;
+ ImVec4() { x = y = z = w = 0.0f; }
ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; }
-#ifdef IM_VEC4_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec4.
- IM_VEC4_CLASS_EXTRA
+#ifdef IM_VEC4_CLASS_EXTRA
+ IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.
#endif
};
// ImGui end-user API
-// In a namespace so that user can add extra functions in a separate file (e.g. Value() helpers for your vector or common types)
+// In a namespace so that user can add extra functions in your own separate file (please don't modify imgui.cpp/.h)
namespace ImGui
{
// Context creation and access
@@ -143,6 +150,7 @@ namespace ImGui
IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = destroy current context
IMGUI_API ImGuiContext* GetCurrentContext();
IMGUI_API void SetCurrentContext(ImGuiContext* ctx);
+ IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert);
// Main
IMGUI_API ImGuiIO& GetIO();
@@ -169,7 +177,7 @@ namespace ImGui
// 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)
// Begin()/BeginChild() return false to indicate the window being collapsed or fully clipped, so you may early out and omit submitting anything to the window.
- // However you need to always call a matching End()/EndChild() for a Begin()/BeginChild() call, regardless of its return value (this is due to legacy reason and is inconsistent with BeginMenu/EndMenu, BeginPopup/EndPopup and other functions where the End call should only be called if the corresponding Begin function returned true.)
+ // You need to always call a matching End()/EndChild() for a Begin()/BeginChild() call, regardless of its return value (this is due to legacy reason and is inconsistent with BeginMenu/EndMenu, BeginPopup/EndPopup and other functions where the End call should only be called if the corresponding Begin function returned true.)
// Passing 'bool* p_open != NULL' shows a close widget in the upper-right corner of the window, which when clicking will set the boolean to false.
// Use child windows to introduce independent scrolling/clipping regions within a host window. Child windows can embed their own child.
IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0);
@@ -231,7 +239,7 @@ namespace ImGui
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val);
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val);
IMGUI_API void PopStyleVar(int count = 1);
- IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwhise use GetColorU32() to get style color with style alpha baked in.
+ IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in.
IMGUI_API ImFont* GetFont(); // get current font
IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied
IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API
@@ -334,43 +342,52 @@ namespace ImGui
// Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds)
// For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
+ // Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
// Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
- IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound
- IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f);
- IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f);
- IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f);
- IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", const char* display_format_max = NULL, float power = 1.0f);
- IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound
- IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f");
- IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f");
- IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f");
- IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f", const char* display_format_max = NULL);
+ IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound
+ IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f);
+ IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f);
+ IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f);
+ IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, float power = 1.0f);
+ IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // If v_min >= v_max we have no bound
+ IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d");
+ IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d");
+ IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d");
+ IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL);
+ IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f);
+ IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f);
// Widgets: Input with Keyboard
IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback 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, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);
- IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
- IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
- IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
- IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0);
+ IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0);
+ IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0);
+ IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0);
+ IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0);
IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0);
- IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0f, double step_fast = 0.0f, const char* display_format = "%.6f", ImGuiInputTextFlags extra_flags = 0);
+ IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0f, double step_fast = 0.0f, const char* format = "%.6f", ImGuiInputTextFlags extra_flags = 0);
+ IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* v, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags extra_flags = 0);
+ IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags extra_flags = 0);
// Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds)
- IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for logarithmic sliders
- IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
- IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
- IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
+ // Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
+ IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders
+ IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
+ IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
+ IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f);
- IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f");
- IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f");
- IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f");
- IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f");
- IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f);
- IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f");
+ IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d");
+ IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d");
+ IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d");
+ IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d");
+ IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f);
+ IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f);
+ IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
+ IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d");
+ IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f);
// Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little colored preview square that can be left-clicked to open a picker, and right-clicked to open an option menu.)
// Note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can the pass the address of a first float element out of a contiguous structure, e.g. &myvector.x
@@ -406,9 +423,9 @@ namespace ImGui
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 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. make sure to 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
+ IMGUI_API void ListBoxFooter(); // terminate the scrolling region. only call ListBoxFooter() if ListBoxHeader() returned true!
// Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace)
IMGUI_API void Value(const char* prefix, bool b);
@@ -516,7 +533,7 @@ namespace ImGui
// Inputs
IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key]
- IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]!
+ IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down). if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
IMGUI_API bool IsKeyReleased(int user_key_index); // was key released (went from Down to !Down)..
IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate
@@ -541,6 +558,14 @@ namespace ImGui
IMGUI_API const char* GetClipboardText();
IMGUI_API void SetClipboardText(const char* text);
+ // Settings/.Ini Utilities
+ // The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini").
+ // Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
+ IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
+ IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
+ IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename);
+ IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
+
// Memory Utilities
// All those functions are not reliant on the current context.
// If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again.
@@ -695,10 +720,22 @@ enum ImGuiDragDropFlags_
ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect // For peeking ahead and inspecting the payload before delivery.
};
-// Standard Drag and Drop payload types. You can define you own payload types using 12-characters long strings. Types starting with '_' are defined by Dear ImGui.
+// Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui.
#define IMGUI_PAYLOAD_TYPE_COLOR_3F "_COL3F" // float[3]: Standard type for colors, without alpha. User code may use this type.
#define IMGUI_PAYLOAD_TYPE_COLOR_4F "_COL4F" // float[4]: Standard type for colors. User code may use this type.
+// A primary data type
+enum ImGuiDataType_
+{
+ ImGuiDataType_S32, // int
+ ImGuiDataType_U32, // unsigned int
+ ImGuiDataType_S64, // long long, __int64
+ ImGuiDataType_U64, // unsigned long long, unsigned __int64
+ ImGuiDataType_Float, // float
+ ImGuiDataType_Double, // double
+ ImGuiDataType_COUNT
+};
+
// A cardinal direction
enum ImGuiDir_
{
@@ -738,7 +775,7 @@ enum ImGuiKey_
};
// [BETA] Gamepad/Keyboard directional navigation
-// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays.
+// 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 goo.gl/9LgVZW.
enum ImGuiNavInput_
@@ -762,7 +799,7 @@ enum ImGuiNavInput_
ImGuiNavInput_TweakFast, // faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch)
// [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them.
- // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeyDown[] instead of io.NavInputs[].
+ // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[].
ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt
ImGuiNavInput_KeyLeft_, // move left // = Arrow keys
ImGuiNavInput_KeyRight_, // move right
@@ -775,7 +812,7 @@ enum ImGuiNavInput_
// Configuration flags stored in io.ConfigFlags. Set by user/application.
enum ImGuiConfigFlags_
{
- ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[].
+ ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[].
ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. Back-end also needs to set ImGuiBackendFlags_HasGamepad.
ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth.
ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag with io.NavActive is set.
@@ -790,9 +827,9 @@ enum ImGuiConfigFlags_
// Back-end capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom back-end.
enum ImGuiBackendFlags_
{
- ImGuiBackendFlags_HasGamepad = 1 << 0, // Back-end has a connected gamepad.
- ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Back-end can honor GetMouseCursor() values and change the OS cursor shape.
- ImGuiBackendFlags_HasSetMousePos = 1 << 2 // Back-end can honor io.WantSetMousePos and reposition the mouse (only used if ImGuiConfigFlags_NavEnableSetMousePos is set).
+ ImGuiBackendFlags_HasGamepad = 1 << 0, // Back-end supports and has a connected gamepad.
+ ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Back-end supports reading GetMouseCursor() to change the OS cursor shape.
+ ImGuiBackendFlags_HasSetMousePos = 1 << 2 // Back-end supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set).
};
// Enumeration for PushStyleColor() / PopStyleColor()
@@ -836,10 +873,10 @@ enum ImGuiCol_
ImGuiCol_PlotHistogram,
ImGuiCol_PlotHistogramHovered,
ImGuiCol_TextSelectedBg,
- ImGuiCol_ModalWindowDarkening, // darken/colorize entire screen behind a modal window, when one is active
+ ImGuiCol_ModalWindowDarkening, // Darken/colorize entire screen behind a modal window, when one is active
ImGuiCol_DragDropTarget,
- ImGuiCol_NavHighlight, // gamepad/keyboard: current highlighted item
- ImGuiCol_NavWindowingHighlight, // gamepad/keyboard: when holding NavMenu to focus/move/resize windows
+ ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item
+ ImGuiCol_NavWindowingHighlight, // Gamepad/keyboard: when holding NavMenu to focus/move/resize windows
ImGuiCol_COUNT
// Obsolete names (will be removed)
@@ -980,7 +1017,7 @@ struct ImGuiStyle
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered.
ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
- ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
+ ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly!
float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU.
bool AntiAliasedFill; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
@@ -1000,11 +1037,11 @@ struct ImGuiIO
//------------------------------------------------------------------
ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc.
- ImGuiBackendFlags BackendFlags; // = 0 // Set ImGuiBackendFlags_ enum. Set by imgui_impl_xxx files or custom back-end.
+ ImGuiBackendFlags BackendFlags; // = 0 // Set ImGuiBackendFlags_ enum. Set by imgui_impl_xxx files or custom back-end to communicate features supported by the back-end.
ImVec2 DisplaySize; // <unset> // Display size, in pixels. For clamping windows positions.
float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds.
- float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds.
- const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving.
+ float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds.
+ const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory.
const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified).
float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds.
float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels.
@@ -1042,9 +1079,12 @@ struct ImGuiIO
void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
- // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render().
- // See example applications if you are unsure of how to implement this.
+ // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now!
+ // You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). See example applications if you are unsure of how to implement this.
void (*RenderDrawListsFn)(ImDrawData* data);
+#else
+ // This is only here to keep ImGuiIO the same size, so that IMGUI_DISABLE_OBSOLETE_FUNCTIONS can exceptionally be used outside of imconfig.h.
+ void* RenderDrawListsFnDummy;
#endif
//------------------------------------------------------------------
@@ -1053,8 +1093,8 @@ struct ImGuiIO
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: left, right, 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.
- float MouseWheel; // Mouse wheel: 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.
+ 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 MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor).
bool KeyCtrl; // Keyboard modifier pressed: Control
bool KeyShift; // Keyboard modifier pressed: Shift
@@ -1077,6 +1117,7 @@ struct ImGuiIO
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
@@ -1115,6 +1156,11 @@ struct ImGuiIO
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui
{
+ // OBSOLETED in 1.61 (from Apr 2018)
+ bool InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags = 0); // Use the 'const char* format' version instead of 'decimal_precision'!
+ bool InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags = 0);
+ bool InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags = 0);
+ bool InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags = 0);
// OBSOLETED in 1.60 (from Dec 2017)
static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); }
static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }
@@ -1212,7 +1258,7 @@ inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; }
inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symetrical new()
#define IM_PLACEMENT_NEW(_PTR) new(ImNewDummy(), _PTR)
#define IM_NEW(_TYPE) new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE
-template<typename T> void IM_DELETE(T*& p) { if (p) { p->~T(); ImGui::MemFree(p); p = NULL; } }
+template<typename T> void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } }
// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame.
// Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text("This will be called only once per frame");
@@ -1368,7 +1414,7 @@ struct ImGuiSizeCallbackData
struct ImGuiPayload
{
// Members
- const void* Data; // Data (copied and owned by dear imgui)
+ void* Data; // Data (copied and owned by dear imgui)
int DataSize; // Data size
// [Internal]
@@ -1531,7 +1577,7 @@ enum ImDrawListFlags_
// This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering.
// Each ImGui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to access the current window draw list and draw custom primitives.
// You can interleave normal ImGui:: calls and adding primitives to the current draw list.
-// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), however you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well)
+// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), but you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well)
// Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects.
struct ImDrawList
{
@@ -1582,17 +1628,17 @@ struct ImDrawList
IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,0), const ImVec2& uv_c = ImVec2(1,1), const ImVec2& uv_d = ImVec2(0,1), ImU32 col = 0xFFFFFFFF);
IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col, float rounding, int rounding_corners = ImDrawCornerFlags_All);
IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness);
- IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col);
+ IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order.
IMGUI_API void AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0);
- // Stateful path API, add points then finish with PathFill() or PathStroke()
+ // Stateful path API, add points then finish with PathFillConvex() or PathStroke()
inline void PathClear() { _Path.resize(0); }
inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); }
inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); }
- inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); PathClear(); }
+ inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); PathClear(); } // 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); PathClear(); }
IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10);
- IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
+ IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
IMGUI_API void PathBezierCurveTo(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, int num_segments = 0);
IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All);
@@ -1686,7 +1732,7 @@ enum ImFontAtlasFlags_
// 2. Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data.
// 3. Upload the pixels data into a texture within your graphics system.
// 4. Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture. This value will be passed back to you during rendering to identify the texture.
-// IMPORTANT: If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the ImFont is build (when calling GetTextData*** or Build()). We only copy the pointer, not the data.
+// IMPORTANT: If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the ImFont is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data.
struct ImFontAtlas
{
IMGUI_API ImFontAtlas();
diff --git a/imgui/imgui_demo.cpp b/imgui/imgui_demo.cpp
index 8b7b5450..fcff5065 100644
--- a/imgui/imgui_demo.cpp
+++ b/imgui/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.60
+// dear imgui, v1.61
// (demo code)
// Message to the person tempted to delete this file when integrating ImGui into their code base:
@@ -24,6 +24,7 @@
#include "imgui.h"
#include <ctype.h> // toupper, isprint
+#include <limits.h> // INT_MIN, INT_MAX
#include <math.h> // sqrtf, powf, cosf, sinf, floorf, ceilf
#include <stdio.h> // vsnprintf, sscanf, printf
#include <stdlib.h> // NULL, malloc, free, atoi
@@ -82,7 +83,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open);
static void ShowExampleAppLongText(bool* p_open);
static void ShowExampleAppAutoResize(bool* p_open);
static void ShowExampleAppConstrainedResize(bool* p_open);
-static void ShowExampleAppFixedOverlay(bool* p_open);
+static void ShowExampleAppSimpleOverlay(bool* p_open);
static void ShowExampleAppWindowTitles(bool* p_open);
static void ShowExampleAppCustomRendering(bool* p_open);
static void ShowExampleAppMainMenuBar();
@@ -135,7 +136,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
static bool show_app_long_text = false;
static bool show_app_auto_resize = false;
static bool show_app_constrained_resize = false;
- static bool show_app_fixed_overlay = false;
+ static bool show_app_simple_overlay = false;
static bool show_app_window_titles = false;
static bool show_app_custom_rendering = false;
static bool show_app_style_editor = false;
@@ -151,7 +152,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text);
if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize);
if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize);
- if (show_app_fixed_overlay) ShowExampleAppFixedOverlay(&show_app_fixed_overlay);
+ if (show_app_simple_overlay) ShowExampleAppSimpleOverlay(&show_app_simple_overlay);
if (show_app_window_titles) ShowExampleAppWindowTitles(&show_app_window_titles);
if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering);
@@ -218,7 +219,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::MenuItem("Long text display", NULL, &show_app_long_text);
ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize);
ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize);
- ImGui::MenuItem("Simple overlay", NULL, &show_app_fixed_overlay);
+ ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay);
ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles);
ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering);
ImGui::EndMenu();
@@ -336,18 +337,20 @@ void ImGui::ShowDemoWindow(bool* p_open)
{
static char str0[128] = "Hello, world!";
static int i0 = 123;
- static float f0 = 0.001f;
ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0));
ImGui::SameLine(); ShowHelpMarker("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");
ImGui::InputInt("input int", &i0);
ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n");
+ static float f0 = 0.001f;
ImGui::InputFloat("input float", &f0, 0.01f, 1.0f);
- // NB: You can use the %e notation as well.
- static double d0 = 999999.000001;
- ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.6f");
+ static double d0 = 999999.00000001;
+ ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f");
+
+ static float f1 = 1.e10f;
+ ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e");
ImGui::SameLine(); ShowHelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n");
static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
@@ -359,7 +362,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::DragInt("drag int", &i1, 1);
ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value.");
- ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%.0f%%");
+ ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%");
static float f1=1.00f, f2=0.0067f;
ImGui::DragFloat("drag float", &f1, 0.005f);
@@ -373,7 +376,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
static float f1=0.123f, f2=0.0f;
ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
- ImGui::SliderFloat("slider log float", &f2, -10.0f, 10.0f, "%.4f", 3.0f);
+ ImGui::SliderFloat("slider float (curve)", &f2, -10.0f, 10.0f, "%.4f", 2.0f);
static float angle = 0.0f;
ImGui::SliderAngle("slider angle", &angle);
}
@@ -547,16 +550,18 @@ void ImGui::ShowDemoWindow(bool* p_open)
if (ImGui::TreeNode("UTF-8 Text"))
{
// UTF-8 test with Japanese characters
- // (needs a suitable font, try Arial Unicode or M+ fonts http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index-en.html)
+ // (Needs a suitable font, try Noto, or Arial Unicode, or M+ fonts. Read misc/fonts/README.txt 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')
- // - HOWEVER, FOR THIS DEMO FILE, BECAUSE WE WANT TO SUPPORT COMPILER, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE.
- // Instead we are encoding a few string with hexadecimal constants. Don't do this in your application!
+ // - 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->LoadFromFileTTF() manually to load extra character ranges.");
- ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
+ ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->LoadFromFileTTF() manually to load extra character ranges. Read misc/fonts/README.txt 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"; // "nihongo"
+ static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
+ //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis
ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf));
ImGui::TreePop();
}
@@ -787,7 +792,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::TreePop();
}
- if (ImGui::TreeNode("Plots widgets"))
+ if (ImGui::TreeNode("Plots Widgets"))
{
static bool animate = true;
ImGui::Checkbox("Animate", &animate);
@@ -978,13 +983,12 @@ void ImGui::ShowDemoWindow(bool* p_open)
if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX;
ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);
- ImGui::Text("Programmatically set defaults/options:");
+ ImGui::Text("Programmatically set defaults:");
ImGui::SameLine(); ShowHelpMarker("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.");
- if (ImGui::Button("Uint8 + HSV"))
- ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV);
- ImGui::SameLine();
- if (ImGui::Button("Float + HDR"))
- ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_RGB);
+ if (ImGui::Button("Default: Uint8 + HSV + Hue Bar"))
+ ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV | ImGuiColorEditFlags_PickerHueBar);
+ if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
+ ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
ImGui::TreePop();
}
@@ -994,7 +998,80 @@ void ImGui::ShowDemoWindow(bool* p_open)
static float begin = 10, end = 90;
static int begin_i = 100, end_i = 1000;
ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%");
- ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %.0f units", "Max: %.0f units");
+ ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units");
+ ImGui::TreePop();
+ }
+
+ if (ImGui::TreeNode("Data Types"))
+ {
+ // The DragScalar, InputScalar, SliderScalar functions allow manipulating most common 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 argument-by-values are turned into argument-by-address.
+ // 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 may want to wrap it yourself inside a 1 line function
+ // which can take typed values argument instead of void*, and then pass their address to the generic function. For example:
+ // bool SliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") { return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); }
+ // Below are helper variables we can take the address of to work-around this:
+ // Note that the SliderScalar function has a maximum usable range of half the natural type maximum, hence the /2 below.
+ const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2;
+ const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2;
+ const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2;
+ const ImU64 u64_zero = 0, u64_one = 1, u64_fifty = 50, u64_min = 0, u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2;
+ const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;
+ const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000, f64_hi_a = +1000000000000000;
+
+ // State
+ static ImS32 s32_v = -1;
+ static ImU32 u32_v = (ImU32)-1;
+ static ImS64 s64_v = -1;
+ static ImU64 u64_v = (ImU64)-1;
+ static float f32_v = 0.123f;
+ static double f64_v = 90000.01234567890123456789;
+
+ const float drag_speed = 0.2f;
+ static bool drag_clamp = false;
+ ImGui::Text("Drags:");
+ ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); ShowHelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value.");
+ ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL);
+ ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms");
+ ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);
+ ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);
+ ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f);
+ ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); ShowHelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range.");
+ ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f);
+ ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f);
+
+ ImGui::Text("Sliders");
+ ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d");
+ ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d");
+ ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d");
+ ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u");
+ ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u");
+ ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u");
+ ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d");
+ ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d");
+ ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d");
+ ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms");
+ ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms");
+ ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms");
+ ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one);
+ ImGui::SliderScalar("slider float low^2", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", 2.0f);
+ ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e");
+ ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams", 1.0f);
+ ImGui::SliderScalar("slider double low^2",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", 2.0f);
+ ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams", 1.0f);
+
+ static bool inputs_step = true;
+ ImGui::Text("Inputs");
+ ImGui::Checkbox("Show step buttons", &inputs_step);
+ ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d");
+ ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal);
+ ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u");
+ ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal);
+ ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL);
+ ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL);
+ ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL);
+ ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL);
+
ImGui::TreePop();
}
@@ -1006,16 +1083,16 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::InputFloat2("input float2", vec4f);
ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f);
ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f);
- ImGui::DragInt2("drag int2", vec4i, 1, 0, 255);
ImGui::InputInt2("input int2", vec4i);
+ ImGui::DragInt2("drag int2", vec4i, 1, 0, 255);
ImGui::SliderInt2("slider int2", vec4i, 0, 255);
ImGui::Spacing();
ImGui::InputFloat3("input float3", vec4f);
ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f);
ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f);
- ImGui::DragInt3("drag int3", vec4i, 1, 0, 255);
ImGui::InputInt3("input int3", vec4i);
+ ImGui::DragInt3("drag int3", vec4i, 1, 0, 255);
ImGui::SliderInt3("slider int3", vec4i, 0, 255);
ImGui::Spacing();
@@ -1297,10 +1374,12 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Button("LEVERAGE\nBUZZWORD", size);
ImGui::SameLine();
- ImGui::ListBoxHeader("List", size);
- ImGui::Selectable("Selected", true);
- ImGui::Selectable("Not Selected", false);
- ImGui::ListBoxFooter();
+ if (ImGui::ListBoxHeader("List", size))
+ {
+ ImGui::Selectable("Selected", true);
+ ImGui::Selectable("Not Selected", false);
+ ImGui::ListBoxFooter();
+ }
ImGui::TreePop();
}
@@ -1367,9 +1446,9 @@ void ImGui::ShowDemoWindow(bool* p_open)
static int track_line = 50, scroll_to_px = 200;
ImGui::Checkbox("Track", &track);
ImGui::PushItemWidth(100);
- ImGui::SameLine(130); track |= ImGui::DragInt("##line", &track_line, 0.25f, 0, 99, "Line = %.0f");
+ ImGui::SameLine(130); track |= ImGui::DragInt("##line", &track_line, 0.25f, 0, 99, "Line = %d");
bool scroll_to = ImGui::Button("Scroll To Pos");
- ImGui::SameLine(130); scroll_to |= ImGui::DragInt("##pos_y", &scroll_to_px, 1.00f, 0, 9999, "Y = %.0f px");
+ ImGui::SameLine(130); scroll_to |= ImGui::DragInt("##pos_y", &scroll_to_px, 1.00f, 0, 9999, "Y = %d px");
ImGui::PopItemWidth();
if (scroll_to) track = false;
@@ -1706,14 +1785,14 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Text("ImGui");
ImGui::Button("Apple");
static float foo = 1.0f;
- ImGui::InputFloat("red", &foo, 0.05f, 0, 3);
+ ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f");
ImGui::Text("An extra line here.");
ImGui::NextColumn();
ImGui::Text("Sailor");
ImGui::Button("Corniflower");
static float bar = 1.0f;
- ImGui::InputFloat("blue", &bar, 0.05f, 0, 3);
+ ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f");
ImGui::NextColumn();
if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
@@ -1852,8 +1931,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
ImGui::SameLine(); ShowHelpMarker("Instruct ImGui to render a mouse cursor for you in software. 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::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 [beta]", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
+ ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard [beta]", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos);
ImGui::SameLine(); ShowHelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos.");
ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
@@ -1865,6 +1944,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
else
ImGui::Text("Mouse pos: <INVALID>");
+ 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); }
@@ -2169,6 +2249,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::Text("Alignment");
ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content.");
+ ImGui::Text("Safe Area Padding"); ImGui::SameLine(); ShowHelpMarker("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::TreePop();
}
@@ -2281,7 +2363,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
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));
- 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)
+ 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();
@@ -2468,21 +2551,26 @@ static void ShowExampleAppConstrainedResize(bool* p_open)
}
// Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use.
-static void ShowExampleAppFixedOverlay(bool* p_open)
+static void ShowExampleAppSimpleOverlay(bool* p_open)
{
const float DISTANCE = 10.0f;
static int corner = 0;
ImVec2 window_pos = ImVec2((corner & 1) ? ImGui::GetIO().DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? ImGui::GetIO().DisplaySize.y - DISTANCE : DISTANCE);
ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f);
- ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
+ if (corner != -1)
+ ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background
- if (ImGui::Begin("Example: Fixed Overlay", p_open, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNav))
+ if (ImGui::Begin("Example: Simple Overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNav))
{
- ImGui::Text("Simple overlay\nin the corner of the screen.\n(right-click to change position)");
+ ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)");
ImGui::Separator();
- ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y);
+ if (ImGui::IsMousePosValid())
+ ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y);
+ else
+ ImGui::Text("Mouse Position: <invalid>");
if (ImGui::BeginPopupContextWindow())
{
+ if (ImGui::MenuItem("Custom", NULL, corner == -1)) corner = -1;
if (ImGui::MenuItem("Top-left", NULL, corner == 0)) corner = 0;
if (ImGui::MenuItem("Top-right", NULL, corner == 1)) corner = 1;
if (ImGui::MenuItem("Bottom-left", NULL, corner == 2)) corner = 2;
@@ -2540,8 +2628,10 @@ static void ShowExampleAppCustomRendering(bool* p_open)
// Primitives
ImGui::Text("Primitives");
static float sz = 36.0f;
+ static float thickness = 4.0f;
static ImVec4 col = ImVec4(1.0f,1.0f,0.4f,1.0f);
ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f");
+ ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f");
ImGui::ColorEdit3("Color", &col.x);
{
const ImVec2 p = ImGui::GetCursorScreenPos();
@@ -2549,16 +2639,16 @@ static void ShowExampleAppCustomRendering(bool* p_open)
float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f;
for (int n = 0; n < 2; n++)
{
- float thickness = (n == 0) ? 1.0f : 4.0f;
- draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 20, thickness); x += sz+spacing;
- draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 0.0f, ImDrawCornerFlags_All, thickness); x += sz+spacing;
- draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_All, thickness); x += sz+spacing;
- draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight, thickness); x += sz+spacing;
- draw_list->AddTriangle(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32, thickness); x += sz+spacing;
- draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y ), col32, thickness); x += sz+spacing;
- draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, thickness); x += sz+spacing;
- draw_list->AddLine(ImVec2(x, y), ImVec2(x, y+sz), col32, thickness); x += spacing;
- draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x+sz*1.3f,y+sz*0.3f), ImVec2(x+sz-sz*1.3f,y+sz-sz*0.3f), ImVec2(x+sz, y+sz), col32, thickness);
+ float curr_thickness = (n == 0) ? 1.0f : thickness;
+ draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 20, curr_thickness); x += sz+spacing;
+ draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 0.0f, ImDrawCornerFlags_All, curr_thickness); x += sz+spacing;
+ draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_All, curr_thickness); x += sz+spacing;
+ draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight, curr_thickness); x += sz+spacing;
+ draw_list->AddTriangle(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32, curr_thickness); x += sz+spacing;
+ draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y ), col32, curr_thickness); x += sz+spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
+ draw_list->AddLine(ImVec2(x, y), ImVec2(x, y+sz), col32, curr_thickness); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
+ draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, curr_thickness); x += sz+spacing; // Diagonal line
+ draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x+sz*1.3f,y+sz*0.3f), ImVec2(x+sz-sz*1.3f,y+sz-sz*0.3f), ImVec2(x+sz, y+sz), col32, curr_thickness);
x = p.x + 4;
y += sz+spacing;
}
@@ -2567,6 +2657,9 @@ static void ShowExampleAppCustomRendering(bool* p_open)
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f); x += sz+spacing;
draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight); x += sz+spacing;
draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32); x += sz+spacing;
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+thickness), col32); x += sz+spacing; // Horizontal line (faster than AddLine, but only handle integer thickness)
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+thickness, y+sz), col32); x += spacing+spacing; // Vertical line (faster than AddLine, but only handle integer thickness)
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+1, y+1), col32); x += sz; // Pixel (faster than AddLine)
draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x+sz, y+sz), IM_COL32(0,0,0,255), IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255));
ImGui::Dummy(ImVec2((sz+spacing)*8, (sz+spacing)*3));
}
@@ -2580,7 +2673,7 @@ static void ShowExampleAppCustomRendering(bool* p_open)
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()
- // However you can draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos().
+ // 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
@@ -2643,7 +2736,7 @@ struct ExampleAppConsole
Commands.push_back("HISTORY");
Commands.push_back("CLEAR");
Commands.push_back("CLASSIFY"); // "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches.
- AddLog("Welcome to ImGui!");
+ AddLog("Welcome to Dear ImGui!");
}
~ExampleAppConsole()
{
@@ -2656,6 +2749,7 @@ struct ExampleAppConsole
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* buff = malloc(len); return (char*)memcpy(buff, (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; }
void ClearLog()
{
@@ -2731,7 +2825,7 @@ struct ExampleAppConsole
// ImGuiListClipper clipper(Items.Size);
// while (clipper.Step())
// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
- // However take 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.
+ // 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.
@@ -2754,7 +2848,7 @@ struct ExampleAppConsole
if (copy_to_clipboard)
ImGui::LogFinish();
if (ScrollToBottom)
- ImGui::SetScrollHere();
+ ImGui::SetScrollHere(1.0f);
ScrollToBottom = false;
ImGui::PopStyleVar();
ImGui::EndChild();
@@ -2764,8 +2858,7 @@ struct ExampleAppConsole
bool reclaim_focus = false;
if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this))
{
- char* input_end = InputBuf+strlen(InputBuf);
- while (input_end > InputBuf && input_end[-1] == ' ') { input_end--; } *input_end = 0;
+ Strtrim(InputBuf);
if (InputBuf[0])
ExecCommand(InputBuf);
strcpy(InputBuf, "");
diff --git a/imgui/imgui_draw.cpp b/imgui/imgui_draw.cpp
index 2606c1af..8ba9f098 100644
--- a/imgui/imgui_draw.cpp
+++ b/imgui/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.60
+// dear imgui, v1.61
// (drawing and font code)
// Contains implementation for
@@ -91,6 +91,7 @@ namespace IMGUI_STB_NAMESPACE
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
#endif
+#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)
@@ -101,11 +102,19 @@ namespace IMGUI_STB_NAMESPACE
#else
#include "stb_rect_pack.h"
#endif
+#endif
+#ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
-#define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x))
-#define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x))
-#define STBTT_assert(x) IM_ASSERT(x)
+#define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x))
+#define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x))
+#define STBTT_assert(x) IM_ASSERT(x)
+#define STBTT_fmod(x,y) ImFmod(x,y)
+#define STBTT_sqrt(x) ImSqrt(x)
+#define STBTT_pow(x,y) ImPow(x,y)
+#define STBTT_fabs(x) ImFabs(x)
+#define STBTT_ifloor(x) ((int)ImFloorStd(x))
+#define STBTT_iceil(x) ((int)ImCeil(x))
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#else
@@ -116,6 +125,7 @@ namespace IMGUI_STB_NAMESPACE
#else
#include "stb_truetype.h"
#endif
+#endif
#ifdef __GNUC__
#pragma GCC diagnostic pop
@@ -244,8 +254,6 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
- //colors[ImGuiCol_TextHovered] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
- //colors[ImGuiCol_TextActive] = ImVec4(1.00f, 1.00f, 0.00f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);
@@ -303,7 +311,7 @@ ImDrawListSharedData::ImDrawListSharedData()
for (int i = 0; i < IM_ARRAYSIZE(CircleVtx12); i++)
{
const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(CircleVtx12);
- CircleVtx12[i] = ImVec2(cosf(a), sinf(a));
+ CircleVtx12[i] = ImVec2(ImCos(a), ImSin(a));
}
}
@@ -901,7 +909,7 @@ void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float a_min, floa
for (int i = 0; i <= num_segments; i++)
{
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
- _Path.push_back(ImVec2(centre.x + cosf(a) * radius, centre.y + sinf(a) * radius));
+ _Path.push_back(ImVec2(centre.x + ImCos(a) * radius, centre.y + ImSin(a) * radius));
}
}
@@ -957,8 +965,8 @@ void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImV
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners)
{
- rounding = ImMin(rounding, fabsf(b.x - a.x) * ( ((rounding_corners & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f);
- rounding = ImMin(rounding, fabsf(b.y - a.y) * ( ((rounding_corners & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f);
+ rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((rounding_corners & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f);
+ rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((rounding_corners & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f);
if (rounding <= 0.0f || rounding_corners == 0)
{
@@ -1543,7 +1551,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
{
- int data_size = 0;
+ size_t data_size = 0;
void* data = ImFileLoadToMemory(filename, "rb", &data_size, 0);
if (!data)
{
@@ -1558,7 +1566,7 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {}
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels);
}
- return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges);
+ return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);
}
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
@@ -1733,7 +1741,7 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
- IM_ASSERT(font_offset >= 0);
+ IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
{
atlas->TexWidth = atlas->TexHeight = 0; // Reset output on failure
@@ -1776,19 +1784,34 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
buf_packedchars_n += range.num_chars;
}
- // Pack
+ // Gather the sizes of all rectangle we need
tmp.Rects = buf_rects + buf_rects_n;
tmp.RectsCount = font_glyphs_count;
buf_rects_n += font_glyphs_count;
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
IM_ASSERT(n == font_glyphs_count);
+
+ // Detect missing glyphs and replace them with a zero-sized box instead of relying on the default glyphs
+ // This allows us merging overlapping icon fonts more easily.
+ int rect_i = 0;
+ for (int range_i = 0; range_i < tmp.RangesCount; range_i++)
+ for (int char_i = 0; char_i < tmp.Ranges[range_i].num_chars; char_i++, rect_i++)
+ if (stbtt_FindGlyphIndex(&tmp.FontInfo, tmp.Ranges[range_i].first_unicode_codepoint_in_range + char_i) == 0)
+ tmp.Rects[rect_i].w = tmp.Rects[rect_i].h = 0;
+
+ // Pack
stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);
// Extend texture height
+ // Also mark missing glyphs as non-packed so we don't attempt to render into them
for (int i = 0; i < n; i++)
+ {
+ if (tmp.Rects[i].w == 0 && tmp.Rects[i].h == 0)
+ tmp.Rects[i].was_packed = 0;
if (tmp.Rects[i].was_packed)
atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
+ }
}
IM_ASSERT(buf_rects_n == total_glyphs_count);
IM_ASSERT(buf_packedchars_n == total_glyphs_count);
@@ -2353,7 +2376,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
}
const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX);
- if (ImCharIsSpace(c))
+ if (ImCharIsBlankW(c))
{
if (inside_word)
{
@@ -2436,7 +2459,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
while (s < text_end)
{
const char c = *s;
- if (ImCharIsSpace((unsigned int)c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
+ if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
}
continue;
}
@@ -2561,7 +2584,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
while (s < text_end)
{
const char c = *s;
- if (ImCharIsSpace((unsigned int)c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
+ if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; }
}
continue;
}
@@ -2685,7 +2708,7 @@ static inline float ImAcos01(float x)
{
if (x <= 0.0f) return IM_PI * 0.5f;
if (x >= 1.0f) return 0.0f;
- return acosf(x);
+ return ImAcos(x);
//return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do.
}
diff --git a/imgui/imgui_internal.h b/imgui/imgui_internal.h
index 0af53025..7ec9aa9a 100644
--- a/imgui/imgui_internal.h
+++ b/imgui/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.60
+// dear imgui, v1.61
// (internals)
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
@@ -13,6 +13,7 @@
#endif
#include <stdio.h> // FILE*
+#include <stdlib.h> // NULL, malloc, free, qsort, atoi, atof
#include <math.h> // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf
#include <limits.h> // INT_MIN, INT_MAX
@@ -82,7 +83,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe
#define IM_PI 3.14159265358979323846f
#ifdef _WIN32
-#define IM_NEWLINE "\r\n" // Play it nice with Windows users (2018: Notepad _still_ doesn't display files properly when they use Unix-style carriage returns)
+#define IM_NEWLINE "\r\n" // Play it nice with Windows users (2018/05 news: Microsoft announced that Notepad will finally display Unix-style carriage returns!)
#else
#define IM_NEWLINE "\n"
#endif
@@ -96,9 +97,10 @@ IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, cons
// Helpers: Misc
IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings
-IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0);
+IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size = NULL, int padding_bytes = 0);
IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode);
-static inline bool ImCharIsSpace(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
+static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
+static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; }
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
@@ -117,11 +119,17 @@ IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end
IMGUI_API int ImStrlenW(const ImWchar* str);
IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
+IMGUI_API void ImStrTrimBlanks(char* str);
IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);
IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3);
-
-// Helpers: Math
-// We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined)
+IMGUI_API const char* ImParseFormatFindStart(const char* format);
+IMGUI_API const char* ImParseFormatFindEnd(const char* format);
+IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, int buf_size);
+IMGUI_API int ImParseFormatPrecision(const char* format, int default_value);
+
+// Helpers: ImVec2/ImVec4 operators
+// 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); }
@@ -138,26 +146,40 @@ static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs)
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
-static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; }
-static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; }
-static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; }
-static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; }
+// Helpers: Maths
+// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy)
+#ifndef IMGUI_DISABLE_MATH_FUNCTIONS
+static inline float ImFabs(float x) { return fabsf(x); }
+static inline float ImSqrt(float x) { return sqrtf(x); }
+static inline float ImPow(float x, float y) { return powf(x, y); }
+static inline double ImPow(double x, double y) { return pow(x, y); }
+static inline float ImFmod(float x, float y) { return fmodf(x, y); }
+static inline double ImFmod(double x, double y) { return fmod(x, y); }
+static inline float ImCos(float x) { return cosf(x); }
+static inline float ImSin(float x) { return sinf(x); }
+static inline float ImAcos(float x) { return acosf(x); }
+static inline float ImAtan2(float y, float x) { return atan2f(y, x); }
+static inline double ImAtof(const char* s) { return atof(s); }
+static inline float ImFloorStd(float x) { return floorf(x); } // we already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by stb_truetype)
+static inline float ImCeil(float x) { return ceilf(x); }
+#endif
+// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support for variety of types: signed/unsigned int/long long float/double, using templates here but we could also redefine them 6 times
+template<typename T> static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; }
+template<typename T> static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; }
+template<typename T> static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
+template<typename T> static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); }
+template<typename T> static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; }
+// - Misc maths helpers
static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); }
static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); }
-static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
-static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
-static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); }
-static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
-static inline void ImSwap(int& a, int& b) { int tmp = a; a = b; b = tmp; }
-static inline void ImSwap(float& a, float& b) { float tmp = a; a = b; b = tmp; }
-static inline int ImLerp(int a, int b, float t) { return (int)(a + (b - a) * t); }
-static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; }
+static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2& mn, ImVec2 mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); }
static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); }
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 / sqrtf(d); return fail_value; }
+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 float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
@@ -244,14 +266,6 @@ enum ImGuiPlotType
ImGuiPlotType_Histogram
};
-enum ImGuiDataType
-{
- ImGuiDataType_Int,
- ImGuiDataType_Float,
- ImGuiDataType_Double,
- ImGuiDataType_COUNT
-};
-
enum ImGuiInputSource
{
ImGuiInputSource_None = 0,
@@ -322,11 +336,10 @@ struct IMGUI_API ImRect
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& v) { Min.x += v.x; Min.y += v.y; Max.x += v.x; Max.y += v.y; }
+ void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }
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 = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; }
- void FixInverted() { if (Min.x > Max.x) ImSwap(Min.x, Max.x); if (Min.y > Max.y) ImSwap(Min.y, Max.y); }
bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; }
};
@@ -415,9 +428,9 @@ struct ImGuiSettingsHandler
{
const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']'
ImGuiID TypeHash; // == ImHash(TypeName, 0, 0)
- void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);
- void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line);
- void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf);
+ 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)); }
@@ -529,10 +542,11 @@ struct ImGuiNextWindowData
ImVec2 SizeVal;
ImVec2 ContentSizeVal;
bool CollapsedVal;
- ImRect SizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true
+ ImRect SizeConstraintRect;
ImGuiSizeCallback SizeCallback;
void* SizeCallbackUserData;
float BgAlphaVal;
+ ImVec2 MenuBarOffsetMinVal; // This is not exposed publicly, so we don't clear it.
ImGuiNextWindowData()
{
@@ -544,6 +558,7 @@ struct ImGuiNextWindowData
SizeCallback = NULL;
SizeCallbackUserData = NULL;
BgAlphaVal = FLT_MAX;
+ MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
}
void Clear()
@@ -662,37 +677,36 @@ struct ImGuiContext
ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc.
ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets
ImVec4 ColorPickerRef;
- float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings
- ImVec2 DragLastMouseDelta;
+ bool DragCurrentAccumDirty;
+ float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings
float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
- float DragSpeedScaleSlow;
- float DragSpeedScaleFast;
ImVec2 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
- ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor
+ ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor
// Settings
bool SettingsLoaded;
- float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero
- ImVector<ImGuiWindowSettings> SettingsWindows; // .ini settings for ImGuiWindow
+ float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero
+ ImGuiTextBuffer SettingsIniData; // In memory .ini settings
ImVector<ImGuiSettingsHandler> SettingsHandlers; // List of .ini settings handlers
+ ImVector<ImGuiWindowSettings> SettingsWindows; // ImGuiWindow .ini settings entries (parsed from the last loaded .ini file and maintained on saving)
// Logging
bool LogEnabled;
FILE* LogFile; // If != NULL log to stdout/ file
- ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
+ ImGuiTextBuffer LogClipboard; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
int LogStartDepth;
int LogAutoExpandMaxDepth;
// Misc
- float FramerateSecPerFrame[120]; // calculate estimate of framerate for user
+ float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds.
int FramerateSecPerFrameIdx;
float FramerateSecPerFrameAccum;
- int WantCaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags
+ 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) : OverlayDrawList(NULL)
{
@@ -767,21 +781,18 @@ struct ImGuiContext
ScalarAsInputTextId = 0;
ColorEditOptions = ImGuiColorEditFlags__OptionsDefault;
- DragCurrentValue = 0.0f;
- DragLastMouseDelta = ImVec2(0.0f, 0.0f);
+ DragCurrentAccumDirty = false;
+ DragCurrentAccum = 0.0f;
DragSpeedDefaultRatio = 1.0f / 100.0f;
- DragSpeedScaleSlow = 1.0f / 100.0f;
- DragSpeedScaleFast = 10.0f;
ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f);
TooltipOverrideCount = 0;
- OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f);
+ PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX);
SettingsLoaded = false;
SettingsDirtyTimer = 0.0f;
LogEnabled = false;
LogFile = NULL;
- LogClipboard = NULL;
LogStartDepth = 0;
LogAutoExpandMaxDepth = 2;
@@ -832,7 +843,7 @@ struct IMGUI_API ImGuiDrawContext
int NavLayerActiveMask; // Which layer have been written to (result from previous frame)
int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame)
bool MenuBarAppending; // FIXME: Remove this
- float MenuBarOffsetX;
+ ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
ImVector<ImGuiWindow*> ChildWindows;
ImGuiStorage* StateStorage;
ImGuiLayoutType LayoutType;
@@ -870,7 +881,7 @@ struct IMGUI_API ImGuiDrawContext
NavLayerCurrent = 0;
NavLayerCurrentMask = 1 << 0;
MenuBarAppending = false;
- MenuBarOffsetX = 0.0f;
+ MenuBarOffset = ImVec2(0.0f, 0.0f);
StateStorage = NULL;
LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical;
ItemWidth = 0.0f;
@@ -891,14 +902,12 @@ struct IMGUI_API ImGuiWindow
char* Name;
ImGuiID ID; // == ImHash(Name)
ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_
- ImVec2 PosFloat;
- ImVec2 Pos; // Position rounded-up to nearest pixel
+ ImVec2 Pos; // Position (always rounded-up to nearest pixel)
ImVec2 Size; // Current size (==SizeFull or collapsed title bar size)
ImVec2 SizeFull; // Size when non collapsed
ImVec2 SizeFullAtLastBegin; // Copy of SizeFull at the end of Begin. This is the reference value we'll use on the next frame to decide if we need scrollbars.
ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame. Include decoration, window title, border, menu, etc.
ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize()
- ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis
ImVec2 WindowPadding; // Window padding at the time of begin.
float WindowRounding; // Window rounding at the time of begin.
float WindowBorderSize; // Window border size at the time of begin.
@@ -907,8 +916,8 @@ struct IMGUI_API ImGuiWindow
ImVec2 Scroll;
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
bool ScrollbarX, ScrollbarY;
- ImVec2 ScrollbarSizes;
bool Active; // Set to true on Begin(), unless Collapsed
bool WasActive;
bool WriteAccessed; // Set to true when any widget access the current window
@@ -926,24 +935,27 @@ struct IMGUI_API ImGuiWindow
int AutoFitChildAxises;
ImGuiDir AutoPosLastDirection;
int HiddenFrames;
- ImGuiCond SetWindowPosAllowFlags; // store condition flags for next SetWindowPos() call.
- ImGuiCond SetWindowSizeAllowFlags; // store condition flags for next SetWindowSize() call.
- ImGuiCond SetWindowCollapsedAllowFlags; // store condition flags for next SetWindowCollapsed() call.
+ ImGuiCond SetWindowPosAllowFlags; // store acceptable condition flags for SetNextWindowPos() use.
+ 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.
ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame
ImVector<ImGuiID> IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack
- ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2.
- ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window.
- ImRect InnerRect, InnerClipRect;
+ ImRect ClipRect; // Current clipping rectangle. = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2.
+ ImRect OuterRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window.
+ ImRect InnerMainRect, InnerClipRect;
+ ImRect ContentsRegionRect; // FIXME: This is currently confusing/misleading. Maximum visible content position ~~ Pos + (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis
int LastFrameActive;
float ItemWidthDefault;
ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items
ImGuiStorage StateStorage;
ImVector<ImGuiColumnsSet> ColumnsStorage;
- float FontWindowScale; // Scale multiplier per-window
- ImDrawList* DrawList;
+ float FontWindowScale; // User scale multiplier per-window
+
+ ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)
+ ImDrawList DrawListInst;
ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL.
ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window.
ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
@@ -977,7 +989,7 @@ public:
float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; }
float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; }
ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
- float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; }
+ float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + GImGui->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()); }
};
@@ -1020,6 +1032,7 @@ namespace ImGui
IMGUI_API void NewFrameUpdateHoveredWindowAndCaptureFlags();
IMGUI_API void MarkIniSettingsDirty();
+ IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window);
IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);
IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id);
@@ -1052,6 +1065,7 @@ namespace ImGui
IMGUI_API bool IsPopupOpen(ImGuiID id);
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true);
+ IMGUI_API ImGuiWindow* GetFrontMostPopupModal();
IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit);
IMGUI_API void NavMoveRequestCancel();
@@ -1093,19 +1107,11 @@ namespace ImGui
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, float radius);
- IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0);
- IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power);
- IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format);
-
- IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power);
- IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power);
- IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format);
+ IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power);
+ IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags = 0);
IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);
- IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags);
- IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags);
- IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags);
- IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision);
+ IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format);
IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags);
IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags);
@@ -1116,9 +1122,6 @@ namespace ImGui
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 graph_size);
- IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value);
- IMGUI_API float RoundScalar(float value, int decimal_precision);
-
// Shade functions (write over already created vertices)
IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);
IMGUI_API void ShadeVertsLinearAlphaGradientForLeftToRightText(ImDrawVert* vert_start, ImDrawVert* vert_end, float gradient_p0_x, float gradient_p1_x);
diff --git a/imgui/stb_textedit.h b/imgui/stb_textedit.h
index 7324fb6b..270153c1 100644
--- a/imgui/stb_textedit.h
+++ b/imgui/stb_textedit.h
@@ -1,4 +1,4 @@
-// [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb
+// [ImGui] this is a slightly modified version of stb_textedit.h 1.9. Those changes would need to be pushed into nothings/stb
// [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815)
// [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715)
// [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681)