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
diff options
context:
space:
mode:
authorBartosz Taudul <wolf.pld@gmail.com>2018-01-13 15:52:52 +0300
committerBartosz Taudul <wolf.pld@gmail.com>2018-01-13 15:52:52 +0300
commit9330e950da6cc101e8b2e33cad4d99849911bd81 (patch)
tree2728a0e7b717f9a78a1d33b66d7184c4fa24c624 /imgui/imgui.cpp
parent7300c2e46eb5d5ed249a66ef06021bae5bb043bf (diff)
Bump ImGui to 1.53.
Diffstat (limited to 'imgui/imgui.cpp')
-rw-r--r--imgui/imgui.cpp2630
1 files changed, 1741 insertions, 889 deletions
diff --git a/imgui/imgui.cpp b/imgui/imgui.cpp
index b15c059f..075fb8c4 100644
--- a/imgui/imgui.cpp
+++ b/imgui/imgui.cpp
@@ -1,14 +1,14 @@
-// dear imgui, v1.52
+// dear imgui, v1.53
// (main code and documentation)
-// See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
+// Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
// Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
// Get latest version at https://github.com/ocornut/imgui
// Releases change-log at https://github.com/ocornut/imgui/releases
// Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// This library is free but I need your support to sustain development and maintenance.
-// If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
+// If you work for a company, please consider financial support, see Readme. For individuals: https://www.patreon.com/imgui
/*
@@ -59,23 +59,21 @@
END-USER GUIDE
==============
- - Double-click title bar to collapse window
- - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin()
- - Click and drag on lower right corner to resize window
- - Click and drag on any empty space to move window
- - Double-click/double-tap on lower right corner grip to auto-fit to content
- - TAB/SHIFT+TAB to cycle through keyboard editable fields
- - Use mouse wheel to scroll
- - Use CTRL+mouse wheel to zoom window contents (if io.FontAllowScaling is true)
- - CTRL+Click on a slider or drag box to input value as text
+ - Double-click on title bar to collapse window.
+ - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
+ - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
+ - Click and drag on any empty space to move window.
+ - TAB/SHIFT+TAB to cycle through keyboard editable fields.
+ - CTRL+Click on a slider or drag box to input value as text.
+ - Use mouse wheel to scroll.
- Text editor:
- Hold SHIFT or use mouse to select text.
- - CTRL+Left/Right to word jump
- - CTRL+Shift+Left/Right to select words
- - CTRL+A our Double-Click to select all
- - CTRL+X,CTRL+C,CTRL+V to use OS clipboard
- - CTRL+Z,CTRL+Y to undo/redo
- - ESCAPE to revert text to its original value
+ - CTRL+Left/Right to word jump.
+ - CTRL+Shift+Left/Right to select words.
+ - CTRL+A our Double-Click to select all.
+ - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
+ - CTRL+Z,CTRL+Y to undo/redo.
+ - ESCAPE to revert text to its original value.
- You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
- Controls are automatically adjusted for OSX to match standard OSX text editing operations.
@@ -88,7 +86,7 @@
- Read the FAQ below this section!
- Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention
on your side, no state duplication, less sync, less bugs.
- - Call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
+ - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
- You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
@@ -105,8 +103,7 @@
- Add the Dear ImGui source files to your projects, using your preferred build system.
It is recommended you build the .cpp files as part of your project and not as a library.
- You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
- - See examples/ folder for standalone sample applications. To understand the integration process, you can read examples/opengl2_example/ because
- it is short, then switch to the one more appropriate to your use case.
+ - See examples/ folder for standalone sample applications.
- You may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
- When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
@@ -216,6 +213,22 @@
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.
+ - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
+ - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags
+ - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.
+ - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.
+ - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
+ - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
+ - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
+ - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
+ - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
+ - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.
+ - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
+ Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
+ - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
+ - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
+ - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
+ - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
- 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
- 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
- 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
@@ -564,7 +577,7 @@
- tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
of a deep nested inner loop in your code.
- tip: you can call Render() multiple times (e.g for VR renders).
- - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
+ - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
*/
@@ -574,7 +587,6 @@
#include "imgui.h"
#define IMGUI_DEFINE_MATH_OPERATORS
-#define IMGUI_DEFINE_PLACEMENT_NEW
#include "imgui_internal.h"
#include <ctype.h> // toupper, isprint
@@ -618,8 +630,6 @@
// Forward Declarations
//-------------------------------------------------------------------------
-static float GetDraggedColumnOffset(int column_index);
-
static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);
static ImFont* GetDefaultFont();
@@ -639,10 +649,12 @@ static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_rende
static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window);
-static ImGuiIniData* FindWindowSettings(const char* name);
-static ImGuiIniData* AddWindowSettings(const char* name);
+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 GetVisibleRect();
@@ -650,7 +662,6 @@ static ImRect GetVisibleRect();
static void CloseInactivePopups(ImGuiWindow* ref_window);
static void ClosePopupToLevel(int remaining);
static ImGuiWindow* GetFrontMostModalRootWindow();
-static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
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);
@@ -701,12 +712,17 @@ ImGuiStyle::ImGuiStyle()
{
Alpha = 1.0f; // Global alpha applies to everything in ImGui
WindowPadding = ImVec2(8,8); // Padding within a window
+ WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
+ WindowBorderSize = 0.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
WindowMinSize = ImVec2(32,32); // Minimum window size
- WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
- ChildWindowRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
+ ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
+ ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
+ PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
+ PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
+ FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
@@ -720,83 +736,34 @@ ImGuiStyle::ImGuiStyle()
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.
AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
- AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
- CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
+ AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
+ CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
ImGui::StyleColorsClassic(this);
}
-void ImGui::StyleColorsClassic(ImGuiStyle* dst)
-{
- ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();
- ImVec4* colors = style->Colors;
-
- colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
- colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
- colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
- colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
- colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
- colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.40f);
- colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
- colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input
- colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
- colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
- colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
- colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
- colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
- colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
- colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
- colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
- colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
- colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
- colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
- colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
- colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
- colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
- colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
- colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
- colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
- colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
- colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
- colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
- colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
- colors[ImGuiCol_SeparatorHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f);
- colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f);
- colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
- colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
- colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
- colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
- colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
- colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
- colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
- colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
- colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
- colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
- colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
- colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
-}
-
// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
-// Tips: if you need to change your scale multiple times, prefer calling this on a freshly initialized ImGuiStyle structure rather than scaling multiple times (because floating point multiplications are lossy).
+// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
void ImGuiStyle::ScaleAllSizes(float scale_factor)
{
- WindowPadding *= scale_factor;
- WindowMinSize *= scale_factor;
- WindowRounding *= scale_factor;
- ChildWindowRounding *= scale_factor;
- FramePadding *= scale_factor;
- FrameRounding *= scale_factor;
- ItemSpacing *= scale_factor;
- ItemInnerSpacing *= scale_factor;
- TouchExtraPadding *= scale_factor;
- IndentSpacing *= scale_factor;
- ColumnsMinSpacing *= scale_factor;
- ScrollbarSize *= scale_factor;
- ScrollbarRounding *= scale_factor;
- GrabMinSize *= scale_factor;
- GrabRounding *= scale_factor;
- DisplayWindowPadding *= scale_factor;
- DisplaySafeAreaPadding *= scale_factor;
+ WindowPadding = ImFloor(WindowPadding * scale_factor);
+ WindowRounding = ImFloor(WindowRounding * scale_factor);
+ WindowMinSize = ImFloor(WindowMinSize * scale_factor);
+ ChildRounding = ImFloor(ChildRounding * scale_factor);
+ PopupRounding = ImFloor(PopupRounding * scale_factor);
+ FramePadding = ImFloor(FramePadding * scale_factor);
+ FrameRounding = ImFloor(FrameRounding * scale_factor);
+ ItemSpacing = ImFloor(ItemSpacing * scale_factor);
+ ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
+ TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
+ IndentSpacing = ImFloor(IndentSpacing * scale_factor);
+ ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
+ ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
+ ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
+ GrabMinSize = ImFloor(GrabMinSize * scale_factor);
+ GrabRounding = ImFloor(GrabRounding * scale_factor);
+ DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
+ DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
}
ImGuiIO::ImGuiIO()
@@ -825,7 +792,15 @@ ImGuiIO::ImGuiIO()
DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
- // User functions
+ // Advanced/subtle behaviors
+#ifdef __APPLE__
+ OptMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
+#else
+ OptMacOSXBehaviors = false;
+#endif
+ OptCursorBlink = true;
+
+ // Settings (User Functions)
RenderDrawListsFn = NULL;
MemAllocFn = malloc;
MemFreeFn = free;
@@ -841,11 +816,6 @@ ImGuiIO::ImGuiIO()
MouseDragThreshold = 6.0f;
for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
-
- // Set OS X style defaults based on __APPLE__ compile time flag
-#ifdef __APPLE__
- OSXBehaviors = true;
-#endif
}
// Pass in translated ASCII characters for text input.
@@ -941,25 +911,33 @@ int ImStricmp(const char* str1, const char* str2)
return d;
}
-int ImStrnicmp(const char* str1, const char* str2, int count)
+int ImStrnicmp(const char* str1, const char* str2, size_t count)
{
int d = 0;
while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
return d;
}
-void ImStrncpy(char* dst, const char* src, int count)
+void ImStrncpy(char* dst, const char* src, size_t count)
{
if (count < 1) return;
- strncpy(dst, src, (size_t)count);
+ strncpy(dst, src, count);
dst[count-1] = 0;
}
char* ImStrdup(const char *str)
{
size_t len = strlen(str) + 1;
- void* buff = ImGui::MemAlloc(len);
- return (char*)memcpy(buff, (const void*)str, len);
+ void* buf = ImGui::MemAlloc(len);
+ return (char*)memcpy(buf, (const void*)str, len);
+}
+
+char* ImStrchrRange(const char* str, const char* str_end, char c)
+{
+ for ( ; str < str_end; str++)
+ if (*str == c)
+ return (char*)str;
+ return NULL;
}
int ImStrlenW(const ImWchar* str)
@@ -1014,7 +992,7 @@ static const char* ImAtoi(const char* src, int* output)
// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
// B) When buf==NULL vsnprintf() will return the output size.
#ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
-int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
+int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
@@ -1022,19 +1000,19 @@ int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
va_end(args);
if (buf == NULL)
return w;
- if (w == -1 || w >= buf_size)
- w = buf_size - 1;
+ if (w == -1 || w >= (int)buf_size)
+ w = (int)buf_size - 1;
buf[w] = 0;
return w;
}
-int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
+int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
{
int w = vsnprintf(buf, buf_size, fmt, args);
if (buf == NULL)
return w;
- if (w == -1 || w >= buf_size)
- w = buf_size - 1;
+ if (w == -1 || w >= (int)buf_size)
+ w = (int)buf_size - 1;
buf[w] = 0;
return w;
}
@@ -1423,23 +1401,18 @@ void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int*
//-----------------------------------------------------------------------------
// ImGuiStorage
-//-----------------------------------------------------------------------------
-
// Helper: Key->value storage
-void ImGuiStorage::Clear()
-{
- Data.clear();
-}
+//-----------------------------------------------------------------------------
// std::lower_bound but without the bullshit
static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
{
ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
ImVector<ImGuiStorage::Pair>::iterator last = data.end();
- int count = (int)(last - first);
+ size_t count = (size_t)(last - first);
while (count > 0)
{
- int count2 = count / 2;
+ size_t count2 = count >> 1;
ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
if (mid->key < key)
{
@@ -1454,6 +1427,23 @@ static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::
return first;
}
+// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
+void ImGuiStorage::BuildSortByKey()
+{
+ struct StaticFunc
+ {
+ static int PairCompareByID(const void* lhs, const void* rhs)
+ {
+ // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
+ if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
+ if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
+ return 0;
+ }
+ };
+ if (Data.Size > 1)
+ qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
+}
+
int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
{
ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
@@ -1669,7 +1659,7 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
#endif
// Helper: Text buffer for logging/accumulating text
-void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
+void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
{
va_list args_copy;
va_copy(args_copy, args);
@@ -1690,16 +1680,16 @@ void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
ImFormatStringV(&Buf[write_off - 1], len + 1, fmt, args_copy);
}
-void ImGuiTextBuffer::append(const char* fmt, ...)
+void ImGuiTextBuffer::appendf(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
- appendv(fmt, args);
+ appendfv(fmt, args);
va_end(args);
}
//-----------------------------------------------------------------------------
-// ImGuiSimpleColumns
+// ImGuiSimpleColumns (internal use only)
//-----------------------------------------------------------------------------
ImGuiSimpleColumns::ImGuiSimpleColumns()
@@ -1755,8 +1745,8 @@ static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
ImGuiWindow* window = ImGui::GetCurrentWindow();
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
- if (window->DC.ColumnsCount > 1)
- window->DC.ColumnsCellMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
+ if (window->DC.ColumnsSet)
+ window->DC.ColumnsSet->CellMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
}
// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
@@ -1830,35 +1820,38 @@ bool ImGuiListClipper::Step()
// ImGuiWindow
//-----------------------------------------------------------------------------
-ImGuiWindow::ImGuiWindow(const char* name)
+ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
{
Name = ImStrdup(name);
ID = ImHash(name, 0);
IDStack.push_back(ID);
Flags = 0;
- OrderWithinParent = 0;
PosFloat = 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);
+ WindowRounding = 0.0f;
+ WindowBorderSize = 0.0f;
MoveId = GetID("#MOVE");
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);
- BorderSize = 0.0f;
Active = WasActive = false;
- Accessed = false;
+ WriteAccessed = false;
Collapsed = false;
SkipItems = false;
Appearing = false;
+ CloseButton = false;
+ BeginOrderWithinParent = -1;
+ BeginOrderWithinContext = -1;
BeginCount = 0;
PopupId = 0;
AutoFitFramesX = AutoFitFramesY = -1;
AutoFitOnlyGrows = false;
AutoFitChildAxises = 0x00;
- AutoPosLastDirection = -1;
+ AutoPosLastDirection = ImGuiDir_None;
HiddenFrames = 0;
SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
@@ -1867,8 +1860,7 @@ ImGuiWindow::ImGuiWindow(const char* name)
ItemWidthDefault = 0.0f;
FontWindowScale = 1.0f;
- DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
- IM_PLACEMENT_NEW(DrawList) ImDrawList();
+ DrawList = IM_NEW(ImDrawList)(&context->DrawListSharedData);
DrawList->_OwnerName = Name;
ParentWindow = NULL;
RootWindow = NULL;
@@ -1881,11 +1873,8 @@ ImGuiWindow::ImGuiWindow(const char* name)
ImGuiWindow::~ImGuiWindow()
{
- DrawList->~ImDrawList();
- ImGui::MemFree(DrawList);
- DrawList = NULL;
- ImGui::MemFree(Name);
- Name = NULL;
+ IM_DELETE(DrawList);
+ IM_DELETE(Name);
}
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
@@ -1910,6 +1899,16 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
}
+// This is only used in rare/specific situations to manufacture an ID out of nowhere.
+ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
+{
+ ImGuiID seed = IDStack.back();
+ const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) };
+ ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
+ ImGui::KeepAliveID(id);
+ return id;
+}
+
//-----------------------------------------------------------------------------
// Internal API exposed in imgui_internal.h
//-----------------------------------------------------------------------------
@@ -1919,20 +1918,15 @@ static void SetCurrentWindow(ImGuiWindow* window)
ImGuiContext& g = *GImGui;
g.CurrentWindow = window;
if (window)
- g.FontSize = window->CalcFontSize();
-}
-
-ImGuiWindow* ImGui::GetParentWindow()
-{
- ImGuiContext& g = *GImGui;
- IM_ASSERT(g.CurrentWindowStack.Size >= 2);
- return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2];
+ g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
}
void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
g.ActiveIdIsJustActivated = (g.ActiveId != id);
+ if (g.ActiveIdIsJustActivated)
+ g.ActiveIdTimer = 0.0f;
g.ActiveId = id;
g.ActiveIdAllowOverlap = false;
g.ActiveIdIsAlive |= (id != 0);
@@ -1949,6 +1943,7 @@ void ImGui::SetHoveredID(ImGuiID id)
ImGuiContext& g = *GImGui;
g.HoveredId = id;
g.HoveredIdAllowOverlap = false;
+ g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f;
}
void ImGui::KeepAliveID(ImGuiID id)
@@ -1993,7 +1988,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
- window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
+ window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
window->DC.PrevLineHeight = line_height;
@@ -2031,28 +2026,42 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id)
}
// This is roughly matching the behavior of internal-facing ItemHoverable()
-// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered())
+// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
{
ImGuiContext& g = *GImGui;
-
ImGuiWindow* window = g.CurrentWindow;
+
+ // Test for bounding box overlap, as updated as ItemAdd()
if (!window->DC.LastItemRectHoveredRect)
return false;
+ IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
+
+ // Test if we are hovering the right window (our window could be behind another window)
// [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
// Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
//if (g.HoveredWindow != window)
// return false;
if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
return false;
+
+ // Test if another item is active (e.g. being dragged)
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
return false;
+
+ // Test if interactions on this window are blocked by an active popup or modal
if (!IsWindowContentHoverable(window, flags))
return false;
+
+ // Test if the item is disabled
if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
return false;
+
+ // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
+ if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
+ return false;
return true;
}
@@ -2238,31 +2247,47 @@ int ImGui::GetFrameCount()
return GImGui->FrameCount;
}
+ImDrawList* ImGui::GetOverlayDrawList()
+{
+ return &GImGui->OverlayDrawList;
+}
+
+ImDrawListSharedData* ImGui::GetDrawListSharedData()
+{
+ return &GImGui->DrawListSharedData;
+}
+
void ImGui::NewFrame()
{
ImGuiContext& g = *GImGui;
// Check user data
- 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);
- IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
- IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
- IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid style setting
- IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f); // Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)
+ // (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)
+ 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");
+ IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?");
+ IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?");
+ IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting");
+ IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)");
+ IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
// Initialize on first frame
if (!g.Initialized)
- ImGui::Initialize();
+ Initialize();
SetCurrentFont(GetDefaultFont());
IM_ASSERT(g.Font->IsLoaded());
+ g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
+ g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
g.Time += g.IO.DeltaTime;
g.FrameCount += 1;
g.TooltipOverrideCount = 0;
+ g.WindowsActiveCount = 0;
g.OverlayDrawList.Clear();
g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
g.OverlayDrawList.PushClipRectFullScreen();
+ g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
// Mark rendering data as invalid to prevent user who may have a handle on it to use it
g.RenderDrawData.Valid = false;
@@ -2270,17 +2295,32 @@ void ImGui::NewFrame()
g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
// Clear reference to active widget if the widget isn't alive anymore
+ if (!g.HoveredIdPreviousFrame)
+ g.HoveredIdTimer = 0.0f;
g.HoveredIdPreviousFrame = g.HoveredId;
g.HoveredId = 0;
g.HoveredIdAllowOverlap = false;
if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
ClearActiveID();
+ if (g.ActiveId)
+ g.ActiveIdTimer += g.IO.DeltaTime;
g.ActiveIdPreviousFrame = g.ActiveId;
g.ActiveIdIsAlive = false;
g.ActiveIdIsJustActivated = false;
if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
g.ScalarAsInputTextId = 0;
+ // Elapse drag & drop payload
+ if (g.DragDropActive && g.DragDropPayload.DataFrameCount + 1 < g.FrameCount)
+ {
+ ClearDragDrop();
+ g.DragDropPayloadBufHeap.clear();
+ memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
+ }
+ g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
+ g.DragDropAcceptIdCurr = 0;
+ g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
+
// Update keyboard input state
memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
@@ -2313,11 +2353,15 @@ void ImGui::NewFrame()
g.IO.MouseClickedTime[i] = g.Time;
}
g.IO.MouseClickedPos[i] = g.IO.MousePos;
+ g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
}
else if (g.IO.MouseDown[i])
{
- g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]));
+ ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i];
+ g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x);
+ g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y);
+ g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta));
}
}
@@ -2335,9 +2379,10 @@ void ImGui::NewFrame()
IM_ASSERT(g.MovingWindow->MoveId == g.MovingWindowMoveId);
if (g.IO.MouseDown[0])
{
- g.MovingWindow->RootWindow->PosFloat += g.IO.MouseDelta;
- if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
+ ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
+ if (g.MovingWindow->RootWindow->PosFloat.x != pos.x || g.MovingWindow->RootWindow->PosFloat.y != pos.y)
MarkIniSettingsDirty(g.MovingWindow->RootWindow);
+ g.MovingWindow->RootWindow->PosFloat = pos;
FocusWindow(g.MovingWindow);
}
else
@@ -2368,13 +2413,11 @@ void ImGui::NewFrame()
g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(g.IO.MousePos);
g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
- if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
+ ImGuiWindow* modal_window = GetFrontMostModalRootWindow();
+ if (modal_window != NULL)
{
g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f);
- ImGuiWindow* window = g.HoveredRootWindow;
- while (window && window != modal_window)
- window = window->ParentWindow;
- if (!window)
+ if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
g.HoveredRootWindow = g.HoveredWindow = NULL;
}
else
@@ -2400,15 +2443,19 @@ void ImGui::NewFrame()
g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
else
g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
- g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != -1) ? (g.WantCaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
+ if (g.WantCaptureKeyboardNextFrame != -1)
+ g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
+ else
+ g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0;
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
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
- // FIXME: For patterns of drag and drop between "application" and "imgui" we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
- if (!mouse_avail_to_imgui)
+ // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
+ bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
+ if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
g.HoveredWindow = g.HoveredRootWindow = NULL;
// Scale & Scrolling
@@ -2428,11 +2475,20 @@ void ImGui::NewFrame()
window->Size *= scale;
window->SizeFull *= scale;
}
- else if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse))
+ else if (!g.IO.KeyCtrl)
{
- // Scroll
- const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5;
- SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines);
+ // Mouse wheel Scrolling
+ // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set).
+ ImGuiWindow* scroll_window = window;
+ while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow)
+ scroll_window = scroll_window->ParentWindow;
+
+ if (!(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs))
+ {
+ float scroll_amount = 5 * scroll_window->CalcFontSize();
+ scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
+ SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
+ }
}
}
@@ -2446,7 +2502,7 @@ void ImGui::NewFrame()
ImGuiWindow* window = g.Windows[i];
window->WasActive = window->Active;
window->Active = false;
- window->Accessed = false;
+ window->WriteAccessed = false;
}
// Closing the focused window restore focus to the first active root window in descending z-order
@@ -2461,17 +2517,79 @@ void ImGui::NewFrame()
// Create implicit window - we will only render it if the user has added something to it.
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
- ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
- ImGui::Begin("Debug##Default");
+ SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
+ Begin("Debug##Default");
+}
+
+static void* SettingsHandlerWindow_ReadOpen(ImGuiContext&, const char* name)
+{
+ ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
+ if (!settings)
+ settings = AddWindowSettings(name);
+ return (void*)settings;
+}
+
+static void SettingsHandlerWindow_ReadLine(ImGuiContext&, void* entry, const char* line)
+{
+ ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
+ float x, y;
+ int i;
+ if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y);
+ else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
+ else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
+}
+
+static void SettingsHandlerWindow_WriteAll(ImGuiContext& g, ImGuiTextBuffer* buf)
+{
+ // Gather data from windows that were active during this session
+ for (int i = 0; i != g.Windows.Size; i++)
+ {
+ ImGuiWindow* window = g.Windows[i];
+ if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
+ continue;
+ ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID);
+ if (!settings)
+ settings = AddWindowSettings(window->Name);
+ settings->Pos = window->Pos;
+ settings->Size = window->SizeFull;
+ settings->Collapsed = window->Collapsed;
+ }
+
+ // Write a buffer
+ // If a window wasn't opened in this session we preserve its settings
+ buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
+ for (int i = 0; i != g.SettingsWindows.Size; i++)
+ {
+ const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
+ if (settings->Pos.x == FLT_MAX)
+ continue;
+ const char* name = settings->Name;
+ if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
+ name = p;
+ buf->appendf("[Window][%s]\n", name);
+ buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
+ buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
+ buf->appendf("Collapsed=%d\n", settings->Collapsed);
+ buf->appendf("\n");
+ }
}
void ImGui::Initialize()
{
ImGuiContext& g = *GImGui;
- g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
- IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
+ g.LogClipboard = IM_NEW(ImGuiTextBuffer)();
- IM_ASSERT(g.Settings.empty());
+ // Add .ini handle for ImGuiWindow type
+ ImGuiSettingsHandler ini_handler;
+ ini_handler.TypeName = "Window";
+ ini_handler.TypeHash = ImHash("Window", 0, 0);
+ ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
+ ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
+ ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
+ g.SettingsHandlers.push_front(ini_handler);
+
+ // Load .ini file
+ IM_ASSERT(g.SettingsWindows.empty());
LoadIniSettingsFromDisk(g.IO.IniFilename);
g.Initialized = true;
}
@@ -2492,22 +2610,19 @@ void ImGui::Shutdown()
SaveIniSettingsToDisk(g.IO.IniFilename);
for (int i = 0; i < g.Windows.Size; i++)
- {
- g.Windows[i]->~ImGuiWindow();
- ImGui::MemFree(g.Windows[i]);
- }
+ IM_DELETE(g.Windows[i]);
g.Windows.clear();
g.WindowsSortBuffer.clear();
g.CurrentWindow = NULL;
g.CurrentWindowStack.clear();
+ g.WindowsById.Clear();
g.NavWindow = NULL;
g.HoveredWindow = NULL;
g.HoveredRootWindow = NULL;
g.ActiveIdWindow = NULL;
g.MovingWindow = NULL;
- for (int i = 0; i < g.Settings.Size; i++)
- ImGui::MemFree(g.Settings[i].Name);
- g.Settings.clear();
+ for (int i = 0; i < g.SettingsWindows.Size; i++)
+ IM_DELETE(g.SettingsWindows[i].Name);
g.ColorModifiers.clear();
g.StyleModifiers.clear();
g.FontStack.clear();
@@ -2523,90 +2638,110 @@ void ImGui::Shutdown()
g.InputTextState.InitialText.clear();
g.InputTextState.TempTextBuffer.clear();
+ g.SettingsWindows.clear();
+ g.SettingsHandlers.clear();
+
if (g.LogFile && g.LogFile != stdout)
{
fclose(g.LogFile);
g.LogFile = NULL;
}
if (g.LogClipboard)
- {
- g.LogClipboard->~ImGuiTextBuffer();
- ImGui::MemFree(g.LogClipboard);
- }
+ IM_DELETE(g.LogClipboard);
g.Initialized = false;
}
-static ImGuiIniData* FindWindowSettings(const char* name)
+ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
{
ImGuiContext& g = *GImGui;
- ImGuiID id = ImHash(name, 0);
- for (int i = 0; i != g.Settings.Size; i++)
- {
- ImGuiIniData* ini = &g.Settings[i];
- if (ini->Id == id)
- return ini;
- }
+ for (int i = 0; i != g.SettingsWindows.Size; i++)
+ if (g.SettingsWindows[i].Id == id)
+ return &g.SettingsWindows[i];
return NULL;
}
-static ImGuiIniData* AddWindowSettings(const char* name)
+static ImGuiWindowSettings* AddWindowSettings(const char* name)
{
- GImGui->Settings.resize(GImGui->Settings.Size + 1);
- ImGuiIniData* ini = &GImGui->Settings.back();
- ini->Name = ImStrdup(name);
- ini->Id = ImHash(name, 0);
- ini->Collapsed = false;
- ini->Pos = ImVec2(FLT_MAX,FLT_MAX);
- ini->Size = ImVec2(0,0);
- return ini;
+ ImGuiContext& g = *GImGui;
+ g.SettingsWindows.push_back(ImGuiWindowSettings());
+ ImGuiWindowSettings* settings = &g.SettingsWindows.back();
+ settings->Name = ImStrdup(name);
+ settings->Id = ImHash(name, 0);
+ return settings;
}
-// Zero-tolerance, poor-man .ini parsing
-// FIXME: Write something less rubbish
static void LoadIniSettingsFromDisk(const char* ini_filename)
{
- ImGuiContext& g = *GImGui;
if (!ini_filename)
return;
-
- int file_size;
- char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_size, 1);
+ char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", NULL, +1);
if (!file_data)
return;
+ LoadIniSettingsFromMemory(file_data);
+ ImGui::MemFree(file_data);
+}
+
+ImGuiSettingsHandler* ImGui::FindSettingsHandler(ImGuiID type_hash)
+{
+ ImGuiContext& g = *GImGui;
+ for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
+ if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
+ return &g.SettingsHandlers[handler_n];
+ return NULL;
+}
+
+// Zero-tolerance, no error reporting, cheap .ini parsing
+static void LoadIniSettingsFromMemory(const char* buf_readonly)
+{
+ // 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;
+ void* entry_data = NULL;
+ const ImGuiSettingsHandler* entry_handler = NULL;
- ImGuiIniData* settings = NULL;
- const char* buf_end = file_data + file_size;
- for (const char* line_start = file_data; line_start < buf_end; )
+ char* line_end = NULL;
+ for (char* line = buf; line < buf_end; line = line_end + 1)
{
- const char* line_end = line_start;
+ // Skip new lines markers, then find end of the line
+ while (*line == '\n' || *line == '\r')
+ line++;
+ line_end = line;
while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
line_end++;
+ line_end[0] = 0;
- if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']')
+ if (line[0] == '[' && line_end > line && line_end[-1] == ']')
{
- char name[64];
- ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1);
- settings = FindWindowSettings(name);
- if (!settings)
- settings = AddWindowSettings(name);
+ // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
+ line_end[-1] = 0;
+ const char* name_end = line_end - 1;
+ const char* type_start = line + 1;
+ char* type_end = ImStrchrRange(type_start, name_end, ']');
+ const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
+ if (!type_end || !name_start)
+ {
+ name_start = type_start; // Import legacy entries that have no type
+ type_start = "Window";
+ }
+ else
+ {
+ *type_end = 0; // Overwrite first ']'
+ name_start++; // Skip second '['
+ }
+ const ImGuiID type_hash = ImHash(type_start, 0, 0);
+ entry_handler = ImGui::FindSettingsHandler(type_hash);
+ entry_data = entry_handler ? entry_handler->ReadOpenFn(g, name_start) : NULL;
}
- else if (settings)
+ else if (entry_handler != NULL && entry_data != NULL)
{
- float x, y;
- int i;
- if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2)
- settings->Pos = ImVec2(x, y);
- else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2)
- settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize);
- else if (sscanf(line_start, "Collapsed=%d", &i) == 1)
- settings->Collapsed = (i != 0);
+ // Let type handler parse the line
+ entry_handler->ReadLineFn(g, entry_data, line);
}
-
- line_start = line_end+1;
}
-
- ImGui::MemFree(file_data);
+ ImGui::MemFree(buf);
}
static void SaveIniSettingsToDisk(const char* ini_filename)
@@ -2616,43 +2751,36 @@ static void SaveIniSettingsToDisk(const char* ini_filename)
if (!ini_filename)
return;
- // Gather data from windows that were active during this session
- for (int i = 0; i != g.Windows.Size; i++)
- {
- ImGuiWindow* window = g.Windows[i];
- if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
- continue;
- ImGuiIniData* settings = FindWindowSettings(window->Name);
- if (!settings) // This will only return NULL in the rare instance where the window was first created with ImGuiWindowFlags_NoSavedSettings then had the flag disabled later on. We don't bind settings in this case (bug #1000).
- continue;
- settings->Pos = window->Pos;
- settings->Size = window->SizeFull;
- settings->Collapsed = window->Collapsed;
- }
+ ImVector<char> buf;
+ SaveIniSettingsToMemory(buf);
- // Write .ini file
- // If a window wasn't opened in this session we preserve its settings
FILE* f = ImFileOpen(ini_filename, "wt");
if (!f)
return;
- for (int i = 0; i != g.Settings.Size; i++)
- {
- const ImGuiIniData* settings = &g.Settings[i];
- if (settings->Pos.x == FLT_MAX)
- continue;
- const char* name = settings->Name;
- if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
- name = p;
- fprintf(f, "[%s]\n", name);
- fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
- fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
- fprintf(f, "Collapsed=%d\n", settings->Collapsed);
- fprintf(f, "\n");
- }
-
+ fwrite(buf.Data, sizeof(char), (size_t)buf.Size, f);
fclose(f);
}
+static void SaveIniSettingsToMemory(ImVector<char>& out_buf)
+{
+ ImGuiContext& g = *GImGui;
+ g.SettingsDirtyTimer = 0.0f;
+
+ ImGuiTextBuffer buf;
+ for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
+ g.SettingsHandlers[handler_n].WriteAllFn(g, &buf);
+
+ buf.Buf.pop_back(); // Remove extra zero-terminator used by ImGuiTextBuffer
+ out_buf.swap(buf.Buf);
+}
+
+void ImGui::MarkIniSettingsDirty()
+{
+ ImGuiContext& g = *GImGui;
+ if (g.SettingsDirtyTimer <= 0.0f)
+ g.SettingsDirtyTimer = g.IO.IniSavingRate;
+}
+
static void MarkIniSettingsDirty(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
@@ -2670,9 +2798,7 @@ static int ChildWindowComparer(const void* lhs, const void* rhs)
return d;
if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
return d;
- if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox))
- return d;
- return (a->OrderWithinParent - b->OrderWithinParent);
+ return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
}
static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>& out_sorted_windows, ImGuiWindow* window)
@@ -2711,15 +2837,17 @@ static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDr
IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
- // Check that draw_list doesn't use more vertices than indexable in a single draw call (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per window)
- // If this assert triggers because you are drawing lots of stuff manually, you can:
- // A) Add '#define ImDrawIdx unsigned int' in imconfig.h to set the index size to 4 bytes. You'll need to handle the 4-bytes indices to your renderer.
- // For example, the OpenGL example code detect index size at compile-time by doing:
- // 'glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);'
+ // Check that draw_list doesn't use more vertices than indexable in a single draw call (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
+ // If this assert triggers because you are drawing lots of stuff manually:
+ // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use thre Metrics window to inspect draw list contents.
+ // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes.
+ // You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing:
+ // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
// Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
- // B) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
- IM_ASSERT(((ImU64)draw_list->_VtxCurrentIdx >> (sizeof(ImDrawIdx)*8)) == 0); // Too many vertices in same ImDrawList. See comment above.
-
+ // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
+ 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);
GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size;
GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size;
@@ -2772,7 +2900,8 @@ void ImGui::EndFrame()
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame()
- IM_ASSERT(g.FrameCountEnded != g.FrameCount); // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again
+ if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
+ 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)
@@ -2783,7 +2912,7 @@ void ImGui::EndFrame()
// Hide implicit "Debug" window if it hasn't been used
IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls
- if (g.CurrentWindow && !g.CurrentWindow->Accessed)
+ if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
g.CurrentWindow->Active = false;
ImGui::End();
@@ -2802,6 +2931,7 @@ void ImGui::EndFrame()
g.MovingWindow = g.HoveredWindow;
g.MovingWindowMoveId = g.MovingWindow->MoveId;
SetActiveID(g.MovingWindowMoveId, g.HoveredRootWindow);
+ g.ActiveIdClickOffset = g.IO.MousePos - g.MovingWindow->RootWindow->Pos;
}
}
else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL)
@@ -2951,7 +3081,7 @@ void ImGui::LogText(const char* fmt, ...)
}
else
{
- g.LogClipboard->appendv(fmt, args);
+ g.LogClipboard->appendfv(fmt, args);
}
va_end(args);
}
@@ -3101,10 +3231,11 @@ void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border,
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
- if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
+ const float border_size = g.Style.FrameBorderSize;
+ if (border && border_size > 0.0f)
{
- window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
- window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding);
+ window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
+ window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
}
}
@@ -3112,10 +3243,11 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- if (window->Flags & ImGuiWindowFlags_ShowBorders)
+ const float border_size = g.Style.FrameBorderSize;
+ if (border_size > 0.0f)
{
- window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
- window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding);
+ window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
+ window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
}
}
@@ -3133,21 +3265,23 @@ void ImGui::RenderTriangle(ImVec2 p_min, ImGuiDir dir, float scale)
switch (dir)
{
case ImGuiDir_Up:
- r = -r; // ...fall through, no break!
case ImGuiDir_Down:
+ if (dir == ImGuiDir_Up) r = -r;
center.y -= r * 0.25f;
a = ImVec2(0,1) * r;
b = ImVec2(-0.866f,-0.5f) * r;
c = ImVec2(+0.866f,-0.5f) * r;
break;
case ImGuiDir_Left:
- r = -r; // ...fall through, no break!
case ImGuiDir_Right:
+ if (dir == ImGuiDir_Left) r = -r;
+ center.x -= r * 0.25f;
a = ImVec2(1,0) * r;
b = ImVec2(-0.500f,+0.866f) * r;
c = ImVec2(-0.500f,-0.866f) * r;
break;
- default:
+ case ImGuiDir_None:
+ case ImGuiDir_Count_:
IM_ASSERT(0);
break;
}
@@ -3242,7 +3376,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
static ImGuiWindow* FindHoveredWindow(ImVec2 pos)
{
ImGuiContext& g = *GImGui;
- for (int i = g.Windows.Size-1; i >= 0; i--)
+ for (int i = g.Windows.Size - 1; i >= 0; i--)
{
ImGuiWindow* window = g.Windows[i];
if (!window->Active)
@@ -3282,6 +3416,12 @@ bool ImGui::IsAnyWindowHovered()
return g.HoveredWindow != NULL;
}
+bool ImGui::IsAnyWindowFocused()
+{
+ ImGuiContext& g = *GImGui;
+ return g.NavWindow != NULL;
+}
+
static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
{
const int key_index = GImGui->IO.KeyMap[key];
@@ -3475,7 +3615,8 @@ bool ImGui::IsItemClicked(int mouse_button)
bool ImGui::IsAnyItemHovered()
{
- return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0;
+ ImGuiContext& g = *GImGui;
+ return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
}
bool ImGui::IsAnyItemActive()
@@ -3538,17 +3679,17 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_
{
ImGuiContext& g = *GImGui;
char window_name[16];
- ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", g.TooltipOverrideCount);
+ ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
if (override_previous_tooltip)
- if (ImGuiWindow* window = ImGui::FindWindowByName(window_name))
+ if (ImGuiWindow* window = FindWindowByName(window_name))
if (window->Active)
{
// Hide previous tooltips. We can't easily "reset" the content of a window so we create a new one.
window->HiddenFrames = 1;
- ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", ++g.TooltipOverrideCount);
+ ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
}
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
- ImGui::Begin(window_name, NULL, flags | extra_flags);
+ Begin(window_name, NULL, flags | extra_flags);
}
void ImGui::SetTooltipV(const char* fmt, va_list args)
@@ -3584,9 +3725,9 @@ void ImGui::EndTooltip()
void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
+ ImGuiWindow* parent_window = g.CurrentWindow;
int current_stack_size = g.CurrentPopupStack.Size;
- ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here)
+ ImGuiPopupRef popup_ref = ImGuiPopupRef(id, parent_window, parent_window->GetID("##Menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL.
if (g.OpenPopupStack.Size < current_stack_size + 1)
g.OpenPopupStack.push_back(popup_ref);
else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id)
@@ -3597,7 +3738,7 @@ void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
// When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by CloseInactivePopups().
// This is equivalent to what ClosePopupToLevel() does.
if (g.OpenPopupStack[current_stack_size].PopupId == id)
- FocusWindow(window);
+ FocusWindow(parent_window);
}
}
@@ -3690,26 +3831,20 @@ static inline void ClearSetNextWindowData()
bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
if (!IsPopupOpen(id))
{
ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
return false;
}
- PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
- ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings;
-
char name[20];
- if (flags & ImGuiWindowFlags_ChildMenu)
- ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size); // Recycle windows based on depth
+ if (extra_flags & ImGuiWindowFlags_ChildMenu)
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
else
- ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
- bool is_open = Begin(name, NULL, flags);
- if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
- g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders;
- if (!is_open) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
+ bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
+ if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
EndPopup();
return is_open;
@@ -3723,7 +3858,7 @@ bool ImGui::BeginPopup(const char* str_id)
ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
return false;
}
- return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
+ return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
}
bool ImGui::IsPopupOpen(ImGuiID id)
@@ -3768,12 +3903,10 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags ext
void ImGui::EndPopup()
{
- ImGuiWindow* window = GetCurrentWindow();
- IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
- IM_ASSERT(GImGui->CurrentPopupStack.Size > 0);
+ ImGuiContext& g = *GImGui; (void)g;
+ IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
+ IM_ASSERT(g.CurrentPopupStack.Size > 0);
End();
- if (!(window->Flags & ImGuiWindowFlags_Modal))
- PopStyleVar();
}
bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
@@ -3797,9 +3930,10 @@ 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)
- if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
- OpenPopupEx(id, true);
- return BeginPopupEx(id, ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
+ if (IsMouseClicked(mouse_button))
+ if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
+ OpenPopupEx(id, true);
+ return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
}
bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
@@ -3811,7 +3945,7 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool a
if (IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
if (also_over_items || !IsAnyItemHovered())
OpenPopupEx(id, true);
- return BeginPopupEx(id, ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
+ return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
}
bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
@@ -3821,24 +3955,27 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button))
OpenPopupEx(id, true);
- return BeginPopupEx(id, ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
+ return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
}
static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
{
+ ImGuiContext& g = *GImGui;
ImGuiWindow* parent_window = ImGui::GetCurrentWindow();
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
const ImVec2 content_avail = ImGui::GetContentRegionAvail();
ImVec2 size = ImFloor(size_arg);
- const int auto_fit_axises = ((size.x == 0.0f) ? 0x01 : 0x00) | ((size.y == 0.0f) ? 0x02 : 0x00);
+ const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
if (size.x <= 0.0f)
- size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues)
+ size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
if (size.y <= 0.0f)
- size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y);
- if (border)
- flags |= ImGuiWindowFlags_ShowBorders;
+ size.y = ImMax(content_avail.y + size.y, 4.0f);
+
+ const float backup_border_size = g.Style.ChildBorderSize;
+ if (!border)
+ g.Style.ChildBorderSize = 0.0f;
flags |= extra_flags;
char title[256];
@@ -3851,8 +3988,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
bool ret = ImGui::Begin(title, NULL, flags);
ImGuiWindow* child_window = ImGui::GetCurrentWindow();
child_window->AutoFitChildAxises = auto_fit_axises;
- if (!(parent_window->Flags & ImGuiWindowFlags_ShowBorders))
- child_window->Flags &= ~ImGuiWindowFlags_ShowBorders;
+ g.Style.ChildBorderSize = backup_border_size;
return ret;
}
@@ -3873,7 +4009,7 @@ void ImGui::EndChild()
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
- if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1)
+ if (window->BeginCount > 1)
{
ImGui::End();
}
@@ -3881,9 +4017,9 @@ void ImGui::EndChild()
{
// 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();
- if (window->AutoFitChildAxises & 0x01) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
+ 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 & 0x02)
+ if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
sz.y = ImMax(4.0f, sz.y);
ImGui::End();
@@ -3899,16 +4035,17 @@ bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags ext
{
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
- PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
+ PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
+ PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
+ PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
- return BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
+ return BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
}
void ImGui::EndChildFrame()
{
EndChild();
- PopStyleVar(2);
+ PopStyleVar(3);
PopStyleColor();
}
@@ -3927,43 +4064,84 @@ static void CheckStacksSize(ImGuiWindow* window, bool write)
IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
}
-static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner)
+enum ImGuiPopupPositionPolicy
+{
+ ImGuiPopupPositionPolicy_Default,
+ ImGuiPopupPositionPolicy_ComboBox
+};
+
+static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default)
{
const ImGuiStyle& style = GImGui->Style;
- // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it.
+ // 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(GetVisibleRect());
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(base_pos, r_outer.Min, r_outer.Max - size);
+ 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));
- for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Last, Right, down, up, left. (Favor last used direction).
+ // Combo Box policy (we want a connecting edge)
+ if (policy == ImGuiPopupPositionPolicy_ComboBox)
{
- const int dir = (n == -1) ? *last_dir : n;
- ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y);
- if (rect.GetWidth() < size.x || rect.GetHeight() < size.y)
+ const ImGuiDir dir_prefered_order[ImGuiDir_Count_] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
+ for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_Count_; n++)
+ {
+ const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
+ if (n != -1 && dir == *last_dir) // Already tried this direction?
+ continue;
+ ImVec2 pos;
+ if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
+ if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
+ if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
+ if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
+ if (!r_outer.Contains(ImRect(pos, pos + size)))
+ continue;
+ *last_dir = dir;
+ return pos;
+ }
+ }
+
+ // Default popup policy
+ const ImGuiDir dir_prefered_order[ImGuiDir_Count_] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
+ for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_Count_; n++)
+ {
+ const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
+ if (n != -1 && dir == *last_dir) // Already tried this direction?
+ continue;
+ float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
+ float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
+ if (avail_w < size.x || avail_h < size.y)
continue;
+ ImVec2 pos;
+ pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
+ pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
*last_dir = dir;
- return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y);
+ return pos;
}
// Fallback, try to keep within display
- *last_dir = -1;
- ImVec2 pos = base_pos;
+ *last_dir = ImGuiDir_None;
+ ImVec2 pos = ref_pos;
pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
return pos;
}
+static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
+{
+ window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
+ window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
+ window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
+}
+
ImGuiWindow* ImGui::FindWindowByName(const char* name)
{
- // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block
ImGuiContext& g = *GImGui;
ImGuiID id = ImHash(name, 0);
- for (int i = 0; i < g.Windows.Size; i++)
- if (g.Windows[i]->ID == id)
- return g.Windows[i];
- return NULL;
+ return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
}
static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
@@ -3971,45 +4149,29 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
ImGuiContext& g = *GImGui;
// Create window the first time
- ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow));
- IM_PLACEMENT_NEW(window) ImGuiWindow(name);
+ ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
window->Flags = flags;
+ g.WindowsById.SetVoidPtr(window->ID, window);
- if (flags & ImGuiWindowFlags_NoSavedSettings)
- {
- // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
- window->Size = window->SizeFull = size;
- }
- else
+ // 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->PosFloat = ImVec2(60, 60);
window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
- ImGuiIniData* settings = FindWindowSettings(name);
- if (!settings)
- {
- settings = AddWindowSettings(name);
- }
- else
- {
- window->SetWindowPosAllowFlags &= ~ImGuiCond_FirstUseEver;
- window->SetWindowSizeAllowFlags &= ~ImGuiCond_FirstUseEver;
- window->SetWindowCollapsedAllowFlags &= ~ImGuiCond_FirstUseEver;
- }
-
- if (settings->Pos.x != FLT_MAX)
+ if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
{
+ SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
window->PosFloat = settings->Pos;
window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y);
window->Collapsed = settings->Collapsed;
+ if (ImLengthSqr(settings->Size) > 0.00001f)
+ size = settings->Size;
}
-
- if (ImLengthSqr(settings->Size) > 0.00001f)
- size = settings->Size;
- window->Size = window->SizeFull = size;
}
+ window->Size = window->SizeFull = window->SizeFullAtLastBegin = size;
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
{
@@ -4032,7 +4194,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
return window;
}
-static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
+static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
{
ImGuiContext& g = *GImGui;
if (g.SetNextWindowSizeConstraint)
@@ -4052,12 +4214,25 @@ static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
new_size = data.DesiredSize;
}
}
+
+ // Minimum size
if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
+ {
new_size = ImMax(new_size, g.Style.WindowMinSize);
+ new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
+ }
return new_size;
}
-static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
+static ImVec2 CalcSizeContents(ImGuiWindow* window)
+{
+ ImVec2 sz;
+ sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
+ sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
+ return sz + window->WindowPadding;
+}
+
+static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
{
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
@@ -4066,22 +4241,31 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
if ((flags & ImGuiWindowFlags_Tooltip) != 0)
{
// Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
- size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
+ size_auto_fit = size_contents;
}
else
{
- // Handling case of auto fit window not fitting on the screen (on either axis): 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(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
- ImVec2 size_auto_fit_after_constraint = CalcSizeFullWithConstraint(window, size_auto_fit);
- if (size_auto_fit_after_constraint.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
+ // 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_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
+ if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
size_auto_fit.y += style.ScrollbarSize;
- if (size_auto_fit_after_constraint.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
- size_auto_fit.x += style.ScrollbarSize * 2.0f;
- size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
+ if (size_auto_fit_after_constraint.y < size_contents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
+ size_auto_fit.x += style.ScrollbarSize;
}
return size_auto_fit;
}
+static float GetScrollMaxX(ImGuiWindow* window)
+{
+ return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
+}
+
+static float GetScrollMaxY(ImGuiWindow* window)
+{
+ return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
+}
+
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
{
ImVec2 scroll = window->Scroll;
@@ -4094,23 +4278,62 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
if (!window->Collapsed && !window->SkipItems)
{
- scroll.x = ImMin(scroll.x, ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x))); // == GetScrollMaxX for that window
- scroll.y = ImMin(scroll.y, ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y))); // == GetScrollMaxY for that window
+ scroll.x = ImMin(scroll.x, GetScrollMaxX(window));
+ scroll.y = ImMin(scroll.y, GetScrollMaxY(window));
}
return scroll;
}
static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
{
- if (flags & ImGuiWindowFlags_ComboBox)
- return ImGuiCol_ComboBg;
if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
return ImGuiCol_PopupBg;
if (flags & ImGuiWindowFlags_ChildWindow)
- return ImGuiCol_ChildWindowBg;
+ return ImGuiCol_ChildBg;
return ImGuiCol_WindowBg;
}
+static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
+{
+ ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
+ ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
+ ImVec2 size_expected = pos_max - pos_min;
+ ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
+ *out_pos = pos_min;
+ if (corner_norm.x == 0.0f)
+ out_pos->x -= (size_constrained.x - size_expected.x);
+ if (corner_norm.y == 0.0f)
+ out_pos->y -= (size_constrained.y - size_expected.y);
+ *out_size = size_constrained;
+}
+
+struct ImGuiResizeGripDef
+{
+ ImVec2 CornerPos;
+ ImVec2 InnerDir;
+ int AngleMin12, AngleMax12;
+};
+
+const ImGuiResizeGripDef resize_grip_def[4] =
+{
+ { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
+ { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
+ { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
+ { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
+};
+
+static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
+{
+ ImRect rect = window->Rect();
+ if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
+ if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness);
+ if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding);
+ if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y);
+ if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding);
+ IM_ASSERT(0);
+ return ImRect();
+}
+
// Push a new ImGui window to add widgets to.
// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
// - Begin/End can be called multiple times during the frame with the same window name to append content.
@@ -4130,13 +4353,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
// Find or create
- bool window_is_new = false;
ImGuiWindow* window = FindWindowByName(name);
if (!window)
{
ImVec2 size_on_first_use = (g.SetNextWindowSizeCond != 0) ? g.SetNextWindowSizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
window = CreateNewWindow(name, size_on_first_use, flags);
- window_is_new = true;
}
const int current_frame = g.FrameCount;
@@ -4146,33 +4367,42 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
else
flags = window->Flags;
+ // Update the Appearing flag
+ bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
+ const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1);
+ if (flags & ImGuiWindowFlags_Popup)
+ {
+ ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
+ window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
+ window_just_activated_by_user |= (window != popup_ref.Window);
+ }
+ window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
+ window->CloseButton = (p_open != NULL);
+ if (window->Appearing)
+ SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
+
+ // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
+ ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
+ ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
+ IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
+
// Add to stack
- ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL;
g.CurrentWindowStack.push_back(window);
SetCurrentWindow(window);
CheckStacksSize(window, true);
- IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
-
- bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
if (flags & ImGuiWindowFlags_Popup)
{
ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
- window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
- window_just_activated_by_user |= (window != popup_ref.Window);
popup_ref.Window = window;
g.CurrentPopupStack.push_back(popup_ref);
window->PopupId = popup_ref.PopupId;
}
- const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1);
- window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
-
// Process SetNextWindow***() calls
- bool window_pos_set_by_api = false, window_size_set_by_api = false;
+ bool window_pos_set_by_api = false;
+ bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
if (g.SetNextWindowPosCond)
{
- if (window->Appearing)
- window->SetWindowPosAllowFlags |= ImGuiCond_Appearing;
window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosPivot) > 0.00001f)
{
@@ -4190,15 +4420,16 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
if (g.SetNextWindowSizeCond)
{
- if (window->Appearing)
- window->SetWindowSizeAllowFlags |= ImGuiCond_Appearing;
- window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
+ window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0 && (g.SetNextWindowSizeVal.x > 0.0f);
+ window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0 && (g.SetNextWindowSizeVal.y > 0.0f);
SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
g.SetNextWindowSizeCond = 0;
}
if (g.SetNextWindowContentSizeCond)
{
+ // Adjust passed "client size" to become a "window size"
window->SizeContentsExplicit = g.SetNextWindowContentSizeVal;
+ window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
g.SetNextWindowContentSizeCond = 0;
}
else if (first_begin_of_the_frame)
@@ -4207,8 +4438,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
if (g.SetNextWindowCollapsedCond)
{
- if (window->Appearing)
- window->SetWindowCollapsedAllowFlags |= ImGuiCond_Appearing;
SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
g.SetNextWindowCollapsedCond = 0;
}
@@ -4217,34 +4446,37 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
SetWindowFocus();
g.SetNextWindowFocus = false;
}
-
- // Update known root window (if we are a child window, otherwise window == window->RootWindow)
- int root_idx, root_non_popup_idx;
- for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--)
- if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow))
- break;
- for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--)
- if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) || (g.CurrentWindowStack[root_non_popup_idx]->Flags & ImGuiWindowFlags_Modal))
- break;
- window->ParentWindow = parent_window;
- window->RootWindow = g.CurrentWindowStack[root_idx];
- window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing
+ if (window->Appearing)
+ SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
// When reusing window again multiple times a frame, just append content (don't need to setup again)
if (first_begin_of_the_frame)
{
+ // Initialize
+ window->ParentWindow = parent_window;
+ window->RootWindow = window->RootNonPopupWindow = window;
+ if (parent_window && (flags & ImGuiWindowFlags_ChildWindow))
+ window->RootWindow = parent_window->RootWindow;
+ if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
+ window->RootNonPopupWindow = parent_window->RootNonPopupWindow;
+ //window->RootNavWindow = window;
+ //while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened)
+ // window->RootNavWindow = window->RootNavWindow->ParentWindow;
+
window->Active = true;
- window->OrderWithinParent = 0;
+ window->BeginOrderWithinParent = 0;
+ window->BeginOrderWithinContext = g.WindowsActiveCount++;
window->BeginCount = 0;
window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
window->LastFrameActive = current_frame;
window->IDStack.resize(1);
- // Clear draw list, setup texture, outer clipping rectangle
+ // Setup draw list and outer clipping rectangle
window->DrawList->Clear();
+ window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
ImRect fullscreen_rect(GetVisibleRect());
- if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup)))
+ if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
else
PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
@@ -4252,7 +4484,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (window_just_activated_by_user)
{
// Popup first latch mouse position, will position itself when it appears next frame
- window->AutoPosLastDirection = -1;
+ window->AutoPosLastDirection = ImGuiDir_None;
if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
window->PosFloat = g.IO.MousePos;
}
@@ -4276,11 +4508,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// SIZE
- // Save contents size from last frame for auto-fitting (unless explicitly specified)
- window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x));
- window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y));
+ // Update contents size from last frame for auto-fitting (unless explicitly specified)
+ window->SizeContents = CalcSizeContents(window);
- // Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
+ // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
if (window->HiddenFrames > 0)
window->HiddenFrames--;
if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user)
@@ -4288,61 +4519,80 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->HiddenFrames = 1;
if (flags & ImGuiWindowFlags_AlwaysAutoResize)
{
- if (!window_size_set_by_api)
- window->Size = window->SizeFull = ImVec2(0.f, 0.f);
+ 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);
}
}
- // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects.
- window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding;
+ // 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;
+ window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(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);
+ const float window_rounding = window->WindowRounding;
+ const float window_border_size = window->WindowBorderSize;
// Calculate auto-fit size, handle automatic resize
- const ImVec2 size_auto_fit = CalcSizeAutoFit(window);
- if (window->Collapsed)
+ const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
+ ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
+ if (flags & ImGuiWindowFlags_AlwaysAutoResize && !window->Collapsed)
{
- // We still process initial auto-fit on collapsed windows to get a window width,
- // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
- if (window->AutoFitFramesX > 0)
- window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
- if (window->AutoFitFramesY > 0)
- window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
+ // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
+ if (!window_size_x_set_by_api)
+ window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
+ if (!window_size_y_set_by_api)
+ window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
}
- else if (!window_size_set_by_api)
+ else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
{
- if (flags & ImGuiWindowFlags_AlwaysAutoResize)
- {
- window->SizeFull = size_auto_fit;
- }
- else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
- {
- // Auto-fit only grows during the first few frames
- if (window->AutoFitFramesX > 0)
- window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
- if (window->AutoFitFramesY > 0)
- window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
+ // Auto-fit only grows during the first few frames
+ // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
+ if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
+ window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
+ if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
+ window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
+ if (!window->Collapsed)
MarkIniSettingsDirty(window);
- }
}
// Apply minimum/maximum window size constraints and final size
- window->SizeFull = CalcSizeFullWithConstraint(window, window->SizeFull);
+ window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
-
+ if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
+ {
+ IM_ASSERT(window_size_x_set_by_api && window_size_y_set_by_api); // Submitted by BeginChild()
+ window->Size = window->SizeFull;
+ }
+
+ // SCROLLBAR STATUS
+
+ // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
+ if (!window->Collapsed)
+ {
+ // When reading the current size we need to read it after size constraints have been applied
+ float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
+ float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
+ window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
+ window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
+ if (window->ScrollbarX && !window->ScrollbarY)
+ window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars + style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
+ window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
+ }
+
// POSITION
// Position child window
if (flags & ImGuiWindowFlags_ChildWindow)
{
- window->OrderWithinParent = parent_window->DC.ChildWindows.Size;
+ window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
parent_window->DC.ChildWindows.push_back(window);
}
- if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
- {
- IM_ASSERT(window_size_set_by_api); // Submitted by BeginChild()
+ if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api)
window->Pos = window->PosFloat = parent_window->DC.CursorPos;
- window->Size = window->SizeFull;
- }
const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0);
if (window_pos_with_pivot)
@@ -4356,26 +4606,27 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// 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_window->DC.MenuBarAppending)
- rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
+ 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_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
- window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
+ 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);
}
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 = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
+ 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)
{
ImVec2 ref_pos = g.IO.MousePos;
- ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
- window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
- if (window->AutoPosLastDirection == -1)
+ ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Store boxes in mouse cursor data? Scale? Center on cursor hit-point?
+ 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.
}
@@ -4407,81 +4658,130 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
- // Modal window darkens what is behind them
+ // Apply focus, new windows appears in front
+ bool want_focus = false;
+ if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
+ if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
+ want_focus = true;
+
+ // Draw modal window background (darkens what is behind them)
if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio));
// Draw window + handle manual resize
ImRect title_bar_rect = window->TitleBarRect();
- const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
if (window->Collapsed)
{
// Title bar only
+ float backup_border_size = style.FrameBorderSize;
+ g.Style.FrameBorderSize = window->WindowBorderSize;
RenderFrame(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
+ g.Style.FrameBorderSize = backup_border_size;
}
else
{
- ImU32 resize_col = 0;
- const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
+ // Handle resize for: Resize Grips, Borders, Gamepad
+ int border_held = -1;
+ ImU32 resize_grip_col[4] = { 0 };
+ const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4
+ const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0;
+
+ const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f);
+ const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f);
if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
{
- // Manual resize
- // Using the FlattenChilds button flag, we make the resize button accessible even if we are hovering over a child window
- const ImVec2 br = window->Rect().GetBR();
- const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br);
- const ImGuiID resize_id = window->GetID("#RESIZE");
- bool hovered, held;
- ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
- resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
- if (hovered || held)
- g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
+ ImVec2 pos_target(FLT_MAX, FLT_MAX);
+ ImVec2 size_target(FLT_MAX, FLT_MAX);
- ImVec2 size_target(FLT_MAX,FLT_MAX);
- if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
+ // Manual resize grips
+ PushID("#RESIZE");
+ for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
{
- // Manual auto-fit when double-clicking
- size_target = size_auto_fit;
- ClearActiveID();
+ const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
+ const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
+
+ // 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();
+ bool hovered, held;
+ ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
+ if (hovered || held)
+ g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
+
+ if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
+ {
+ // Manual auto-fit when double-clicking
+ size_target = CalcSizeAfterConstraint(window, size_auto_fit);
+ ClearActiveID();
+ }
+ else if (held)
+ {
+ // Resize from any of the four corners
+ // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
+ ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip
+ CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
+ }
+ if (resize_grip_n == 0 || held || hovered)
+ resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
}
- else if (held)
+ for (int border_n = 0; border_n < resize_border_count; border_n++)
{
- // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
- size_target = (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos;
+ const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check.
+ const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise
+ bool hovered, held;
+ ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE);
+ ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n+4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
+ if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held)
+ {
+ g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
+ if (held) border_held = border_n;
+ }
+ if (held)
+ {
+ ImVec2 border_target = window->Pos;
+ ImVec2 border_posn;
+ if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); }
+ if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); }
+ if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); }
+ if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); }
+ CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
+ }
}
+ PopID();
- if (size_target.x != FLT_MAX && size_target.y != FLT_MAX)
+ // Apply back modified position/size to window
+ if (size_target.x != FLT_MAX)
+ {
+ window->SizeFull = size_target;
+ MarkIniSettingsDirty(window);
+ }
+ if (pos_target.x != FLT_MAX)
{
- window->SizeFull = CalcSizeFullWithConstraint(window, size_target);
+ window->Pos = window->PosFloat = ImVec2((float)(int)pos_target.x, (float)(int)pos_target.y);
MarkIniSettingsDirty(window);
}
+
window->Size = window->SizeFull;
title_bar_rect = window->TitleBarRect();
}
- // Scrollbars
- window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar));
- window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
- if (window->ScrollbarX && !window->ScrollbarY)
- window->ScrollbarY = (window->SizeContents.y > window->Size.y + style.ItemSpacing.y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
- window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
- window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f;
-
// Window background, Default Alpha
ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
- window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BotLeft|ImGuiCorner_BotRight);
+ window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
// Title bar
- const bool is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow;
+ const bool window_is_focused = want_focus || (g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow);
if (!(flags & ImGuiWindowFlags_NoTitleBar))
- window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
+ window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImDrawCornerFlags_Top);
// Menu bar
if (flags & ImGuiWindowFlags_MenuBar)
{
ImRect menu_bar_rect = window->MenuBarRect();
- if (flags & ImGuiWindowFlags_ShowBorders)
- window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border));
- window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
+ menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
+ window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
+ if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
+ window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
}
// Scrollbars
@@ -4490,27 +4790,35 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (window->ScrollbarY)
Scrollbar(ImGuiLayoutType_Vertical);
- // Render resize grip
- // (after the input handling so we don't have a frame of latency)
+ // Render resize grips (after their input handling so we don't have a frame of latency)
if (!(flags & ImGuiWindowFlags_NoResize))
{
- const ImVec2 br = window->Rect().GetBR();
- window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize));
- window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size));
- window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3);
- window->DrawList->PathFillConvex(resize_col);
+ for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
+ {
+ const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
+ const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
+ window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
+ window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
+ window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
+ window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
+ }
}
// Borders
- if (flags & ImGuiWindowFlags_ShowBorders)
+ 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);
+ if (border_held != -1)
{
- window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding);
- window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding);
- if (!(flags & ImGuiWindowFlags_NoTitleBar))
- window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border));
+ ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f);
+ window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
}
+ if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
+ window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
}
+ // 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();
@@ -4518,6 +4826,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->ContentsRegionRect.Max.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.)
window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x;
window->DC.GroupOffsetX = 0.0f;
window->DC.ColumnsOffsetX = 0.0f;
@@ -4538,11 +4847,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->DC.ItemFlagsStack.resize(0);
window->DC.ItemWidthStack.resize(0);
window->DC.TextWrapPosStack.resize(0);
- window->DC.ColumnsCurrent = 0;
- window->DC.ColumnsCount = 1;
- window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
- window->DC.ColumnsStartMaxPosX = window->DC.CursorMaxPos.x;
- window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY;
+ window->DC.ColumnsSet = NULL;
window->DC.TreeDepth = 0;
window->DC.StateStorage = &window->StateStorage;
window->DC.GroupStack.resize(0);
@@ -4559,10 +4864,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (window->AutoFitFramesY > 0)
window->AutoFitFramesY--;
- // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
- if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
- if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
- FocusWindow(window);
+ // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
+ if (want_focus)
+ FocusWindow(window);
// Title bar
if (!(flags & ImGuiWindowFlags_NoTitleBar))
@@ -4613,26 +4917,31 @@ 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->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight();
- window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
- window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
+ 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;
//window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
+
+ // 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.).
+ window->DC.LastItemId = window->MoveId;
+ window->DC.LastItemRect = title_bar_rect;
+ window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false);
}
// 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.
- const float border_size = window->BorderSize;
+ const float border_size = window->WindowBorderSize;
ImRect clip_rect;
- clip_rect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
- clip_rect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + border_size);
- clip_rect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
- clip_rect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - border_size);
+ clip_rect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - border_size)));
+ clip_rect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y);
+ clip_rect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - border_size)));
+ clip_rect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y);
PushClipRect(clip_rect.Min, clip_rect.Max, true);
// Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
if (first_begin_of_the_frame)
- window->Accessed = false;
+ window->WriteAccessed = false;
window->BeginCount++;
g.SetNextWindowSizeConstraint = false;
@@ -4691,7 +5000,7 @@ void ImGui::End()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- if (window->DC.ColumnsCount != 1) // close columns set if any is open
+ if (window->DC.ColumnsSet != NULL)
EndColumns();
PopClipRect(); // inner window clip rectangle
@@ -4726,7 +5035,7 @@ void ImGui::Scrollbar(ImGuiLayoutType direction)
bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX);
float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f;
const ImRect window_rect = window->Rect();
- const float border_size = window->BorderSize;
+ const float border_size = window->WindowBorderSize;
ImRect bb = horizontal
? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size)
: ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size);
@@ -4735,13 +5044,12 @@ void ImGui::Scrollbar(ImGuiLayoutType direction)
if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f)
return;
- float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
int window_rounding_corners;
if (horizontal)
- window_rounding_corners = ImGuiCorner_BotLeft | (other_scrollbar ? 0 : ImGuiCorner_BotRight);
+ window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
else
- window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BotRight);
- window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
+ window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight);
+ window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners);
bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f)));
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
@@ -4811,13 +5119,43 @@ void ImGui::Scrollbar(ImGuiLayoutType direction)
// Render
const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
+ ImRect grab_rect;
if (horizontal)
- window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding);
+ grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y);
else
- window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding);
+ grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y));
+ window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
}
-// Moving window to front of display (which happens to be back of our sorted list)
+void ImGui::BringWindowToFront(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ if (g.Windows.back() == window)
+ return;
+ for (int i = 0; i < g.Windows.Size; i++)
+ if (g.Windows[i] == window)
+ {
+ g.Windows.erase(g.Windows.begin() + i);
+ g.Windows.push_back(window);
+ break;
+ }
+}
+
+void ImGui::BringWindowToBack(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ if (g.Windows[0] == window)
+ return;
+ for (int i = 0; i < g.Windows.Size; i++)
+ if (g.Windows[i] == window)
+ {
+ memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
+ g.Windows[0] = window;
+ break;
+ }
+}
+
+// Moving window to front of display and set focus (which happens to be back of our sorted list)
void ImGui::FocusWindow(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
@@ -4829,7 +5167,7 @@ void ImGui::FocusWindow(ImGuiWindow* window)
if (!window)
return;
- // And move its root window to the top of the pile
+ // Move the root window to the top of the pile
if (window->RootWindow)
window = window->RootWindow;
@@ -4839,15 +5177,8 @@ void ImGui::FocusWindow(ImGuiWindow* window)
ClearActiveID();
// Bring to front
- if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window)
- return;
- for (int i = 0; i < g.Windows.Size; i++)
- if (g.Windows[i] == window)
- {
- g.Windows.erase(g.Windows.begin() + i);
- break;
- }
- g.Windows.push_back(window);
+ if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
+ BringWindowToFront(window);
}
void ImGui::FocusPreviousWindow()
@@ -4917,7 +5248,11 @@ static void SetCurrentFont(ImFont* font)
g.Font = font;
g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
- g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel;
+
+ ImFontAtlas* atlas = g.Font->ContainerAtlas;
+ g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
+ g.DrawListSharedData.Font = g.Font;
+ g.DrawListSharedData.FontSize = g.FontSize;
}
void ImGui::PushFont(ImFont* font)
@@ -5034,10 +5369,15 @@ static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] =
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
+ { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
- { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildWindowRounding) }, // ImGuiStyleVar_ChildWindowRounding
+ { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
+ { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
+ { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
+ { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
+ { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
{ ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
{ ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
@@ -5102,7 +5442,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
case ImGuiCol_Text: return "Text";
case ImGuiCol_TextDisabled: return "TextDisabled";
case ImGuiCol_WindowBg: return "WindowBg";
- case ImGuiCol_ChildWindowBg: return "ChildWindowBg";
+ case ImGuiCol_ChildBg: return "ChildBg";
case ImGuiCol_PopupBg: return "PopupBg";
case ImGuiCol_Border: return "Border";
case ImGuiCol_BorderShadow: return "BorderShadow";
@@ -5117,7 +5457,6 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
- case ImGuiCol_ComboBg: return "ComboBg";
case ImGuiCol_CheckMark: return "CheckMark";
case ImGuiCol_SliderGrab: return "SliderGrab";
case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
@@ -5142,55 +5481,73 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening";
+ case ImGuiCol_DragDropTarget: return "DragDropTarget";
}
IM_ASSERT(0);
return "Unknown";
}
+bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
+{
+ if (window->RootWindow == potential_parent)
+ return true;
+ while (window != NULL)
+ {
+ if (window == potential_parent)
+ return true;
+ window = window->ParentWindow;
+ }
+ return false;
+}
+
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
{
IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
ImGuiContext& g = *GImGui;
- if (g.HoveredWindow != g.CurrentWindow)
- return false;
+ switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
+ {
+ case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
+ if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
+ return false;
+ break;
+ case ImGuiHoveredFlags_RootWindow:
+ if (g.HoveredWindow != g.CurrentWindow->RootWindow)
+ return false;
+ break;
+ case ImGuiHoveredFlags_ChildWindows:
+ if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
+ return false;
+ break;
+ default:
+ if (g.HoveredWindow != g.CurrentWindow)
+ return false;
+ break;
+ }
+
if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
return false;
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
- if (g.ActiveId != 0 && g.ActiveIdWindow != g.CurrentWindow)
+ if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
return false;
return true;
}
-bool ImGui::IsWindowFocused()
-{
- ImGuiContext& g = *GImGui;
- return g.NavWindow == g.CurrentWindow;
-}
-
-bool ImGui::IsRootWindowFocused()
-{
- ImGuiContext& g = *GImGui;
- return g.NavWindow == g.CurrentWindow->RootWindow;
-}
-
-bool ImGui::IsRootWindowOrAnyChildFocused()
+bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
{
ImGuiContext& g = *GImGui;
- return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
-}
+ IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
-bool ImGui::IsRootWindowOrAnyChildHovered(ImGuiHoveredFlags flags)
-{
- IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
- ImGuiContext& g = *GImGui;
- if (!g.HoveredRootWindow || (g.HoveredRootWindow != g.CurrentWindow->RootWindow))
- return false;
- if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
- return false;
- if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
- if (g.ActiveId != 0 && g.ActiveIdWindow != g.CurrentWindow)
- return false;
- return true;
+ switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
+ {
+ case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
+ return g.NavWindow && g.CurrentWindow->RootWindow == g.NavWindow->RootWindow;
+ case ImGuiFocusedFlags_RootWindow:
+ return g.CurrentWindow->RootWindow == g.NavWindow;
+ case ImGuiFocusedFlags_ChildWindows:
+ return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
+ default:
+ return g.CurrentWindow == g.NavWindow;
+ }
}
float ImGui::GetWindowWidth()
@@ -5324,8 +5681,7 @@ bool ImGui::IsWindowAppearing()
void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
{
- ImGuiWindow* window = FindWindowByName(name);
- if (window)
+ if (ImGuiWindow* window = FindWindowByName(name))
SetWindowCollapsed(window, collapsed, cond);
}
@@ -5374,14 +5730,7 @@ void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& s
void ImGui::SetNextWindowContentSize(const ImVec2& size)
{
ImGuiContext& g = *GImGui;
- g.SetNextWindowContentSizeVal = size;
- g.SetNextWindowContentSizeCond = ImGuiCond_Always;
-}
-
-void ImGui::SetNextWindowContentWidth(float width)
-{
- ImGuiContext& g = *GImGui;
- g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f);
+ g.SetNextWindowContentSizeVal = size; // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value.
g.SetNextWindowContentSizeCond = ImGuiCond_Always;
}
@@ -5403,8 +5752,8 @@ ImVec2 ImGui::GetContentRegionMax()
{
ImGuiWindow* window = GetCurrentWindowRead();
ImVec2 mx = window->ContentsRegionRect.Max;
- if (window->DC.ColumnsCount != 1)
- mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x;
+ if (window->DC.ColumnsSet)
+ mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
return mx;
}
@@ -5450,7 +5799,13 @@ float ImGui::GetTextLineHeightWithSpacing()
return g.FontSize + g.Style.ItemSpacing.y;
}
-float ImGui::GetItemsLineHeightWithSpacing()
+float ImGui::GetFrameHeight()
+{
+ ImGuiContext& g = *GImGui;
+ return g.FontSize + g.Style.FramePadding.y * 2.0f;
+}
+
+float ImGui::GetFrameHeightWithSpacing()
{
ImGuiContext& g = *GImGui;
return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
@@ -5474,7 +5829,7 @@ float ImGui::GetFontSize()
ImVec2 ImGui::GetFontTexUvWhitePixel()
{
- return GImGui->FontTexUvWhitePixel;
+ return GImGui->DrawListSharedData.TexUvWhitePixel;
}
void ImGui::SetWindowFontScale(float scale)
@@ -5482,7 +5837,7 @@ void ImGui::SetWindowFontScale(float scale)
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
window->FontWindowScale = scale;
- g.FontSize = window->CalcFontSize();
+ g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
}
// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
@@ -5557,14 +5912,12 @@ float ImGui::GetScrollY()
float ImGui::GetScrollMaxX()
{
- ImGuiWindow* window = GetCurrentWindowRead();
- return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
+ return GetScrollMaxX(GImGui->CurrentWindow);
}
float ImGui::GetScrollMaxY()
{
- ImGuiWindow* window = GetCurrentWindowRead();
- return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
+ return GetScrollMaxY(GImGui->CurrentWindow);
}
void ImGui::SetScrollX(float scroll_x)
@@ -5587,9 +5940,13 @@ void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
- if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y)
- window->ScrollTarget.y = 0.0f;
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.
@@ -5601,6 +5958,15 @@ void ImGui::SetScrollHere(float center_y_ratio)
SetScrollFromPosY(target_y, center_y_ratio);
}
+// FIXME-NAV: This function is a placeholder for the upcoming Navigation branch + Focusing features.
+// In the current branch this function will only set the scrolling, in the navigation branch it will also set your navigation cursor.
+// Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHere()" when applicable.
+void ImGui::SetItemDefaultFocus()
+{
+ if (IsWindowAppearing())
+ SetScrollHere();
+}
+
void ImGui::SetKeyboardFocusHere(int offset)
{
IM_ASSERT(offset >= -1); // -1 is allowed but not below
@@ -5855,15 +6221,32 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
flags |= ImGuiButtonFlags_PressedOnClickRelease;
ImGuiWindow* backup_hovered_window = g.HoveredWindow;
- if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
+ if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
g.HoveredWindow = window;
bool pressed = false;
bool hovered = ItemHoverable(bb, id);
- if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
+ // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
+ if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && g.DragDropActive && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
+ if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
+ {
+ hovered = true;
+ SetHoveredID(id);
+ if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy
+ {
+ pressed = true;
+ FocusWindow(window);
+ }
+ }
+
+ if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window)
g.HoveredWindow = backup_hovered_window;
+ // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
+ if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
+ hovered = false;
+
if (hovered)
{
if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
@@ -5883,11 +6266,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
{
pressed = true;
if (flags & ImGuiButtonFlags_NoHoldingActiveID)
+ {
ClearActiveID();
+ }
else
+ {
SetActiveID(id, window); // Hold on ID
+ g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
+ }
FocusWindow(window);
- g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
}
if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
{
@@ -5914,15 +6301,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
{
if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease))
if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release>
- pressed = true;
+ if (!g.DragDropActive)
+ pressed = true;
ClearActiveID();
}
}
- // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
- if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0))
- hovered = pressed = held = false;
-
if (out_hovered) *out_hovered = hovered;
if (out_held) *out_held = held;
@@ -6028,6 +6412,34 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius)
return pressed;
}
+// [Internal]
+bool ImGui::ArrowButton(ImGuiID id, ImGuiDir dir, ImVec2 padding, ImGuiButtonFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ if (window->SkipItems)
+ return false;
+
+ const ImGuiStyle& style = g.Style;
+
+ const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + padding.x * 2.0f, g.FontSize + padding.y * 2.0f));
+ ItemSize(bb, style.FramePadding.y);
+ if (!ItemAdd(bb, id))
+ return false;
+
+ bool hovered, held;
+ bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
+
+ const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
+#ifdef IMGUI_HAS_NAV
+ RenderNavHighlight(bb, id);
+#endif
+ RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
+ RenderTriangle(bb.Min + padding, dir, 1.0f);
+
+ return pressed;
+}
+
void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGuiWindow* window = GetCurrentWindow();
@@ -6289,9 +6701,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
// - OpenOnDoubleClick .............. double-click anywhere to open
// - OpenOnArrow .................... single-click on arrow to open
// - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
- ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0);
+ ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0);
+ button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
+
bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf))
{
@@ -6300,13 +6714,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y));
if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
toggled |= g.IO.MouseDoubleClicked[0];
+ if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
+ toggled = false;
if (toggled)
{
is_open = !is_open;
window->DC.StateStorage->SetInt(id, is_open);
}
}
- if (flags & ImGuiTreeNodeFlags_AllowOverlapMode)
+ if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
SetItemAllowOverlap();
// Render
@@ -6372,14 +6788,13 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags
return false;
ImGuiID id = window->GetID(label);
- bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label);
+ bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label);
if (p_open)
{
// Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
ImGuiContext& g = *GImGui;
float button_sz = g.FontSize * 0.5f;
ImGuiItemHoveredDataBackup last_item_backup;
- last_item_backup.Backup();
if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz))
*p_open = false;
last_item_backup.Restore();
@@ -7583,7 +7998,7 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over
// Render
fraction = ImSaturate(fraction);
RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
- bb.Expand(ImVec2(-window->BorderSize, -window->BorderSize));
+ bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
@@ -7706,10 +8121,10 @@ bool ImGui::RadioButton(const char* label, bool active)
window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16);
}
- if (window->Flags & ImGuiWindowFlags_ShowBorders)
+ if (style.FrameBorderSize > 0.0f)
{
- window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16);
- window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16);
+ window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize);
+ window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize);
}
if (g.LogEnabled)
@@ -8001,6 +8416,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0;
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
+ const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
BeginGroup();
@@ -8135,7 +8551,7 @@ 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.OSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text
+ 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]))
{
edit_state.SelectAll();
@@ -8187,9 +8603,9 @@ 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.OSXBehaviors ? (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.OSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
- const bool is_startend_key_down = io.OSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
+ 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
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); }
@@ -8203,7 +8619,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.OSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|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);
}
edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
}
@@ -8227,10 +8643,10 @@ 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) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
- else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { 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_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 (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
{
// Cut, Copy
@@ -8527,7 +8943,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.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f;
+ bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(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))
@@ -8580,12 +8996,6 @@ 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);
}
-static inline float SmallSquareSize()
-{
- ImGuiContext& g = *GImGui;
- return g.FontSize + g.Style.FramePadding.y * 2.0f;
-}
-
// 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)
{
@@ -8599,7 +9009,7 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data
BeginGroup();
PushID(label);
- const ImVec2 button_sz = ImVec2(SmallSquareSize(), SmallSquareSize());
+ const ImVec2 button_sz = ImVec2(GetFrameHeight(), GetFrameHeight());
if (step_ptr)
PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
@@ -8744,62 +9154,25 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla
return InputIntN(label, v, 4, extra_flags);
}
-static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
-{
- const char* const* items = (const char* const*)data;
- if (out_text)
- *out_text = items[idx];
- return true;
-}
-
-static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
-{
- // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
- const char* items_separated_by_zeros = (const char*)data;
- int items_count = 0;
- const char* p = items_separated_by_zeros;
- while (*p)
- {
- if (idx == items_count)
- break;
- p += strlen(p) + 1;
- items_count++;
- }
- if (!*p)
- return false;
- if (out_text)
- *out_text = p;
- return true;
-}
-
-// Combo box helper allowing to pass an array of strings.
-bool ImGui::Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items)
-{
- const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
- return value_changed;
-}
-
-// Combo box helper allowing to pass all items in a single string.
-bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
+static float CalcMaxPopupHeightFromItemCount(int items_count)
{
- int items_count = 0;
- const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
- while (*p)
- {
- p += strlen(p) + 1;
- items_count++;
- }
- bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
- return value_changed;
+ ImGuiContext& g = *GImGui;
+ if (items_count <= 0)
+ return FLT_MAX;
+ return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
}
-bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popup_size)
+bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
{
+ // Always consume the SetNextWindowSizeConstraint() call in our early return paths
+ ImGuiContext& g = *GImGui;
+ bool backup_has_next_window_size_constraint = g.SetNextWindowSizeConstraint;
+ g.SetNextWindowSizeConstraint = false;
+
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
- ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const float w = CalcItemWidth();
@@ -8811,21 +9184,17 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popu
if (!ItemAdd(total_bb, id))
return false;
- const float arrow_size = SmallSquareSize();
-
bool hovered, held;
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
-
bool popup_open = IsPopupOpen(id);
+ const float arrow_size = GetFrameHeight();
const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
RenderTriangle(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
-
if (preview_value != NULL)
RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
-
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@@ -8838,63 +9207,81 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popu
if (!popup_open)
return false;
- if (popup_size.x == 0.0f)
- popup_size.x = w;
-
- float popup_y1 = frame_bb.Max.y;
- float popup_y2 = ImClamp(popup_y1 + popup_size.y, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
- if ((popup_y2 - popup_y1) < ImMin(popup_size.y, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
+ if (backup_has_next_window_size_constraint)
{
- // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
- popup_y1 = ImClamp(frame_bb.Min.y - popup_size.y, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
- popup_y2 = frame_bb.Min.y;
- SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Min.y), ImGuiCond_Always, ImVec2(0.0f, 1.0f));
+ g.SetNextWindowSizeConstraint = true;
+ g.SetNextWindowSizeConstraintRect.Min.x = ImMax(g.SetNextWindowSizeConstraintRect.Min.x, w);
}
else
{
- // Position our combo below
- SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Max.y), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
+ if ((flags & ImGuiComboFlags_HeightMask_) == 0)
+ flags |= ImGuiComboFlags_HeightRegular;
+ IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one
+ int popup_max_height_in_items = -1;
+ if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8;
+ else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4;
+ else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20;
+ SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
+ }
+
+ char name[16];
+ ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
+
+ // Peak into expected window size so we can position it
+ if (ImGuiWindow* popup_window = FindWindowByName(name))
+ {
+ ImVec2 size_contents = CalcSizeContents(popup_window);
+ 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);
+ SetNextWindowPos(pos);
}
- SetNextWindowSize(ImVec2(popup_size.x, popup_y2 - popup_y1), ImGuiCond_Appearing);
- PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
- const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
- if (!BeginPopupEx(id, flags))
+ ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
+ if (!Begin(name, NULL, window_flags))
{
+ EndPopup();
IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
return false;
}
- Spacing();
+
+ // 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();
}
-// Combo box function.
-bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
+// Old API, prefer using BeginCombo() nowadays if you can.
+bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items)
{
ImGuiContext& g = *GImGui;
- const ImGuiStyle& style = g.Style;
const char* preview_text = NULL;
if (*current_item >= 0 && *current_item < items_count)
items_getter(data, *current_item, &preview_text);
- // Size default to hold ~7 items
- if (height_in_items < 0)
- height_in_items = 7;
- float popup_height = (g.FontSize + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
+ // The old Combo() API exposed "popup_max_height_in_items", however the new more general BeginCombo() API doesn't, so we emulate it here.
+ if (popup_max_height_in_items != -1 && !g.SetNextWindowSizeConstraint)
+ {
+ float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
+ SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, popup_max_height));
+ }
- if (!BeginCombo(label, preview_text, ImVec2(0.0f, popup_height)))
+ if (!BeginCombo(label, preview_text, 0))
return false;
// Display items
- // FIXME-OPT: Use clipper
+ // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
bool value_changed = false;
for (int i = 0; i < items_count; i++)
{
@@ -8908,8 +9295,8 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
value_changed = true;
*current_item = i;
}
- if (item_selected && IsWindowAppearing())
- SetScrollHere();
+ if (item_selected)
+ SetItemDefaultFocus();
PopID();
}
@@ -8917,6 +9304,55 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
return value_changed;
}
+static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
+{
+ const char* const* items = (const char* const*)data;
+ if (out_text)
+ *out_text = items[idx];
+ return true;
+}
+
+static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
+{
+ // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
+ const char* items_separated_by_zeros = (const char*)data;
+ int items_count = 0;
+ const char* p = items_separated_by_zeros;
+ while (*p)
+ {
+ if (idx == items_count)
+ break;
+ p += strlen(p) + 1;
+ items_count++;
+ }
+ if (!*p)
+ return false;
+ if (out_text)
+ *out_text = p;
+ return true;
+}
+
+// Combo box helper allowing to pass an array of strings.
+bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
+{
+ const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
+ return value_changed;
+}
+
+// Combo box helper allowing to pass all items in a single string.
+bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
+{
+ int items_count = 0;
+ const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open
+ while (*p)
+ {
+ p += strlen(p) + 1;
+ items_count++;
+ }
+ bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
+ return value_changed;
+}
+
// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID.
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
@@ -8928,7 +9364,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) // FIXME-OPT: Avoid if vertically clipped.
+ if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped.
PopClipRect();
ImGuiID id = window->GetID(label);
@@ -8959,7 +9395,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
bb_with_spacing.Max.y += spacing_D;
if (!ItemAdd(bb_with_spacing, id))
{
- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
+ if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
PushColumnClipRect();
return false;
}
@@ -8981,7 +9417,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f);
}
- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
+ if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
{
PushColumnClipRect();
bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x);
@@ -9052,7 +9488,7 @@ bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_item
void ImGui::ListBoxFooter()
{
- ImGuiWindow* parent_window = GetParentWindow();
+ ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
const ImRect bb = parent_window->DC.LastItemRect;
const ImGuiStyle& style = GetStyle();
@@ -9137,7 +9573,7 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo
PopStyleColor();
}
if (selected)
- RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * (0.20f+0.200f), g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
+ RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
}
return pressed;
}
@@ -9189,9 +9625,15 @@ bool ImGui::BeginMenuBar()
IM_ASSERT(!window->DC.MenuBarAppending);
BeginGroup(); // Save position
PushID("##menubar");
- ImRect rect = window->MenuBarRect();
- PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false);
- window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
+
+ // 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.
+ 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->Rect());
+ 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.LayoutType = ImGuiLayoutType_Horizontal;
window->DC.MenuBarAppending = true;
AlignTextToFramePadding();
@@ -9229,7 +9671,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
bool pressed;
bool menu_is_open = IsPopupOpen(id);
- bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus"));
+ bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##Menus"));
ImGuiWindow* backed_nav_window = g.NavWindow;
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)
@@ -9240,6 +9682,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
{
// 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()
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);
@@ -9256,7 +9699,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
- RenderTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), ImGuiDir_Right);
+ RenderTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right);
if (!enabled) PopStyleColor();
}
@@ -9322,7 +9765,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
if (menu_is_open)
{
SetNextWindowPos(popup_pos, ImGuiCond_Always);
- ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
+ ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | 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)
}
@@ -9349,7 +9792,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags
Separator();
}
- ImVec2 sz(g.FontSize * 3, g.FontSize * 3);
+ ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);
SameLine();
if (flags & ImGuiColorEditFlags_NoAlpha)
@@ -9391,8 +9834,8 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU
if (x2 <= x1)
continue;
int rounding_corners_flags_cell = 0;
- if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImGuiCorner_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImGuiCorner_TopRight; }
- if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImGuiCorner_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImGuiCorner_BotRight; }
+ if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; }
+ if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; }
rounding_corners_flags_cell &= rounding_corners_flags;
window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
}
@@ -9430,7 +9873,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(desc_id);
- float default_size = SmallSquareSize();
+ float default_size = GetFrameHeight();
if (size.x == 0.0f)
size.x = default_size;
if (size.y == 0.0f)
@@ -9455,8 +9898,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
{
float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
- RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImGuiCorner_TopRight|ImGuiCorner_BotRight);
- window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImGuiCorner_TopLeft|ImGuiCorner_BotLeft);
+ RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight);
+ window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft);
}
else
{
@@ -9465,14 +9908,29 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
if (col_source.w < 1.0f)
RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
else
- window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ~0);
+ window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All);
}
- if (window->Flags & ImGuiWindowFlags_ShowBorders)
+ if (g.Style.FrameBorderSize > 0.0f)
RenderFrameBorder(bb.Min, bb.Max, rounding);
else
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
- if (hovered && !(flags & ImGuiColorEditFlags_NoTooltip))
+ // Drag and Drop Source
+ if (g.ActiveId == id && BeginDragDropSource()) // NB: The ActiveId test is merely an optional micro-optimization
+ {
+ if (flags & ImGuiColorEditFlags_NoAlpha)
+ SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once);
+ else
+ SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once);
+ ColorButton(desc_id, col, flags);
+ SameLine();
+ TextUnformatted("Color");
+ EndDragDropSource();
+ hovered = false;
+ }
+
+ // Tooltip
+ if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
return pressed;
@@ -9531,7 +9989,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
EndPopup();
}
-static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, float* ref_col)
+static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, const float* ref_col)
{
bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask);
bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
@@ -9540,7 +9998,7 @@ static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, float* ref_col)
ImGuiContext& g = *GImGui;
if (allow_opt_picker)
{
- ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (SmallSquareSize() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
+ ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
ImGui::PushItemWidth(picker_size.x);
for (int picker_type = 0; picker_type < 2; picker_type++)
{
@@ -9580,7 +10038,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- const float square_sz = SmallSquareSize();
+ const float square_sz = GetFrameHeight();
const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
const float w_items_all = CalcItemWidth() - w_extra;
const char* label_display_end = FindRenderedTextEnd(label);
@@ -9649,7 +10107,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if (n + 1 == components)
PushItemWidth(w_item_last);
if (flags & ImGuiColorEditFlags_Float)
- value_changed |= value_changed_as_float |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
+ value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
else
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
if (!(flags & ImGuiColorEditFlags_NoOptions))
@@ -9669,7 +10127,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
PushItemWidth(w_items_all);
if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))
{
- value_changed |= true;
+ value_changed = true;
char* p = buf;
while (*p == '#' || ImCharIsSpace(*p))
p++;
@@ -9684,7 +10142,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
PopItemWidth();
}
- bool picker_active = false;
+ ImGuiWindow* picker_active_window = NULL;
if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
{
if (!(flags & ImGuiColorEditFlags_NoInputs))
@@ -9706,7 +10164,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if (BeginPopup("picker"))
{
- picker_active = true;
+ picker_active_window = g.CurrentWindow;
if (label != label_display_end)
{
TextUnformatted(label, label_display_end);
@@ -9728,7 +10186,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
}
// Convert back
- if (!picker_active)
+ if (picker_active_window == NULL)
{
if (!value_changed_as_float)
for (int n = 0; n < 4; n++)
@@ -9748,6 +10206,26 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
PopID();
EndGroup();
+ // Drag and Drop Target
+ if (window->DC.LastItemRectHoveredRect && BeginDragDropTarget()) // NB: The LastItemRectHoveredRect test is merely an optional micro-optimization
+ {
+ if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
+ {
+ memcpy((float*)col, payload->Data, sizeof(float) * 3);
+ value_changed = true;
+ }
+ if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
+ {
+ memcpy((float*)col, payload->Data, sizeof(float) * components);
+ value_changed = true;
+ }
+ EndDragDropTarget();
+ }
+
+ // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
+ if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
+ window->DC.LastItemId = g.ActiveId;
+
return value_changed;
}
@@ -9769,7 +10247,7 @@ static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGui
case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;
case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;
case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;
- default: return; // Fix warning for ImGuiDir_None
+ case ImGuiDir_None: case ImGuiDir_Count_: break; // Fix warnings
}
}
@@ -9811,15 +10289,19 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
// Setup
+ int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
ImVec2 picker_pos = window->DC.CursorPos;
- float square_sz = SmallSquareSize();
+ float square_sz = GetFrameHeight();
float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f);
+ float backup_initial_col[4];
+ memcpy(backup_initial_col, col, components * sizeof(float));
+
float wheel_thickness = sv_picker_size * 0.08f;
float wheel_r_outer = sv_picker_size * 0.50f;
float wheel_r_inner = wheel_r_outer - wheel_thickness;
@@ -9935,7 +10417,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)))
{
- memcpy(col, ref_col, ((flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4) * sizeof(float));
+ memcpy(col, ref_col, components * sizeof(float));
value_changed = true;
}
}
@@ -9991,14 +10473,15 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
{
const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps;
const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
- int vert_start_idx = draw_list->_VtxCurrentIdx;
+ const int vert_start_idx = draw_list->VtxBuffer.Size;
draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness);
+ 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);
- ShadeVertsLinearColorGradientKeepAlpha(draw_list->_VtxWritePtr - (draw_list->_VtxCurrentIdx - vert_start_idx), draw_list->_VtxWritePtr, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
+ 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
@@ -10015,7 +10498,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
- ImVec2 uv_white = g.FontTexUvWhitePixel;
+ ImVec2 uv_white = GetFontTexUvWhitePixel();
draw_list->PrimReserve(6, 6);
draw_list->PrimVtx(tra, uv_white, hue_color32);
draw_list->PrimVtx(trb, uv_white, hue_color32);
@@ -10064,7 +10547,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
EndGroup();
PopID();
- return value_changed;
+ return value_changed && memcmp(backup_initial_col, col, components * sizeof(float));
}
// Horizontal separating line.
@@ -10086,7 +10569,7 @@ void ImGui::Separator()
}
// Horizontal Separator
- if (window->DC.ColumnsCount > 1)
+ if (window->DC.ColumnsSet)
PopClipRect();
float x1 = window->Pos.x;
@@ -10098,7 +10581,7 @@ void ImGui::Separator()
ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout.
if (!ItemAdd(bb, 0))
{
- if (window->DC.ColumnsCount > 1)
+ if (window->DC.ColumnsSet)
PushColumnClipRect();
return;
}
@@ -10108,10 +10591,10 @@ void ImGui::Separator()
if (g.LogEnabled)
LogRenderedText(NULL, IM_NEWLINE "--------------------------------");
- if (window->DC.ColumnsCount > 1)
+ if (window->DC.ColumnsSet)
{
PushColumnClipRect();
- window->DC.ColumnsCellMinY = window->DC.CursorPos.y;
+ window->DC.ColumnsSet->CellMinY = window->DC.CursorPos.y;
}
}
@@ -10134,6 +10617,55 @@ void ImGui::VerticalSeparator()
LogText(" |");
}
+bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
+#ifdef IMGUI_HAS_NAV
+ window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
+#endif
+ bool add = ItemAdd(bb, id);
+ window->DC.ItemFlags = item_flags_backup;
+ if (!add)
+ return false;
+
+ bool hovered, held;
+ ImRect bb_interact = bb;
+ bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
+ ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
+ if (g.ActiveId != id)
+ SetItemAllowOverlap();
+
+ if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id))
+ SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
+
+ ImRect bb_render = bb;
+ if (held)
+ {
+ ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
+ float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
+
+ // Minimum pane size
+ if (mouse_delta < min_size1 - *size1)
+ mouse_delta = min_size1 - *size1;
+ if (mouse_delta > *size2 - min_size2)
+ mouse_delta = *size2 - min_size2;
+
+ // Apply resize
+ *size1 += mouse_delta;
+ *size2 -= mouse_delta;
+ bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
+ }
+
+ // Render
+ const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
+ window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding);
+
+ return held;
+}
+
void ImGui::Spacing()
{
ImGuiWindow* window = GetCurrentWindow();
@@ -10199,7 +10731,6 @@ void ImGui::EndGroup()
ImGuiGroupData& group_data = window->DC.GroupStack.back();
ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
- group_bb.Max.y -= g.Style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves.
group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
window->DC.CursorPos = group_data.BackupCursorPos;
@@ -10276,29 +10807,30 @@ void ImGui::NewLine()
void ImGui::NextColumn()
{
ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems || window->DC.ColumnsCount <= 1)
+ if (window->SkipItems || window->DC.ColumnsSet == NULL)
return;
ImGuiContext& g = *GImGui;
PopItemWidth();
PopClipRect();
- window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
- if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount)
+ ImGuiColumnsSet* columns = window->DC.ColumnsSet;
+ columns->CellMaxY = ImMax(columns->CellMaxY, window->DC.CursorPos.y);
+ if (++columns->Current < columns->Count)
{
// Columns 1+ cancel out IndentX
- window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x;
- window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent);
+ window->DC.ColumnsOffsetX = GetColumnOffset(columns->Current) - window->DC.IndentX + g.Style.ItemSpacing.x;
+ window->DrawList->ChannelsSetCurrent(columns->Current);
}
else
{
- window->DC.ColumnsCurrent = 0;
window->DC.ColumnsOffsetX = 0.0f;
- window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY;
window->DrawList->ChannelsSetCurrent(0);
+ columns->Current = 0;
+ columns->CellMinY = columns->CellMaxY;
}
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
- window->DC.CursorPos.y = window->DC.ColumnsCellMinY;
+ window->DC.CursorPos.y = columns->CellMinY;
window->DC.CurrentLineHeight = 0.0f;
window->DC.CurrentLineTextBaseOffset = 0.0f;
@@ -10309,38 +10841,40 @@ void ImGui::NextColumn()
int ImGui::GetColumnIndex()
{
ImGuiWindow* window = GetCurrentWindowRead();
- return window->DC.ColumnsCurrent;
+ return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
}
int ImGui::GetColumnsCount()
{
ImGuiWindow* window = GetCurrentWindowRead();
- return window->DC.ColumnsCount;
+ return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
}
-static float OffsetNormToPixels(ImGuiWindow* window, float offset_norm)
+static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
{
- return offset_norm * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
+ return offset_norm * (columns->MaxX - columns->MinX);
}
-static float PixelsToOffsetNorm(ImGuiWindow* window, float offset)
+static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
{
- return (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX);
+ return offset / (columns->MaxX - columns->MinX);
}
-static float GetDraggedColumnOffset(int column_index)
+static inline float GetColumnsRectHalfWidth() { return 4.0f; }
+
+static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
{
// Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
// window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
- IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
+ IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
- float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x;
- x = ImMax(x, ImGui::GetColumnOffset(column_index-1) + g.Style.ColumnsMinSpacing);
- if ((window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths))
- x = ImMin(x, ImGui::GetColumnOffset(column_index+1) - g.Style.ColumnsMinSpacing);
+ float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
+ x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
+ if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
+ x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
return x;
}
@@ -10348,128 +10882,174 @@ static float GetDraggedColumnOffset(int column_index)
float ImGui::GetColumnOffset(int column_index)
{
ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiColumnsSet* columns = window->DC.ColumnsSet;
+ IM_ASSERT(columns != NULL);
+
if (column_index < 0)
- column_index = window->DC.ColumnsCurrent;
+ column_index = columns->Current;
+ IM_ASSERT(column_index < columns->Columns.Size);
/*
if (g.ActiveId)
{
ImGuiContext& g = *GImGui;
- const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
+ const ImGuiID column_id = columns->ColumnsSetId + ImGuiID(column_index);
if (g.ActiveId == column_id)
- return GetDraggedColumnOffset(column_index);
+ return GetDraggedColumnOffset(columns, column_index);
}
*/
- IM_ASSERT(column_index < window->DC.ColumnsData.Size);
- const float t = window->DC.ColumnsData[column_index].OffsetNorm;
- const float x_offset = ImLerp(window->DC.ColumnsMinX, window->DC.ColumnsMaxX, t);
+ const float t = columns->Columns[column_index].OffsetNorm;
+ const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
return x_offset;
}
-void ImGui::SetColumnOffset(int column_index, float offset)
+static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
{
- ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindow();
if (column_index < 0)
- column_index = window->DC.ColumnsCurrent;
+ column_index = columns->Current;
+
+ float offset_norm;
+ if (before_resize)
+ offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
+ else
+ offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
+ return OffsetNormToPixels(columns, offset_norm);
+}
+
+float ImGui::GetColumnWidth(int column_index)
+{
+ ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiColumnsSet* columns = window->DC.ColumnsSet;
+ IM_ASSERT(columns != NULL);
+
+ if (column_index < 0)
+ column_index = columns->Current;
+ return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
+}
- IM_ASSERT(column_index < window->DC.ColumnsData.Size);
+void ImGui::SetColumnOffset(int column_index, float offset)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiColumnsSet* columns = window->DC.ColumnsSet;
+ IM_ASSERT(columns != NULL);
- const bool preserve_width = !(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < window->DC.ColumnsCount-1);
- const float width = preserve_width ? GetColumnWidth(column_index) : 0.0f;
+ if (column_index < 0)
+ column_index = columns->Current;
+ IM_ASSERT(column_index < columns->Columns.Size);
- if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
- offset = ImMin(offset, window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index));
- const float offset_norm = PixelsToOffsetNorm(window, offset);
+ const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
+ const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
- const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
- window->DC.StateStorage->SetFloat(column_id, offset_norm);
- window->DC.ColumnsData[column_index].OffsetNorm = offset_norm;
+ if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
+ offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
+ columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
if (preserve_width)
SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
}
-float ImGui::GetColumnWidth(int column_index)
+void ImGui::SetColumnWidth(int column_index, float width)
{
ImGuiWindow* window = GetCurrentWindowRead();
- if (column_index < 0)
- column_index = window->DC.ColumnsCurrent;
+ ImGuiColumnsSet* columns = window->DC.ColumnsSet;
+ IM_ASSERT(columns != NULL);
- return OffsetNormToPixels(window, window->DC.ColumnsData[column_index+1].OffsetNorm - window->DC.ColumnsData[column_index].OffsetNorm);
+ if (column_index < 0)
+ column_index = columns->Current;
+ SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
}
-void ImGui::SetColumnWidth(int column_index, float width)
+void ImGui::PushColumnClipRect(int column_index)
{
ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiColumnsSet* columns = window->DC.ColumnsSet;
if (column_index < 0)
- column_index = window->DC.ColumnsCurrent;
+ column_index = columns->Current;
- SetColumnOffset(column_index+1, GetColumnOffset(column_index) + width);
+ PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
}
-void ImGui::PushColumnClipRect(int column_index)
+static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
{
- ImGuiWindow* window = GetCurrentWindowRead();
- if (column_index < 0)
- column_index = window->DC.ColumnsCurrent;
+ for (int n = 0; n < window->ColumnsStorage.Size; n++)
+ if (window->ColumnsStorage[n].ID == id)
+ return &window->ColumnsStorage[n];
- PushClipRect(window->DC.ColumnsData[column_index].ClipRect.Min, window->DC.ColumnsData[column_index].ClipRect.Max, false);
+ window->ColumnsStorage.push_back(ImGuiColumnsSet());
+ ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
+ columns->ID = id;
+ return columns;
}
-void ImGui::BeginColumns(const char* id, int columns_count, ImGuiColumnsFlags flags)
+void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count > 1);
- IM_ASSERT(window->DC.ColumnsCount == 1); // Nested columns are currently not supported
+ IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
// Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
// In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
- PushID(0x11223347 + (id ? 0 : columns_count));
- window->DC.ColumnsSetId = window->GetID(id ? id : "columns");
+ PushID(0x11223347 + (str_id ? 0 : columns_count));
+ ImGuiID id = window->GetID(str_id ? str_id : "columns");
PopID();
- // Set state for first column
- window->DC.ColumnsCurrent = 0;
- window->DC.ColumnsCount = columns_count;
- window->DC.ColumnsFlags = flags;
+ // Acquire storage for the columns set
+ ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
+ IM_ASSERT(columns->ID == id);
+ columns->Current = 0;
+ columns->Count = columns_count;
+ columns->Flags = flags;
+ window->DC.ColumnsSet = columns;
+ // Set state for first column
const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->Size.x -window->ScrollbarSizes.x);
- window->DC.ColumnsMinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range
- //window->DC.ColumnsMaxX = content_region_width - window->Scroll.x -((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
- window->DC.ColumnsMaxX = content_region_width - window->Scroll.x;
- window->DC.ColumnsStartPosY = window->DC.CursorPos.y;
- window->DC.ColumnsStartMaxPosX = window->DC.CursorMaxPos.x;
- window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y;
+ columns->MinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range
+ //column->MaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x;
+ columns->MaxX = content_region_width - window->Scroll.x;
+ columns->StartPosY = window->DC.CursorPos.y;
+ columns->StartMaxPosX = window->DC.CursorMaxPos.x;
+ columns->CellMinY = columns->CellMaxY = window->DC.CursorPos.y;
window->DC.ColumnsOffsetX = 0.0f;
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
- // Cache column offsets
- window->DC.ColumnsData.resize(columns_count + 1);
- for (int column_index = 0; column_index < columns_count + 1; column_index++)
+ // Initialize defaults
+ columns->IsFirstFrame = (columns->Columns.Size == 0);
+ if (columns->Columns.Size == 0)
{
- const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index);
- KeepAliveID(column_id);
- const float default_t = column_index / (float)window->DC.ColumnsCount;
- float t = window->DC.StateStorage->GetFloat(column_id, default_t);
- if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoForceWithinWindow))
- t = ImMin(t, PixelsToOffsetNorm(window, window->DC.ColumnsMaxX - g.Style.ColumnsMinSpacing * (window->DC.ColumnsCount - column_index)));
- window->DC.ColumnsData[column_index].OffsetNorm = t;
+ columns->Columns.reserve(columns_count + 1);
+ for (int n = 0; n < columns_count + 1; n++)
+ {
+ ImGuiColumnData column;
+ column.OffsetNorm = n / (float)columns_count;
+ columns->Columns.push_back(column);
+ }
}
+ IM_ASSERT(columns->Columns.Size == columns_count + 1);
- // Cache clipping rectangles
- for (int column_index = 0; column_index < columns_count; column_index++)
+ for (int n = 0; n < columns_count + 1; n++)
{
- float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index) - 1.0f);
- float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index + 1) - 1.0f);
- window->DC.ColumnsData[column_index].ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
- window->DC.ColumnsData[column_index].ClipRect.ClipWith(window->ClipRect);
+ // Clamp position
+ ImGuiColumnData* column = &columns->Columns[n];
+ float t = column->OffsetNorm;
+ if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
+ t = ImMin(t, PixelsToOffsetNorm(columns, (columns->MaxX - columns->MinX) - g.Style.ColumnsMinSpacing * (columns->Count - n)));
+ column->OffsetNorm = t;
+
+ if (n == columns_count)
+ continue;
+
+ // Compute clipping rectangle
+ float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
+ float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
+ column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
+ column->ClipRect.ClipWith(window->ClipRect);
}
- window->DrawList->ChannelsSplit(window->DC.ColumnsCount);
+ window->DrawList->ChannelsSplit(columns->Count);
PushColumnClipRect();
PushItemWidth(GetColumnWidth() * 0.65f);
}
@@ -10478,73 +11058,75 @@ void ImGui::EndColumns()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
- IM_ASSERT(window->DC.ColumnsCount > 1);
+ ImGuiColumnsSet* columns = window->DC.ColumnsSet;
+ IM_ASSERT(columns != NULL);
PopItemWidth();
PopClipRect();
window->DrawList->ChannelsMerge();
- window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
- window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
- window->DC.CursorMaxPos.x = ImMax(window->DC.ColumnsStartMaxPosX, window->DC.ColumnsMaxX); // Columns don't grow parent
+ columns->CellMaxY = ImMax(columns->CellMaxY, window->DC.CursorPos.y);
+ window->DC.CursorPos.y = columns->CellMaxY;
+ if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
+ window->DC.CursorMaxPos.x = ImMax(columns->StartMaxPosX, columns->MaxX); // Restore cursor max pos, as columns don't grow parent
// Draw columns borders and handle resize
- if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
+ bool is_being_resized = false;
+ if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
{
- const float y1 = window->DC.ColumnsStartPosY;
+ const float y1 = columns->StartPosY;
const float y2 = window->DC.CursorPos.y;
int dragging_column = -1;
- for (int i = 1; i < window->DC.ColumnsCount; i++)
+ for (int n = 1; n < columns->Count; n++)
{
- float x = window->Pos.x + GetColumnOffset(i);
- const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
- const float column_w = 4.0f; // Width for interaction
- const ImRect column_rect(ImVec2(x - column_w, y1), ImVec2(x + column_w, y2));
+ float x = window->Pos.x + GetColumnOffset(n);
+ const ImGuiID column_id = columns->ID + ImGuiID(n);
+ const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
+ const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
+ KeepAliveID(column_id);
if (IsClippedEx(column_rect, column_id, false))
continue;
bool hovered = false, held = false;
- if (!(window->DC.ColumnsFlags & ImGuiColumnsFlags_NoResize))
+ if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
{
ButtonBehavior(column_rect, column_id, &hovered, &held);
if (hovered || held)
g.MouseCursor = ImGuiMouseCursor_ResizeEW;
- if (held && g.ActiveIdIsJustActivated)
- g.ActiveIdClickOffset.x -= column_w; // Store from center of column line (we used a 8 wide rect for columns clicking). This is used by GetDraggedColumnOffset().
- if (held)
- dragging_column = i;
+ if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
+ dragging_column = n;
}
- // Draw column
+ // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
const float xi = (float)(int)x;
- window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);
+ window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
}
// Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
if (dragging_column != -1)
{
- float x = GetDraggedColumnOffset(dragging_column);
+ if (!columns->IsBeingResized)
+ for (int n = 0; n < columns->Count + 1; n++)
+ columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
+ columns->IsBeingResized = is_being_resized = true;
+ float x = GetDraggedColumnOffset(columns, dragging_column);
SetColumnOffset(dragging_column, x);
}
}
+ columns->IsBeingResized = is_being_resized;
- window->DC.ColumnsSetId = 0;
- window->DC.ColumnsCurrent = 0;
- window->DC.ColumnsCount = 1;
- window->DC.ColumnsFlags = 0;
- window->DC.ColumnsData.resize(0);
+ window->DC.ColumnsSet = NULL;
window->DC.ColumnsOffsetX = 0.0f;
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX);
}
-// [2017/08: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
+// [2017/12: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
void ImGui::Columns(int columns_count, const char* id, bool border)
{
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1);
-
- if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1)
+ if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count != columns_count)
EndColumns();
ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
@@ -10557,7 +11139,7 @@ void ImGui::Indent(float indent_w)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
- window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
+ window->DC.IndentX += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
}
@@ -10565,7 +11147,7 @@ void ImGui::Unindent(float indent_w)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
- window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing;
+ window->DC.IndentX -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX;
}
@@ -10631,13 +11213,278 @@ void ImGui::Value(const char* prefix, float v, const char* float_format)
}
//-----------------------------------------------------------------------------
+// DRAG AND DROP
+//-----------------------------------------------------------------------------
+
+void ImGui::ClearDragDrop()
+{
+ ImGuiContext& g = *GImGui;
+ g.DragDropActive = false;
+ g.DragDropPayload.Clear();
+ g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
+ g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
+ g.DragDropAcceptFrameCount = -1;
+}
+
+// Call when current ID is active.
+// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
+bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags, int mouse_button)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ bool source_drag_active = false;
+ ImGuiID source_id = 0;
+ ImGuiID source_parent_id = 0;
+ if (!(flags & ImGuiDragDropFlags_SourceExtern))
+ {
+ source_id = window->DC.LastItemId;
+ if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
+ return false;
+ if (g.IO.MouseDown[mouse_button] == false)
+ return false;
+
+ if (source_id == 0)
+ {
+ // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
+ // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
+ if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
+ {
+ IM_ASSERT(0);
+ return false;
+ }
+
+ // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
+ // We build a throwaway ID based on current ID stack + relative AABB of items in window.
+ // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
+ // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
+ bool is_hovered = window->DC.LastItemRectHoveredRect;
+ if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
+ return false;
+ source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
+ if (is_hovered)
+ SetHoveredID(source_id);
+ if (is_hovered && g.IO.MouseClicked[mouse_button])
+ {
+ SetActiveID(source_id, window);
+ FocusWindow(window);
+ }
+ if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
+ g.ActiveIdAllowOverlap = is_hovered;
+ }
+ if (g.ActiveId != source_id)
+ return false;
+ source_parent_id = window->IDStack.back();
+ source_drag_active = IsMouseDragging(mouse_button);
+ }
+ else
+ {
+ window = NULL;
+ source_id = ImHash("#SourceExtern", 0);
+ source_drag_active = true;
+ }
+
+ if (source_drag_active)
+ {
+ if (!g.DragDropActive)
+ {
+ IM_ASSERT(source_id != 0);
+ ClearDragDrop();
+ ImGuiPayload& payload = g.DragDropPayload;
+ payload.SourceId = source_id;
+ payload.SourceParentId = source_parent_id;
+ g.DragDropActive = true;
+ g.DragDropSourceFlags = flags;
+ g.DragDropMouseButton = mouse_button;
+ }
+
+ if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
+ {
+ // FIXME-DRAG
+ //SetNextWindowPos(g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding);
+ //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This is better but e.g ColorButton with checkboard has issue with transparent colors :(
+ SetNextWindowPos(g.IO.MousePos);
+ PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f));
+ BeginTooltipEx(ImGuiWindowFlags_NoInputs);
+ }
+
+ if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
+ window->DC.LastItemRectHoveredRect = false;
+
+ return true;
+ }
+ return false;
+}
+
+void ImGui::EndDragDropSource()
+{
+ ImGuiContext& g = *GImGui;
+ IM_ASSERT(g.DragDropActive);
+
+ if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
+ {
+ EndTooltip();
+ PopStyleColor();
+ //PopStyleVar();
+ }
+
+ // Discard the drag if have not called SetDragDropPayload()
+ if (g.DragDropPayload.DataFrameCount == -1)
+ ClearDragDrop();
+}
+
+// Use 'cond' to choose to submit payload on drag start or every frame
+bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiPayload& payload = g.DragDropPayload;
+ if (cond == 0)
+ cond = ImGuiCond_Always;
+
+ IM_ASSERT(type != NULL);
+ IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType)); // Payload type can be at most 8 characters longs
+ IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
+ IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
+ IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
+
+ if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
+ {
+ // Copy payload
+ ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
+ g.DragDropPayloadBufHeap.resize(0);
+ if (data_size > sizeof(g.DragDropPayloadBufLocal))
+ {
+ // Store in heap
+ g.DragDropPayloadBufHeap.resize((int)data_size);
+ payload.Data = g.DragDropPayloadBufHeap.Data;
+ memcpy((void*)payload.Data, data, data_size);
+ }
+ else if (data_size > 0)
+ {
+ // Store locally
+ memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
+ payload.Data = g.DragDropPayloadBufLocal;
+ memcpy((void*)payload.Data, data, data_size);
+ }
+ else
+ {
+ payload.Data = NULL;
+ }
+ payload.DataSize = (int)data_size;
+ }
+ payload.DataFrameCount = g.FrameCount;
+
+ return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
+}
+
+bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
+{
+ ImGuiContext& g = *GImGui;
+ if (!g.DragDropActive)
+ return false;
+
+ ImGuiWindow* window = g.CurrentWindow;
+ if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
+ return false;
+ IM_ASSERT(id != 0);
+ if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
+ return false;
+
+ g.DragDropTargetRect = bb;
+ g.DragDropTargetId = id;
+ return true;
+}
+
+// We don't use BeginDragDropTargetCustom() and duplicate its code because:
+// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
+// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
+// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
+bool ImGui::BeginDragDropTarget()
+{
+ ImGuiContext& g = *GImGui;
+ if (!g.DragDropActive)
+ return false;
+
+ ImGuiWindow* window = g.CurrentWindow;
+ if (!window->DC.LastItemRectHoveredRect)
+ return false;
+ if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
+ return false;
+
+ ImGuiID id = window->DC.LastItemId;
+ if (id == 0)
+ id = window->GetIDFromRectangle(window->DC.LastItemRect);
+ if (g.DragDropPayload.SourceId == id)
+ return false;
+
+ g.DragDropTargetRect = window->DC.LastItemRect;
+ g.DragDropTargetId = id;
+ return true;
+}
+
+bool ImGui::IsDragDropPayloadBeingAccepted()
+{
+ ImGuiContext& g = *GImGui;
+ return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
+}
+
+const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiPayload& payload = g.DragDropPayload;
+ IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
+ IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
+ if (type != NULL && !payload.IsDataType(type))
+ return NULL;
+
+ // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
+ // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
+ const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
+ ImRect r = g.DragDropTargetRect;
+ float r_surface = r.GetWidth() * r.GetHeight();
+ if (r_surface < g.DragDropAcceptIdCurrRectSurface)
+ {
+ g.DragDropAcceptIdCurr = g.DragDropTargetId;
+ g.DragDropAcceptIdCurrRectSurface = r_surface;
+ }
+
+ // Render default drop visuals
+ payload.Preview = was_accepted_previously;
+ flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
+ if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
+ {
+ // FIXME-DRAG: Settle on a proper default visuals for drop target.
+ r.Expand(3.5f);
+ bool push_clip_rect = !window->ClipRect.Contains(r);
+ if (push_clip_rect) window->DrawList->PushClipRectFullScreen();
+ window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
+ if (push_clip_rect) window->DrawList->PopClipRect();
+ }
+
+ g.DragDropAcceptFrameCount = g.FrameCount;
+ payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
+ if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
+ return NULL;
+
+ return &payload;
+}
+
+// We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
+void ImGui::EndDragDropTarget()
+{
+ ImGuiContext& g = *GImGui; (void)g;
+ IM_ASSERT(g.DragDropActive);
+}
+
+//-----------------------------------------------------------------------------
// PLATFORM DEPENDENT HELPERS
//-----------------------------------------------------------------------------
#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
+#include <Windows.h>
#endif
// Win32 API clipboard implementation
@@ -10771,7 +11618,6 @@ void ImGui::ShowMetricsWindow(bool* p_open)
return;
ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList; // Render additional visuals into the top-most draw list
- overlay_draw_list->PushClipRectFullScreen();
int elem_offset = 0;
for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
{
@@ -10795,6 +11641,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
if (!pcmd_node_open)
continue;
+
+ // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
while (clipper.Step())
for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
@@ -10810,11 +11658,15 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
ImGui::Selectable(buf, false);
if (ImGui::IsItemHovered())
- overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle
+ {
+ ImDrawListFlags backup_flags = overlay_draw_list->Flags;
+ overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
+ overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
+ overlay_draw_list->Flags = backup_flags;
+ }
}
ImGui::TreePop();
}
- overlay_draw_list->PopClipRect();
ImGui::TreePop();
}
@@ -10835,8 +11687,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
if (ImGui::IsItemHovered())
GImGui->OverlayDrawList.AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255,255,0,255));
- ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y);
- ImGui::BulletText("Active: %d, Accessed: %d", window->Active, window->Accessed);
+ ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window));
+ ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed);
if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
@@ -10866,8 +11718,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
{
ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
- ImGui::Text("HoveredId: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
- ImGui::Text("ActiveId: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame);
+ ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
+ ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec)", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer);
ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
ImGui::TreePop();