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

github.com/wolfpld/tracy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/imgui
diff options
context:
space:
mode:
authorBartosz Taudul <wolf.pld@gmail.com>2020-08-20 18:53:14 +0300
committerBartosz Taudul <wolf.pld@gmail.com>2020-08-20 18:53:14 +0300
commit944625b94b76d1268b71e22d57aa91f50ad73aaf (patch)
tree64a73cfc5a75cf35c6ef324f7fb7d7a99592e2fc /imgui
parent960c7fb1b9eef5d59ce2f82e4991f7b528dd6666 (diff)
Update ImGui to 1.78 + docking.
Diffstat (limited to 'imgui')
-rw-r--r--imgui/imgui.cpp409
-rw-r--r--imgui/imgui.h187
-rw-r--r--imgui/imgui_demo.cpp454
-rw-r--r--imgui/imgui_draw.cpp340
-rw-r--r--imgui/imgui_internal.h138
-rw-r--r--imgui/imgui_widgets.cpp692
6 files changed, 1427 insertions, 793 deletions
diff --git a/imgui/imgui.cpp b/imgui/imgui.cpp
index cf5f13b5..2c1b7e70 100644
--- a/imgui/imgui.cpp
+++ b/imgui/imgui.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.77
+// dear imgui, v1.78
// (main code and documentation)
// Help:
@@ -383,6 +383,15 @@ CODE
- 2020/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
+ - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
+ replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
+ worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
+ - if you omitted the 'power' parameter (likely!), you are not affected.
+ - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
+ - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
+ see https://github.com/ocornut/imgui/issues/3361 for all details.
+ kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used.
+ - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
- 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
- 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete).
- 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
@@ -428,7 +437,7 @@ CODE
- 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
- 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
- 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
- - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value!
+ - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrary small value!
- 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
- 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
- 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
@@ -811,6 +820,7 @@ static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock
// Docking
static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
+static const float DOCKING_SPLITTER_SIZE = 2.0f;
//-------------------------------------------------------------------------
// [SECTION] FORWARD DECLARATIONS
@@ -819,7 +829,7 @@ static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For u
static void SetCurrentWindow(ImGuiWindow* window);
static void FindHoveredWindow();
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
-static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
+static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
@@ -951,6 +961,7 @@ ImGuiStyle::ImGuiStyle()
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
+ LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
TabBorderSize = 0.0f; // Thickness of border around tabs.
TabMinWidthForUnselectedCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
@@ -960,8 +971,9 @@ ImGuiStyle::ImGuiStyle()
DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
- AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
- AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
+ AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
+ AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering.
+ AntiAliasedFill = true; // Enable anti-aliased 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.
CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
@@ -989,6 +1001,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor)
ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
GrabMinSize = ImFloor(GrabMinSize * scale_factor);
GrabRounding = ImFloor(GrabRounding * scale_factor);
+ LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
TabRounding = ImFloor(TabRounding * scale_factor);
if (TabMinWidthForUnselectedCloseButton != FLT_MAX)
TabMinWidthForUnselectedCloseButton = ImFloor(TabMinWidthForUnselectedCloseButton * scale_factor);
@@ -1698,8 +1711,8 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
// Not optimal but we very rarely use this function.
int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
{
- unsigned int dummy = 0;
- return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
+ unsigned int unused = 0;
+ return ImTextCharFromUtf8(&unused, in_text, in_text_end);
}
static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
@@ -2195,7 +2208,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
*out_items_display_end = end;
}
-static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
+static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
{
// Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
// FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
@@ -2227,7 +2240,7 @@ void ImGuiListClipper::Begin(int count, float items_height)
{
ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
if (DisplayStart > 0)
- SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
+ SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
StepNo = 2;
}
}
@@ -2238,7 +2251,7 @@ void ImGuiListClipper::End()
return;
// In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
if (ItemsCount < INT_MAX)
- SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
+ SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
ItemsCount = -1;
StepNo = 3;
}
@@ -2272,7 +2285,7 @@ bool ImGuiListClipper::Step()
StepNo = 3;
return true;
}
- if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
+ if (StepNo == 2) // Step 2: empty step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
{
IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
StepNo = 3;
@@ -2977,6 +2990,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
}
g.ActiveId = id;
g.ActiveIdAllowOverlap = false;
+ g.ActiveIdNoClearOnFocusLoss = false;
g.ActiveIdWindow = window;
g.ActiveIdHasBeenEditedThisFrame = false;
if (id)
@@ -2994,7 +3008,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
void ImGui::ClearActiveID()
{
- SetActiveID(0, NULL);
+ SetActiveID(0, NULL); // g.ActiveId = 0;
}
void ImGui::SetHoveredID(ImGuiID id)
@@ -3096,7 +3110,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
return false;
- // Special handling for the dummy item after Begin() which represent the title bar or tab.
+ // Special handling for calling after Begin() which represent the title bar or tab.
// When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
if ((window->DC.LastItemId == window->ID || window->DC.LastItemId == window->MoveId) && window->WriteAccessed)
return false;
@@ -3117,10 +3131,13 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return false;
if (!IsMouseHoveringRect(bb.Min, bb.Max))
return false;
- if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
+ if (g.NavDisableMouseHover)
return false;
- if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
+ if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled))
+ {
+ g.HoveredIdDisabled = true;
return false;
+ }
// We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
// hover test in widgets code. We could also decide to split this function is two.
@@ -3153,6 +3170,15 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged
return false;
}
+// This is also inlined in ItemAdd()
+// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect!
+void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
+{
+ window->DC.LastItemId = item_id;
+ window->DC.LastItemStatusFlags = item_flags;
+ window->DC.LastItemRect = item_rect;
+}
+
// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
{
@@ -3387,7 +3413,8 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
FocusWindow(window);
SetActiveID(window->MoveId, window);
g.NavDisableHighlight = true;
- g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
+ g.ActiveIdNoClearOnFocusLoss = true;
+ g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
bool can_move_window = true;
if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
@@ -3420,15 +3447,9 @@ void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* nod
const bool clicked = IsMouseClicked(0);
const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f);
if (can_undock_node && dragging)
- {
- DockContextQueueUndockNode(&g, node);
- g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
- }
+ DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
- {
StartMouseMovingWindow(window);
- g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
- }
}
// Handle mouse moving window
@@ -3512,12 +3533,18 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
if (root_window != NULL && !is_closed_popup)
{
StartMouseMovingWindow(g.HoveredWindow);
+
+ // Cancel moving if clicked outside of title bar
if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
g.MovingWindow = NULL;
+
+ // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
+ if (g.HoveredIdDisabled)
+ g.MovingWindow = NULL;
}
- else if (root_window != NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
+ else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
{
// Clicking on void disable focus
FocusWindow(NULL);
@@ -3765,11 +3792,10 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
FindHoveredWindow();
IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
- // Modal windows prevents cursor from hovering behind them.
+ // Modal windows prevents mouse from hovering behind them.
ImGuiWindow* modal_window = GetTopMostPopupModal();
- if (modal_window)
- if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
- g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
+ if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
+ g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
// Disabled mouse?
if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
@@ -3870,6 +3896,8 @@ void ImGui::NewFrame()
g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
if (g.Style.AntiAliasedLines)
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
+ if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
+ g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
if (g.Style.AntiAliasedFill)
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
@@ -3899,6 +3927,7 @@ void ImGui::NewFrame()
g.HoveredIdPreviousFrame = g.HoveredId;
g.HoveredId = 0;
g.HoveredIdAllowOverlap = false;
+ g.HoveredIdDisabled = false;
// Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
@@ -4365,6 +4394,8 @@ static void ImGui::EndFrameDrawDimmedBackgrounds()
draw_list->PushClipRectFullScreen();
// Docking: draw modal whitening background on other nodes of a same dock tree
+ // For CTRL+TAB within a docking node we need to render the dimming background in 8 steps
+ // (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted)
if (window->RootWindowDockStop->DockIsActive)
if (window->RootWindow != window->RootWindowDockStop)
RenderRectFilledWithHole(draw_list, window->RootWindow->Rect(), window->RootWindowDockStop->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding);
@@ -4605,12 +4636,13 @@ static void FindHoveredWindow()
if (!bb.Contains(g.IO.MousePos))
continue;
+ // Support for one rectangular hole in any given window
+ // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
if (window->HitTestHoleSize.x != 0)
{
- // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
- ImRect hole_bb((float)(window->HitTestHoleOffset.x), (float)(window->HitTestHoleOffset.y),
- (float)(window->HitTestHoleOffset.x + window->HitTestHoleSize.x), (float)(window->HitTestHoleOffset.y + window->HitTestHoleSize.y));
- if (hole_bb.Contains(g.IO.MousePos - window->Pos))
+ ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
+ ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
+ if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
continue;
}
@@ -4758,6 +4790,7 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
return g.IO.MouseDoubleClicked[button];
}
+// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
// [Internal] This doesn't test if the button is pressed
bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
{
@@ -5030,7 +5063,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
{
FocusWindow(child_window);
NavInitWindow(child_window, false);
- SetActiveID(id + 1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
+ SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
g.ActiveIdSource = ImGuiInputSource_Nav;
}
return ret;
@@ -6369,7 +6402,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
// Apply scrolling
- window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
+ window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
// DRAWING
@@ -6451,6 +6484,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
+ window->ParentWorkRect = window->WorkRect;
// [LEGACY] Content Region
// FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
@@ -6560,17 +6594,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
// This is useful to allow creating context menus on title bar only, etc.
if (window->DockIsActive)
- {
- window->DC.LastItemId = window->ID;
- window->DC.LastItemStatusFlags = window->DockTabItemStatusFlags;
- window->DC.LastItemRect = window->DockTabItemRect;
- }
+ SetLastItemData(window, window->ID, window->DockTabItemStatusFlags, window->DockTabItemRect);
else
- {
- window->DC.LastItemId = window->MoveId;
- window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
- window->DC.LastItemRect = title_bar_rect;
- }
+ SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
#ifdef IMGUI_ENABLE_TEST_ENGINE
if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
@@ -6699,7 +6725,7 @@ void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* current_front_window = g.Windows.back();
- if (current_front_window == window || current_front_window->RootWindow == window)
+ if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
return;
for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
if (g.Windows[i] == window)
@@ -6756,8 +6782,9 @@ void ImGui::FocusWindow(ImGuiWindow* window)
// - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
// - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
// - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
- if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window && !active_id_window_is_dock_node_host)
- ClearActiveID();
+ if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window)
+ if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
+ ClearActiveID();
// Passing NULL allow to disable keyboard focus
if (!window)
@@ -6815,6 +6842,7 @@ void ImGui::SetCurrentFont(ImFont* font)
ImFontAtlas* atlas = g.Font->ContainerAtlas;
g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
+ g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
g.DrawListSharedData.Font = g.Font;
g.DrawListSharedData.FontSize = g.FontSize;
}
@@ -7674,6 +7702,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
#endif
}
+ // Equivalent to calling SetLastItemData()
window->DC.LastItemId = id;
window->DC.LastItemRect = bb;
window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
@@ -8041,30 +8070,20 @@ void ImGui::EndGroup()
// [SECTION] SCROLLING
//-----------------------------------------------------------------------------
-static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
+static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
{
- ImGuiContext& g = *GImGui;
ImVec2 scroll = window->Scroll;
if (window->ScrollTarget.x < FLT_MAX)
{
float cr_x = window->ScrollTargetCenterRatio.x;
float target_x = window->ScrollTarget.x;
- if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x)
- target_x = 0.0f;
- else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x)
- target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f;
scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
}
if (window->ScrollTarget.y < FLT_MAX)
{
- // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
float cr_y = window->ScrollTargetCenterRatio.y;
float target_y = window->ScrollTarget.y;
- if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
- target_y = 0.0f;
- if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y)
- target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f;
scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
}
scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
@@ -8096,7 +8115,7 @@ ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_
else if (item_rect.Max.y >= window_rect.Max.y)
SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
- ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
+ ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
delta_scroll = next_scroll - window->Scroll;
}
@@ -8157,10 +8176,10 @@ void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
window->ScrollTargetCenterRatio.y = 0.0f;
}
-
+// Note that a local position will vary depending on initial scroll value
+// We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
{
- // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
window->ScrollTargetCenterRatio.x = center_x_ratio;
@@ -8168,10 +8187,8 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x
void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
{
- // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
- const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
- local_y -= decoration_up_height;
+ local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect
window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
window->ScrollTargetCenterRatio.y = center_y_ratio;
}
@@ -8188,15 +8205,34 @@ void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
}
+// Tweak: snap on edges when aiming at an item very close to the edge,
+// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
+// When we refactor the scrolling API this may be configurable with a flag?
+// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
+static float CalcScrollSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
+{
+ if (target <= snap_min + snap_threshold)
+ return ImLerp(snap_min, target, center_ratio);
+ if (target >= snap_max - snap_threshold)
+ return ImLerp(target, snap_max, center_ratio);
+ return target;
+}
+
// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
void ImGui::SetScrollHereX(float center_x_ratio)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space
- float last_item_width = window->DC.LastItemRect.GetWidth();
- target_x += (last_item_width * center_x_ratio) + (g.Style.ItemSpacing.x * (center_x_ratio - 0.5f) * 2.0f); // Precisely aim before, in the middle or after the last item.
- SetScrollFromPosX(target_x, center_x_ratio);
+ float spacing_x = g.Style.ItemSpacing.x;
+ float target_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio);
+
+ // Tweak: snap on edges when aiming at an item very close to the edge
+ const float snap_x_threshold = ImMax(0.0f, window->WindowPadding.x - spacing_x);
+ const float snap_x_min = window->DC.CursorStartPos.x - window->WindowPadding.x;
+ const float snap_x_max = window->DC.CursorStartPos.x + window->ContentSize.x + window->WindowPadding.x;
+ target_x = CalcScrollSnap(target_x, snap_x_min, snap_x_max, snap_x_threshold, center_x_ratio);
+
+ SetScrollFromPosX(window, target_x - window->Pos.x, center_x_ratio);
}
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
@@ -8204,9 +8240,16 @@ void ImGui::SetScrollHereY(float center_y_ratio)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
- target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (g.Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
- SetScrollFromPosY(target_y, center_y_ratio);
+ float spacing_y = g.Style.ItemSpacing.y;
+ float target_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
+
+ // Tweak: snap on edges when aiming at an item very close to the edge
+ const float snap_y_threshold = ImMax(0.0f, window->WindowPadding.y - spacing_y);
+ const float snap_y_min = window->DC.CursorStartPos.y - window->WindowPadding.y;
+ const float snap_y_max = window->DC.CursorStartPos.y + window->ContentSize.y + window->WindowPadding.y;
+ target_y = CalcScrollSnap(target_y, snap_y_min, snap_y_max, snap_y_threshold, center_y_ratio);
+
+ SetScrollFromPosY(window, target_y - window->Pos.y, center_y_ratio);
}
//-----------------------------------------------------------------------------
@@ -8383,13 +8426,14 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
}
}
+// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
+// This function closes any popups that are over 'ref_window'.
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
{
ImGuiContext& g = *GImGui;
if (g.OpenPopupStack.Size == 0)
return;
- // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
// Don't close our own child popup windows.
int popup_count_to_keep = 0;
if (ref_window)
@@ -8404,13 +8448,20 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to
if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
continue;
- // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
- bool popup_or_descendent_is_ref_window = false;
- for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
- if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
+ // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
+ // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
+ // Window -> Popup1 -> Popup2 -> Popup3
+ // - Each popups may contain child windows, which is why we compare ->RootWindow!
+ // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
+ bool ref_window_is_descendent_of_popup = false;
+ for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
+ if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
if (popup_window->RootWindow == ref_window->RootWindow)
- popup_or_descendent_is_ref_window = true;
- if (!popup_or_descendent_is_ref_window)
+ {
+ ref_window_is_descendent_of_popup = true;
+ break;
+ }
+ if (!ref_window_is_descendent_of_popup)
break;
}
}
@@ -8568,6 +8619,7 @@ void ImGui::EndPopup()
g.WithinEndChild = false;
}
+// Open a popup if mouse button is released over the item
bool ImGui::OpenPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
{
ImGuiWindow* window = GImGui->CurrentWindow;
@@ -9907,6 +9959,7 @@ void ImGui::NavUpdateWindowingOverlay()
PopStyleVar();
}
+
//-----------------------------------------------------------------------------
// [SECTION] DRAG AND DROP
//-----------------------------------------------------------------------------
@@ -10096,7 +10149,8 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
return false;
ImGuiWindow* window = g.CurrentWindow;
- if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow)
+ ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
+ if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
return false;
IM_ASSERT(id != 0);
if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
@@ -10124,7 +10178,8 @@ bool ImGui::BeginDragDropTarget()
ImGuiWindow* window = g.CurrentWindow;
if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
return false;
- if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow)
+ ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
+ if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
return false;
const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
@@ -10675,14 +10730,14 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*,
int x, y;
int i;
ImU32 u1;
- if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
- else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
- else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; }
- else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2) { settings->ViewportPos = ImVec2ih((short)x, (short)y); }
- else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
- else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; }
- else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; }
- else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; }
+ if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
+ else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
+ else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; }
+ else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }
+ else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
+ else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; }
+ else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; }
+ else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; }
}
// Apply to existing windows (if any)
@@ -11602,8 +11657,6 @@ void ImGui::DestroyPlatformWindows()
// - ImGuiDockContext
//-----------------------------------------------------------------------------
-static float IMGUI_DOCK_SPLITTER_SIZE = 2.0f;
-
enum ImGuiDockRequestType
{
ImGuiDockRequestType_None = 0,
@@ -11647,7 +11700,7 @@ struct ImGuiDockPreviewData
float SplitRatio;
ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
- ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; }
+ ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_ARRAYSIZE(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); }
};
// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)
@@ -11716,7 +11769,7 @@ namespace ImGui
static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);
static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false);
static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
- static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos);
+ static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
// Settings
@@ -11855,6 +11908,16 @@ void ImGui::DockContextUpdateDocking(ImGuiContext* ctx)
if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
return;
+ // Store hovered dock node. We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.
+ g.HoveredDockNode = NULL;
+ if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)
+ {
+ if (hovered_window->DockNode)
+ g.HoveredDockNode = hovered_window->DockNode;
+ else if (hovered_window->DockNodeAsHost)
+ g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
+ }
+
// Process Docking requests
for (int n = 0; n < dc->Requests.Size; n++)
if (dc->Requests[n].Type == ImGuiDockRequestType_Dock)
@@ -12706,11 +12769,10 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind
{
ImGuiContext& g = *GImGui;
IM_ASSERT(node->WantMouseMove == true);
- ImVec2 backup_active_click_offset = g.ActiveIdClickOffset;
StartMouseMovingWindow(window);
+ g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
node->WantMouseMove = false;
- g.ActiveIdClickOffset = backup_active_click_offset;
}
// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
@@ -13227,7 +13289,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w
window->DockTabItemRect = host_window->DC.LastItemRect;
// Update navigation ID on menu layer
- if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << 1)) == 0)
+ if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
host_window->NavLastIds[1] = window->ID;
}
}
@@ -13486,22 +13548,27 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN
IM_ASSERT(ref_node_for_rect->IsVisible);
// Filter, figure out where we are allowed to dock
- ImGuiDockNodeFlags host_node_flags = host_node ? host_node->GetMergedFlags() : 0;
+ ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->GetMergedFlags() : root_payload->WindowClass.DockNodeFlagsOverrideSet;
+ ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->GetMergedFlags() : host_window->WindowClass.DockNodeFlagsOverrideSet;
data->IsCenterAvailable = true;
if (is_outer_docking)
data->IsCenterAvailable = false;
- else if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDocking))
+ else if (dst_node_flags & ImGuiDockNodeFlags_NoDocking)
data->IsCenterAvailable = false;
- else if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode())
+ else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode())
data->IsCenterAvailable = false;
else if ((!host_node || !host_node->IsEmpty()) && root_payload_as_host && root_payload_as_host->IsSplitNode() && (root_payload_as_host->OnlyNodeWithWindows == NULL)) // Is _visibly_ split?
data->IsCenterAvailable = false;
+ else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther))
+ data->IsCenterAvailable = false;
data->IsSidesAvailable = true;
- if ((host_node && (host_node_flags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit)
+ if ((dst_node_flags & ImGuiDockNodeFlags_NoSplit) || g.IO.ConfigDockingNoSplit)
data->IsSidesAvailable = false;
else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())
data->IsSidesAvailable = false;
+ else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplitMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther))
+ data->IsSidesAvailable = false;
// Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton);
@@ -13692,7 +13759,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG
parent_node->VisibleWindow = NULL;
parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;
- float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE);
+ float size_avail = (parent_node->Size[split_axis] - DOCKING_SPLITTER_SIZE);
size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);
IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
child_0->SizeRef = child_1->SizeRef = parent_node->Size;
@@ -13780,7 +13847,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si
ImVec2 child_0_size = size, child_1_size = size;
if (child_0->IsVisible && child_1->IsVisible)
{
- const float spacing = IMGUI_DOCK_SPLITTER_SIZE;
+ const float spacing = DOCKING_SPLITTER_SIZE;
const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
const float size_avail = ImMax(size[axis] - spacing, 0.0f);
@@ -13790,22 +13857,27 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si
const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
// 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
- IM_ASSERT(!(child_0->WantLockSizeOnce && child_1->WantLockSizeOnce));
- if (child_0->WantLockSizeOnce)
+ if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
{
- child_0->WantLockSizeOnce = false;
- child_0_size[axis] = child_0->SizeRef[axis] = child_0->Size[axis];
+ child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);
child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
-
}
- else if (child_1->WantLockSizeOnce)
+ else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)
{
- child_1->WantLockSizeOnce = false;
- child_1_size[axis] = child_1->SizeRef[axis] = child_1->Size[axis];
+ child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);
child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);
IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
}
+ else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)
+ {
+ // FIXME-DOCK: We cannot honor the requested size, so apply ratio.
+ // Currently this path will only be taken if code programmatically sets WantLockSizeOnce
+ float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
+ child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0);
+ child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
+ IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
+ }
// 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node
else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f)
@@ -13825,8 +13897,11 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si
child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F));
child_1_size[axis] = (size_avail - child_0_size[axis]);
}
+
child_1_pos[axis] += spacing + child_0_size[axis];
}
+ child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;
+
if (child_0->IsVisible)
DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);
if (child_1->IsVisible)
@@ -13869,7 +13944,9 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];
//if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));
- if ((child_0->GetMergedFlags() | child_1->GetMergedFlags()) & ImGuiDockNodeFlags_NoResize)
+ const ImGuiDockNodeFlags merged_flags = child_0->GetMergedFlags() | child_1->GetMergedFlags();
+ const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;
+ if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))
{
ImGuiWindow* window = g.CurrentWindow;
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
@@ -13970,13 +14047,12 @@ ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)
return NULL;
}
-ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos)
+ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
{
if (!node->IsVisible)
return NULL;
- ImGuiContext& g = *GImGui;
- const float dock_spacing = g.Style.ItemInnerSpacing.x;
+ const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?
ImRect r(node->Pos, node->Pos + node->Size);
r.Expand(dock_spacing * 0.5f);
bool inside = r.Contains(pos);
@@ -13985,20 +14061,11 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos)
if (node->IsLeafNode())
return node;
- if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos))
+ if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
return hovered_node;
- if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[1], pos))
+ if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
return hovered_node;
- // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active)
- // In this case we need to fallback into any leaf mode, possibly the central node.
- if (node->IsDockSpace() && node->IsRootNode())
- {
- if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
- return node->CentralNode;
- return DockNodeTreeFindFallbackLeafNode(node);
- }
-
return NULL;
}
@@ -14131,6 +14198,17 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla
node->OnlyNodeWithWindows = NULL;
IM_ASSERT(node->IsRootNode());
+
+ // We need to handle the rare case were a central node is missing.
+ // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.
+ // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
+ // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining.
+ // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
+ // as it doesn't make sense for an empty dockspace to not have this property.
+ if (node->IsLeafNode() && !node->IsCentralNode())
+ node->LocalFlags |= ImGuiDockNodeFlags_CentralNode;
+
+ // Update the node
DockNodeUpdate(node);
g.WithinEndChild = true;
@@ -14250,10 +14328,15 @@ void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
// For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
// - Use (id == 0) to let the system allocate a node identifier.
+// - Existing node with a same id will be removed.
ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags)
{
ImGuiContext* ctx = GImGui;
ImGuiDockNode* node = NULL;
+
+ if (id != 0)
+ DockBuilderRemoveNode(id);
+
if (flags & ImGuiDockNodeFlags_DockSpace)
{
DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
@@ -14261,10 +14344,7 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags)
}
else
{
- if (id != 0)
- node = DockContextFindNodeByID(ctx, id);
- if (!node)
- node = DockContextAddNode(ctx, id);
+ node = DockContextAddNode(ctx, id);
node->LocalFlags = flags;
}
node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame.
@@ -14806,14 +14886,19 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))
{
// Select target node
- ImGuiDockNode* node = NULL;
- bool allow_null_target_node = false;
- if (window->DockNodeAsHost)
- node = DockNodeTreeFindNodeByPos(window->DockNodeAsHost, g.IO.MousePos);
- else if (window->DockNode) // && window->DockIsActive)
- node = window->DockNode;
- else
- allow_null_target_node = true; // Dock into a regular window
+ // (we should not assume that g.HoveredDockNode is != NULL when window is a host dock node: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos)
+ ImGuiDockNode* node = g.HoveredDockNode;
+ const bool allow_null_target_node = window->DockNode == NULL && window->DockNodeAsHost == NULL;
+
+ // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active)
+ // In this case we need to fallback into any leaf mode, possibly the central node.
+ if (window->DockNodeAsHost && node && node->IsDockSpace() && node->IsRootNode())
+ {
+ if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
+ node = node->CentralNode;
+ else
+ node = DockNodeTreeFindFallbackLeafNode(node);
+ }
const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight()));
const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);
@@ -15584,12 +15669,19 @@ void ImGui::ShowMetricsWindow(bool* p_open)
NodeWindow(node->HostWindow, "HostWindow");
NodeWindow(node->VisibleWindow, "VisibleWindow");
ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
- ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : "");
+ ImGui::BulletText("Misc:%s%s%s%s%s",
+ node->IsDockSpace() ? " IsDockSpace" : "",
+ node->IsCentralNode() ? " IsCentralNode" : "",
+ (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "",
+ (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : "",
+ node->WantLockSizeOnce ? " WantLockSizeOnce" : "");
if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags))
{
ImGui::CheckboxFlags("LocalFlags: NoDocking", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoDocking);
ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit);
ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize);
+ ImGui::CheckboxFlags("LocalFlags: NoResizeX", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeX);
+ ImGui::CheckboxFlags("LocalFlags: NoResizeY", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeY);
ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar);
ImGui::CheckboxFlags("LocalFlags: HiddenTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar);
ImGui::CheckboxFlags("LocalFlags: NoWindowMenuButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton);
@@ -15744,14 +15836,17 @@ void ImGui::ShowMetricsWindow(bool* p_open)
#ifdef IMGUI_HAS_DOCK
if (ImGui::TreeNode("Dock nodes"))
{
+ static bool root_nodes_only = true;
ImGuiDockContext* dc = &g.DockContext;
+ ImGui::Checkbox("List root nodes", &root_nodes_only);
ImGui::Checkbox("Ctrl shows window dock info", &show_docking_nodes);
if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
ImGui::SameLine();
if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; }
+ ImGui::Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0);
for (int n = 0; n < dc->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
- if (node->IsRootNode())
+ if (!root_nodes_only || node->IsRootNode())
Funcs::NodeDockNode(node, "Node");
ImGui::TreePop();
}
@@ -15892,29 +15987,21 @@ void ImGui::ShowMetricsWindow(bool* p_open)
#ifdef IMGUI_HAS_DOCK
// Overlay: Display Docking info
- if (show_docking_nodes && g.IO.KeyCtrl)
- {
- ImGuiDockContext* dc = &g.DockContext;
- for (int n = 0; n < dc->Nodes.Data.Size; n++)
- if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
- {
- ImGuiDockNode* root_node = DockNodeGetRootNode(node);
- if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(root_node, g.IO.MousePos))
- if (hovered_node != node)
- continue;
- char buf[64] = "";
- char* p = buf;
- ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
- p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
- p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
- p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
- p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
- int depth = DockNodeGetDepth(node);
- overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255));
- ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
- overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
- overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
- }
+ if (show_docking_nodes && g.IO.KeyCtrl && g.HoveredDockNode)
+ {
+ char buf[64] = "";
+ char* p = buf;
+ ImGuiDockNode* node = g.HoveredDockNode;
+ ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport());
+ p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
+ p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
+ p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
+ p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
+ int depth = DockNodeGetDepth(node);
+ overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255));
+ ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
+ overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
+ overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
}
#endif // #ifdef IMGUI_HAS_DOCK
diff --git a/imgui/imgui.h b/imgui/imgui.h
index d91a343f..27829eb3 100644
--- a/imgui/imgui.h
+++ b/imgui/imgui.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.77
+// dear imgui, v1.78
// (headers)
// Help:
@@ -60,8 +60,8 @@ Index of this file:
// Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
-#define IMGUI_VERSION "1.77"
-#define IMGUI_VERSION_NUM 17701
+#define IMGUI_VERSION "1.78"
+#define IMGUI_VERSION_NUM 17800
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_VIEWPORT 1 // Viewport WIP branch
#define IMGUI_HAS_DOCK 1 // Docking WIP branch
@@ -158,8 +158,9 @@ typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A
typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling
typedef int ImDrawCornerFlags; // -> enum ImDrawCornerFlags_ // Flags: for ImDrawList::AddRect(), AddRectFilled() etc.
typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList
-typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas
+typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build
typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags
+typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for InvisibleButton()
typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc.
typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags
typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo()
@@ -171,6 +172,7 @@ typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: f
typedef int ImGuiKeyModFlags; // -> enum ImGuiKeyModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super)
typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
+typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar()
typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem()
typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader()
@@ -344,8 +346,8 @@ namespace ImGui
// Windows Scrolling
IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()]
IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()]
- IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.X - WindowSize.X
- IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y
+ IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x
+ IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y
IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()]
IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()]
IMGUI_API void SetScrollHereX(float center_x_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead.
@@ -449,7 +451,7 @@ namespace ImGui
// - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.
IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)); // button
IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text
- IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
+ IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0));
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
@@ -458,7 +460,7 @@ namespace ImGui
IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; }
IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer
IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1, 0), const char* overlay = NULL);
- IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses
+ IMGUI_API void Bullet(); // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses
// Widgets: Combo Box
// - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items.
@@ -469,44 +471,50 @@ namespace ImGui
IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0"
IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1);
- // Widgets: Drags
+ // Widgets: Drag Sliders
// - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds.
// - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
// - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
+ // - Format string may also be set to NULL or use the default format ("%f" or "%d").
// - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
// - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits.
// - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum.
- // - Use v_min > v_max to lock edits.
- IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound
- IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f);
- IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f);
- IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f);
- IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, float power = 1.0f);
- IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // If v_min >= v_max we have no bound
- IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d");
- IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d");
- IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d");
- IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL);
- IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, float power = 1.0f);
- IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, float power = 1.0f);
-
- // Widgets: Sliders
+ // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
+ // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
+ // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361
+ IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0); // If v_min >= v_max we have no bound
+ IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0); // If v_min >= v_max we have no bound
+ IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL, ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
+ IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
+
+ // Widgets: Regular Sliders
// - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds.
// - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
- IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders
- IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
- IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
- IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
- IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "%.0f deg");
- IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d");
- IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d");
- IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d");
- IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d");
- IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f);
- IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f);
- IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
- IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d");
- IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f);
+ // - Format string may also be set to NULL or use the default format ("%f" or "%d").
+ // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
+ // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361
+ IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display.
+ IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "%.0f deg", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
+ IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
+ IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
+ IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
// Widgets: Input with Keyboard
// - If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.
@@ -770,7 +778,7 @@ namespace ImGui
IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held?
IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down)
IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down)
- IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime.
+ IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true)
IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block.
IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available
IMGUI_API bool IsAnyMouseDown(); // is any mouse button held?
@@ -922,6 +930,7 @@ enum ImGuiTreeNodeFlags_
// small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags.
// It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags.
// - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0.
+// - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later).
enum ImGuiPopupFlags_
{
ImGuiPopupFlags_None = 0,
@@ -1297,6 +1306,19 @@ enum ImGuiStyleVar_
#endif
};
+// Flags for InvisibleButton() [extended in imgui_internal.h]
+enum ImGuiButtonFlags_
+{
+ ImGuiButtonFlags_None = 0,
+ ImGuiButtonFlags_MouseButtonLeft = 1 << 0, // React on left mouse button (default)
+ ImGuiButtonFlags_MouseButtonRight = 1 << 1, // React on right mouse button
+ ImGuiButtonFlags_MouseButtonMiddle = 1 << 2, // React on center mouse button
+
+ // [Internal]
+ ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle,
+ ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft
+};
+
// Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()
enum ImGuiColorEditFlags_
{
@@ -1343,6 +1365,18 @@ enum ImGuiColorEditFlags_
#endif
};
+// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
+// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
+enum ImGuiSliderFlags_
+{
+ ImGuiSliderFlags_None = 0,
+ ImGuiSliderFlags_ClampOnInput = 1 << 4, // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
+ ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
+ ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits)
+ ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget
+ ImGuiSliderFlags_InvalidMask_ = 0x7000000F // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.
+};
+
// Identify a mouse button.
// Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience.
enum ImGuiMouseButton_
@@ -1391,16 +1425,16 @@ enum ImGuiCond_
// Helpers: Memory allocations macros
// IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE()
// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax.
-// Defining a custom placement new() with a dummy parameter allows us to bypass including <new> which on some platforms complains when user has disabled exceptions.
+// Defining a custom placement new() with a custom parameter allows us to bypass including <new> which on some platforms complains when user has disabled exceptions.
//-----------------------------------------------------------------------------
-struct ImNewDummy {};
-inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; }
-inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symmetrical new()
+struct ImNewWrapper {};
+inline void* operator new(size_t, ImNewWrapper, void* ptr) { return ptr; }
+inline void operator delete(void*, ImNewWrapper, void*) {} // This is only required so we can use the symmetrical new()
#define IM_ALLOC(_SIZE) ImGui::MemAlloc(_SIZE)
#define IM_FREE(_PTR) ImGui::MemFree(_PTR)
-#define IM_PLACEMENT_NEW(_PTR) new(ImNewDummy(), _PTR)
-#define IM_NEW(_TYPE) new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE
+#define IM_PLACEMENT_NEW(_PTR) new(ImNewWrapper(), _PTR)
+#define IM_NEW(_TYPE) new(ImNewWrapper(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE
template<typename T> void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } }
//-----------------------------------------------------------------------------
@@ -1504,6 +1538,7 @@ struct ImGuiStyle
float ScrollbarRounding; // Radius of grab corners for scrollbar.
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
+ float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
float TabBorderSize; // Thickness of border around tabs.
float TabMinWidthForUnselectedCloseButton; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
@@ -1513,8 +1548,9 @@ struct ImGuiStyle
ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly!
float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later.
- bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU.
- bool AntiAliasedFill; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
+ bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
+ bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering. Latched at the beginning of the frame (copied to ImDrawList).
+ bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
float CircleSegmentMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
ImVec4 Colors[ImGuiCol_COUNT];
@@ -1739,7 +1775,7 @@ struct ImGuiWindowClass
ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!)
ImGuiDockNodeFlags DockNodeFlagsOverrideClear; // [EXPERIMENTAL]
bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar)
- bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window.
+ bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override?
ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockNodeFlagsOverrideSet = DockNodeFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; }
};
@@ -1774,6 +1810,20 @@ struct ImGuiPayload
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui
{
+ // OBSOLETED in 1.78 (from August 2020)
+ // Old drag/sliders functions that took a 'float power = 1.0' argument instead of flags
+ IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power);
+ IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power);
+ static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); }
+ static inline bool DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); }
+ static inline bool DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); }
+ static inline bool DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); }
+ IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power);
+ IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power);
+ static inline bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); }
+ static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); }
+ static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); }
+ static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); }
// OBSOLETED in 1.77 (from June 2020)
static inline bool OpenPopupOnItemClick(const char* str_id = NULL, ImGuiMouseButton mb = 1) { return OpenPopupContextItem(str_id, mb); } // Passing a mouse button to ImGuiPopupFlags is legal
static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); }
@@ -1932,7 +1982,7 @@ struct ImGuiStorage
// ImGui::Text("line number %d", i);
// - Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height (step skipped if we passed a known height as second arg to constructor).
// - Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
-// - (Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.)
+// - (Step 2: empty step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.)
// - Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
struct ImGuiListClipper
{
@@ -2000,6 +2050,11 @@ struct ImColor
// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList.
//-----------------------------------------------------------------------------
+// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking.
+#ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX
+#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (63)
+#endif
+
// ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h]
// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering,
// you can poke into the draw list for that! Draw callback may be useful for example to:
@@ -2096,12 +2151,15 @@ enum ImDrawCornerFlags_
ImDrawCornerFlags_All = 0xF // In your function calls you may use ~0 (= all bits sets) instead of ImDrawCornerFlags_All, as a convenience
};
+// Flags for ImDrawList. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.
+// It is however possible to temporarily alter flags between calls to ImDrawList:: functions.
enum ImDrawListFlags_
{
- ImDrawListFlags_None = 0,
- ImDrawListFlags_AntiAliasedLines = 1 << 0, // Lines are anti-aliased (*2 the number of triangles for 1.0f wide line, otherwise *3 the number of triangles)
- ImDrawListFlags_AntiAliasedFill = 1 << 1, // Filled shapes have anti-aliased edges (*2 the number of vertices)
- ImDrawListFlags_AllowVtxOffset = 1 << 2 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.
+ ImDrawListFlags_None = 0,
+ ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles)
+ ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require back-end to render with bilinear filtering.
+ ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles).
+ ImDrawListFlags_AllowVtxOffset = 1 << 3 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.
};
// Draw command list
@@ -2147,6 +2205,7 @@ struct ImDrawList
// Primitives
// - For rectangular primitives, "p_min" and "p_max" represent the upper-left and lower-right corners.
// - For circle primitives, use "num_segments == 0" to automatically calculate tessellation (preferred).
+ // In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12.
// In future versions we will use textures to provide cheaper and higher-quality circles.
// Use AddNgon() and AddNgonFilled() functions if you need to guaranteed a specific number of sides.
IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f);
@@ -2157,8 +2216,8 @@ struct ImDrawList
IMGUI_API void AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col);
IMGUI_API void AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness = 1.0f);
IMGUI_API void AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col);
- IMGUI_API void AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 12, float thickness = 1.0f);
- IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 12);
+ IMGUI_API void AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f);
+ IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0);
IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f);
IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments);
IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);
@@ -2316,11 +2375,13 @@ struct ImFontAtlasCustomRect
bool IsPacked() const { return X != 0xFFFF; }
};
+// Flags for ImFontAtlas build
enum ImFontAtlasFlags_
{
ImFontAtlasFlags_None = 0,
ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two
- ImFontAtlasFlags_NoMouseCursors = 1 << 1 // Don't build software mouse cursors into the atlas
+ ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory)
+ ImFontAtlasFlags_NoBakedLines = 1 << 2 // Don't build thick line textures into the atlas (save a little texture memory). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).
};
// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:
@@ -2394,7 +2455,7 @@ struct ImFontAtlas
// Note: this API may be redesigned later in order to support multi-monitor varying DPI settings.
IMGUI_API int AddCustomRectRegular(int width, int height);
IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0, 0));
- const ImFontAtlasCustomRect*GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; }
+ ImFontAtlasCustomRect* GetCustomRectByIndex(int index) { IM_ASSERT(index >= 0); return &CustomRects[index]; }
// [Internal]
IMGUI_API void CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const;
@@ -2420,8 +2481,12 @@ struct ImFontAtlas
ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel
ImVector<ImFont*> Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font.
ImVector<ImFontAtlasCustomRect> CustomRects; // Rectangles for packing custom texture data into the atlas.
- ImVector<ImFontConfig> ConfigData; // Internal data
- int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList
+ ImVector<ImFontConfig> ConfigData; // Configuration data
+ ImVec4 TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1]; // UVs for baked anti-aliased lines
+
+ // [Internal] Packing data
+ int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors
+ int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
@@ -2476,7 +2541,7 @@ struct ImFont
IMGUI_API void BuildLookupTable();
IMGUI_API void ClearOutputData();
IMGUI_API void GrowIndex(int new_size);
- IMGUI_API void AddGlyph(ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x);
+ IMGUI_API void AddGlyph(ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x);
IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built.
IMGUI_API void SetGlyphVisible(ImWchar c, bool visible);
IMGUI_API void SetFallbackChar(ImWchar c);
@@ -2601,7 +2666,7 @@ struct ImGuiPlatformIO
struct ImGuiPlatformMonitor
{
ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right)
- ImVec2 WorkPos, WorkSize; // Coordinates without task bars / side bars / menu bars. Used to avoid positioning popups/tooltips inside this region. If you don't have this info, please copy the value for MainPos/MainSize.
+ ImVec2 WorkPos, WorkSize; // Coordinates without task bars / side bars / menu bars. Used to avoid positioning popups/tooltips inside this region. If you don't have this info, please copy the value for MainPos/MainSize.
float DpiScale; // 1.0f = 96 DPI
ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0, 0); DpiScale = 1.0f; }
};
diff --git a/imgui/imgui_demo.cpp b/imgui/imgui_demo.cpp
index 5999c1e7..d4881195 100644
--- a/imgui/imgui_demo.cpp
+++ b/imgui/imgui_demo.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.77
+// dear imgui, v1.78
// (demo code)
// Help:
@@ -243,6 +243,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
static bool show_app_main_menu_bar = false;
static bool show_app_dockspace = false;
static bool show_app_documents = false;
+
static bool show_app_console = false;
static bool show_app_log = false;
static bool show_app_layout = false;
@@ -257,6 +258,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
if (show_app_main_menu_bar) ShowExampleAppMainMenuBar();
if (show_app_dockspace) ShowExampleAppDockSpace(&show_app_dockspace); // Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function)
if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); // Process the Document app next, as it may also use a DockSpace()
+
if (show_app_console) ShowExampleAppConsole(&show_app_console);
if (show_app_log) ShowExampleAppLog(&show_app_log);
if (show_app_layout) ShowExampleAppLayout(&show_app_layout);
@@ -456,6 +458,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).");
+ ImGui::Text("Also see Style->Rendering for rendering options.");
ImGui::TreePop();
ImGui::Separator();
}
@@ -681,7 +684,7 @@ static void ShowDemoWindowWidgets()
static float f1 = 0.123f, f2 = 0.0f;
ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
- ImGui::SliderFloat("slider float (curve)", &f2, -10.0f, 10.0f, "%.4f", 2.0f);
+ ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic);
static float angle = 0.0f;
ImGui::SliderAngle("slider angle", &angle);
@@ -1295,7 +1298,7 @@ static void ShowDemoWindowWidgets()
static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr));
- // Create a dummy array of contiguous float values to plot
+ // Fill an array of contiguous float values to plot
// Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
// and the sizeof() of your structure in the "stride" parameter.
static float values[90] = {};
@@ -1303,7 +1306,7 @@ static void ShowDemoWindowWidgets()
static double refresh_time = 0.0;
if (!animate || refresh_time == 0.0)
refresh_time = ImGui::GetTime();
- while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 Hz rate for the demo
+ while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
{
static float phase = 0.0f;
values[values_offset] = cosf(phase);
@@ -1403,7 +1406,7 @@ static void ShowDemoWindowWidgets()
ImGui::Text("Color button with Custom Picker Popup:");
- // Generate a dummy default palette. The palette will persist and can be edited.
+ // Generate a default palette. The palette will persist and can be edited.
static bool saved_palette_init = true;
static ImVec4 saved_palette[32] = {};
if (saved_palette_init)
@@ -1540,11 +1543,45 @@ static void ShowDemoWindowWidgets()
ImGui::TreePop();
}
+ if (ImGui::TreeNode("Drag/Slider Flags"))
+ {
+ // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
+ static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
+ ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", (unsigned int*)&flags, ImGuiSliderFlags_ClampOnInput);
+ ImGui::SameLine(); HelpMarker("Always clamp value to min/max bounds (if any) when input manually with CTRL+Click.");
+ ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", (unsigned int*)&flags, ImGuiSliderFlags_Logarithmic);
+ ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values).");
+ ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", (unsigned int*)&flags, ImGuiSliderFlags_NoRoundToFormat);
+ ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
+ ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", (unsigned int*)&flags, ImGuiSliderFlags_NoInput);
+ ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
+
+ // Drags
+ static float drag_f = 0.5f;
+ static int drag_i = 50;
+ ImGui::Text("Underlying float value: %f", drag_f);
+ ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags);
+ ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags);
+ ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags);
+ ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags);
+ ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags);
+
+ // Sliders
+ static float slider_f = 0.5f;
+ static int slider_i = 50;
+ ImGui::Text("Underlying float value: %f", slider_f);
+ ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags);
+ ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%.3f", flags);
+
+ ImGui::TreePop();
+ }
+
if (ImGui::TreeNode("Range Widgets"))
{
static float begin = 10, end = 90;
static int begin_i = 100, end_i = 1000;
- ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%");
+ ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_ClampOnInput);
+ ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units");
ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units");
ImGui::TreePop();
}
@@ -1608,34 +1645,34 @@ static void ShowDemoWindowWidgets()
ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms");
ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);
ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);
- ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f);
- ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); HelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range.");
- ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f);
- ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f);
+ ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f");
+ ImGui::DragScalar("drag float log", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", ImGuiSliderFlags_Logarithmic);
+ ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams");
+ ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic);
ImGui::Text("Sliders");
- ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d");
- ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u");
- ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d");
- ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u");
- ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d");
- ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d");
- ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d");
- ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u");
- ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u");
- ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u");
- ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d");
- ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d");
- ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d");
- ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms");
- ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms");
- ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms");
- ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one);
- ImGui::SliderScalar("slider float low^2", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", 2.0f);
- ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e");
- ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams", 1.0f);
- ImGui::SliderScalar("slider double low^2",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", 2.0f);
- ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams", 1.0f);
+ ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d");
+ ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u");
+ ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d");
+ ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u");
+ ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d");
+ ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d");
+ ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d");
+ ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u");
+ ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u");
+ ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u");
+ ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d");
+ ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d");
+ ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d");
+ ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms");
+ ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms");
+ ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms");
+ ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one);
+ ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic);
+ ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e");
+ ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams");
+ ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", ImGuiSliderFlags_Logarithmic);
+ ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams");
static bool inputs_step = true;
ImGui::Text("Inputs");
@@ -1983,8 +2020,8 @@ static void ShowDemoWindowWidgets()
if (embed_all_inside_a_child_window)
ImGui::EndChild();
- static char dummy_str[] = "This is a dummy field to be able to tab-out of the widgets above.";
- ImGui::InputText("dummy", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
+ static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above.";
+ ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly);
// Calling IsItemHovered() after begin returns the hovered status of the title bar.
// This is useful in particular if you want to create a context menu associated to the title bar of a window.
@@ -2014,7 +2051,7 @@ static void ShowDemoWindowWidgets()
static void ShowDemoWindowLayout()
{
- if (!ImGui::CollapsingHeader("Layout"))
+ if (!ImGui::CollapsingHeader("Layout & Scrolling"))
return;
if (ImGui::TreeNode("Child windows"))
@@ -2025,12 +2062,6 @@ static void ShowDemoWindowLayout()
ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel);
ImGui::Checkbox("Disable Menu", &disable_menu);
- static int line = 50;
- bool goto_line = ImGui::Button("Goto");
- ImGui::SameLine();
- ImGui::SetNextItemWidth(100);
- goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue);
-
// Child 1: no border, enable horizontal scrollbar
{
ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
@@ -2038,13 +2069,7 @@ static void ShowDemoWindowLayout()
window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
ImGui::BeginChild("ChildL", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags);
for (int i = 0; i < 100; i++)
- {
ImGui::Text("%04d: scrollable region", i);
- if (goto_line && line == i)
- ImGui::SetScrollHereY();
- }
- if (goto_line && line >= 100)
- ImGui::SetScrollHereY();
ImGui::EndChild();
}
@@ -2090,15 +2115,21 @@ static void ShowDemoWindowLayout()
// - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
// the POV of the parent window). See 'Demo->Querying Status (Active/Focused/Hovered etc.)' for details.
{
- ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10);
+ static int offset_x = 0;
+ ImGui::SetNextItemWidth(100);
+ ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000);
+
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
ImGui::BeginChild("Red", ImVec2(200, 100), true, ImGuiWindowFlags_None);
for (int n = 0; n < 50; n++)
ImGui::Text("Some test %d", n);
ImGui::EndChild();
+ bool child_is_hovered = ImGui::IsItemHovered();
ImVec2 child_rect_min = ImGui::GetItemRectMin();
ImVec2 child_rect_max = ImGui::GetItemRectMax();
ImGui::PopStyleColor();
+ ImGui::Text("Hovered: %d", child_is_hovered);
ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y);
}
@@ -2436,7 +2467,7 @@ static void ShowDemoWindowLayout()
ImGui::SameLine(0.0f, spacing);
if (ImGui::TreeNode("Node##1"))
{
- // Dummy tree data
+ // Placeholder tree data
for (int i = 0; i < 6; i++)
ImGui::BulletText("Item %d..", i);
ImGui::TreePop();
@@ -2452,7 +2483,7 @@ static void ShowDemoWindowLayout()
ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2");
if (node_open)
{
- // Dummy tree data
+ // Placeholder tree data
for (int i = 0; i < 6; i++)
ImGui::BulletText("Item %d..", i);
ImGui::TreePop();
@@ -2484,8 +2515,6 @@ static void ShowDemoWindowLayout()
static float scroll_to_pos_px = 200.0f;
ImGui::Checkbox("Decoration", &enable_extra_decorations);
- ImGui::SameLine();
- HelpMarker("We expose this for testing because scrolling sometimes had issues with window decoration such as menu-bars.");
ImGui::Checkbox("Track", &enable_track);
ImGui::PushItemWidth(100);
@@ -2552,11 +2581,9 @@ static void ShowDemoWindowLayout()
ImGui::Spacing();
HelpMarker(
"Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
- "Using the \"Scroll To Pos\" button above will make the discontinuity at edges visible: "
- "scrolling to the top/bottom/left/right-most item will add an additional WindowPadding to reflect "
- "on reaching the edge of the list.\n\nBecause the clipping rectangle of most window hides half "
- "worth of WindowPadding on the left/right, using SetScrollFromPosX(+1) will usually result in "
- "clipped text whereas the equivalent SetScrollFromPosY(+1) wouldn't.");
+ "Because the clipping rectangle of most window hides half worth of WindowPadding on the "
+ "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the "
+ "equivalent SetScrollFromPosY(+1) wouldn't.");
ImGui::PushID("##HorizontalScrolling");
for (int i = 0; i < 5; i++)
{
@@ -2746,23 +2773,66 @@ static void ShowDemoWindowLayout()
if (ImGui::TreeNode("Clipping"))
{
- static ImVec2 size(100, 100), offset(50, 20);
- ImGui::TextWrapped(
- "On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. "
- "Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. "
- "The system is designed to try minimizing both execution and CPU/GPU rendering cost.");
+ static ImVec2 size(100.0f, 100.0f);
+ static ImVec2 offset(30.0f, 30.0f);
ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f");
- ImGui::TextWrapped("(Click and drag)");
- ImVec2 pos = ImGui::GetCursorScreenPos();
- ImVec4 clip_rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
- ImGui::InvisibleButton("##dummy", size);
- if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0))
+ ImGui::TextWrapped("(Click and drag to scroll)");
+
+ for (int n = 0; n < 3; n++)
{
- offset.x += ImGui::GetIO().MouseDelta.x;
- offset.y += ImGui::GetIO().MouseDelta.y;
+ if (n > 0)
+ ImGui::SameLine();
+ ImGui::PushID(n);
+ ImGui::BeginGroup(); // Lock X position
+
+ ImGui::InvisibleButton("##empty", size);
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))
+ {
+ offset.x += ImGui::GetIO().MouseDelta.x;
+ offset.y += ImGui::GetIO().MouseDelta.y;
+ }
+ const ImVec2 p0 = ImGui::GetItemRectMin();
+ const ImVec2 p1 = ImGui::GetItemRectMax();
+ const char* text_str = "Line 1 hello\nLine 2 clip me!";
+ const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+
+ switch (n)
+ {
+ case 0:
+ HelpMarker(
+ "Using ImGui::PushClipRect():\n"
+ "Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
+ "(use this if you want your clipping rectangle to affect interactions)");
+ ImGui::PushClipRect(p0, p1, true);
+ draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
+ draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
+ ImGui::PopClipRect();
+ break;
+ case 1:
+ HelpMarker(
+ "Using ImDrawList::PushClipRect():\n"
+ "Will alter ImDrawList rendering only.\n"
+ "(use this as a shortcut if you are only using ImDrawList calls)");
+ draw_list->PushClipRect(p0, p1, true);
+ draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
+ draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
+ draw_list->PopClipRect();
+ break;
+ case 2:
+ HelpMarker(
+ "Using ImDrawList::AddText() with a fine ClipRect:\n"
+ "Will alter only this specific ImDrawList::AddText() rendering.\n"
+ "(this is often used internally to avoid altering the clipping rectangle and minimize draw calls)");
+ ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
+ draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
+ draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
+ break;
+ }
+ ImGui::EndGroup();
+ ImGui::PopID();
}
- ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(90, 90, 120, 255));
- ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x + offset.x, pos.y + offset.y), IM_COL32_WHITE, "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect);
+
ImGui::TreePop();
}
}
@@ -2934,8 +3004,8 @@ static void ShowDemoWindowPopups()
ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n");
ImGui::Separator();
- //static int dummy_i = 0;
- //ImGui::Combo("Combo", &dummy_i, "Delete\0Delete harder\0");
+ //static int unused_i = 0;
+ //ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0");
static bool dont_ask_me_next_time = false;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
@@ -2957,7 +3027,7 @@ static void ShowDemoWindowPopups()
{
if (ImGui::BeginMenu("File"))
{
- if (ImGui::MenuItem("Dummy menu item")) {}
+ if (ImGui::MenuItem("Some menu item")) {}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
@@ -2976,8 +3046,8 @@ static void ShowDemoWindowPopups()
// Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
// will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
// of the bool actually doesn't matter here.
- bool dummy_open = true;
- if (ImGui::BeginPopupModal("Stacked 2", &dummy_open))
+ bool unused_open = true;
+ if (ImGui::BeginPopupModal("Stacked 2", &unused_open))
{
ImGui::Text("Hello from Stacked The Second!");
if (ImGui::Button("Close"))
@@ -3284,7 +3354,7 @@ static void ShowDemoWindowMisc()
ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
- ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
+ ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); }
ImGui::Text("Mouse wheel: %.1f", io.MouseWheel);
@@ -3312,7 +3382,7 @@ static void ShowDemoWindowMisc()
if (ImGui::TreeNode("Tabbing"))
{
ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields.");
- static char buf[32] = "dummy";
+ static char buf[32] = "hello";
ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
ImGui::InputText("3", buf, IM_ARRAYSIZE(buf));
@@ -3787,6 +3857,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
+ ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
ImGui::Text("Alignment");
ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
@@ -3898,10 +3969,9 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
"Using those settings here will give you poor quality results.");
static float window_scale = 1.0f;
- if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f")) // Scale only this window
- ImGui::SetWindowFontScale(IM_MAX(window_scale, MIN_SCALE));
- if (ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f")) // Scale everything
- io.FontGlobalScale = IM_MAX(io.FontGlobalScale, MIN_SCALE);
+ if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_ClampOnInput)) // Scale only this window
+ ImGui::SetWindowFontScale(window_scale);
+ ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_ClampOnInput); // Scale everything
ImGui::PopItemWidth();
ImGui::EndTabItem();
@@ -3911,11 +3981,34 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
{
ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
+ ImGui::Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
+ ImGui::SameLine(); HelpMarker("Faster lines using texture data. Require back-end to render with bilinear filtering (not point/nearest filtering).");
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
ImGui::PushItemWidth(100);
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
- ImGui::DragFloat("Circle segment Max Error", &style.CircleSegmentMaxError, 0.01f, 0.10f, 10.0f, "%.2f");
+
+ // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
+ ImGui::DragFloat("Circle Segment Max Error", &style.CircleSegmentMaxError, 0.01f, 0.10f, 10.0f, "%.2f");
+ if (ImGui::IsItemActive())
+ {
+ ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
+ ImGui::BeginTooltip();
+ ImVec2 p = ImGui::GetCursorScreenPos();
+ float RAD_MIN = 10.0f, RAD_MAX = 80.0f;
+ float off_x = 10.0f;
+ for (int n = 0; n < 7; n++)
+ {
+ const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (7.0f - 1.0f);
+ ImGui::GetWindowDrawList()->AddCircle(ImVec2(p.x + off_x + rad, p.y + RAD_MAX), rad, ImGui::GetColorU32(ImGuiCol_Text), 0);
+ off_x += 10.0f + rad * 2.0f;
+ }
+ ImGui::Dummy(ImVec2(off_x, RAD_MAX * 2.0f));
+ ImGui::EndTooltip();
+ }
+ ImGui::SameLine();
+ HelpMarker("When drawing circle primitives with \"num_segments == 0\" tesselation will be calculated automatically.");
+
ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.
ImGui::PopItemWidth();
@@ -3966,7 +4059,7 @@ static void ShowExampleAppMainMenuBar()
// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
static void ShowExampleMenuFile()
{
- ImGui::MenuItem("(dummy menu)", NULL, false, false);
+ ImGui::MenuItem("(demo menu)", NULL, false, false);
if (ImGui::MenuItem("New")) {}
if (ImGui::MenuItem("Open", "Ctrl+O")) {}
if (ImGui::BeginMenu("Open Recent"))
@@ -4130,8 +4223,8 @@ struct ExampleAppConsole
// TODO: display items starting from the bottom
- if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine();
- if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine();
+ if (ImGui::SmallButton("Add Debug Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine();
+ if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine();
if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine();
bool copy_to_clipboard = ImGui::SmallButton("Copy");
//static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
@@ -4621,7 +4714,7 @@ static void ShowExampleAppLayout(bool* p_open)
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
//-----------------------------------------------------------------------------
-static void ShowDummyObject(const char* prefix, int uid)
+static void ShowPlaceholderObject(const char* prefix, int uid)
{
// Use object uid as identifier. Most commonly you could also use the object pointer as a base ID.
ImGui::PushID(uid);
@@ -4633,13 +4726,13 @@ static void ShowDummyObject(const char* prefix, int uid)
ImGui::NextColumn();
if (node_open)
{
- static float dummy_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f };
+ static float placeholder_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f };
for (int i = 0; i < 8; i++)
{
ImGui::PushID(i); // Use field index as identifier.
if (i < 2)
{
- ShowDummyObject("Child", 424242);
+ ShowPlaceholderObject("Child", 424242);
}
else
{
@@ -4650,9 +4743,9 @@ static void ShowDummyObject(const char* prefix, int uid)
ImGui::NextColumn();
ImGui::SetNextItemWidth(-1);
if (i >= 5)
- ImGui::InputFloat("##value", &dummy_members[i], 1.0f);
+ ImGui::InputFloat("##value", &placeholder_members[i], 1.0f);
else
- ImGui::DragFloat("##value", &dummy_members[i], 0.01f);
+ ImGui::DragFloat("##value", &placeholder_members[i], 0.01f);
ImGui::NextColumn();
}
ImGui::PopID();
@@ -4682,9 +4775,9 @@ static void ShowExampleAppPropertyEditor(bool* p_open)
ImGui::Columns(2);
ImGui::Separator();
- // Iterate dummy objects with dummy members (all the same data)
+ // Iterate placeholder objects (all the same data)
for (int obj_i = 0; obj_i < 3; obj_i++)
- ShowDummyObject("Object", obj_i);
+ ShowPlaceholderObject("Object", obj_i);
ImGui::Columns(1);
ImGui::Separator();
@@ -4929,31 +5022,32 @@ static void ShowExampleAppCustomRendering(bool* p_open)
// overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
// types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
// exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
- ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::BeginTabBar("##TabBar"))
{
if (ImGui::BeginTabItem("Primitives"))
{
ImGui::PushItemWidth(-ImGui::GetFontSize() * 10);
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
// Draw gradients
// (note that those are currently exacerbating our sRGB/Linear issues)
+ // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..
ImGui::Text("Gradients");
ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
{
ImVec2 p0 = ImGui::GetCursorScreenPos();
ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
- ImU32 col_a = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
- ImU32 col_b = ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
+ ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));
+ ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));
draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
ImGui::InvisibleButton("##gradient1", gradient_size);
}
{
ImVec2 p0 = ImGui::GetCursorScreenPos();
ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
- ImU32 col_a = ImGui::GetColorU32(ImVec4(0.0f, 1.0f, 0.0f, 1.0f));
- ImU32 col_b = ImGui::GetColorU32(ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
+ ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));
+ ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));
draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
ImGui::InvisibleButton("##gradient2", gradient_size);
}
@@ -4974,6 +5068,7 @@ static void ShowExampleAppCustomRendering(bool* p_open)
if (ImGui::SliderInt("Circle segments", &circle_segments_override_v, 3, 40))
circle_segments_override = true;
ImGui::ColorEdit4("Color", &colf.x);
+
const ImVec2 p = ImGui::GetCursorScreenPos();
const ImU32 col = ImColor(colf);
const float spacing = 10.0f;
@@ -4981,38 +5076,39 @@ static void ShowExampleAppCustomRendering(bool* p_open)
const ImDrawCornerFlags corners_all = ImDrawCornerFlags_All;
const ImDrawCornerFlags corners_tl_br = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight;
const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;
- float x = p.x + 4.0f, y = p.y + 4.0f;
+ float x = p.x + 4.0f;
+ float y = p.y + 4.0f;
for (int n = 0; n < 2; n++)
{
// First line uses a thickness of 1.0f, second line uses the configurable thickness
float th = (n == 0) ? 1.0f : thickness;
- draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon
- draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle
- draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, corners_none, th); x += sz + spacing; // Square
- draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_all, th); x += sz + spacing; // Square with all rounded corners
- draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners
- draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th); x += sz + spacing; // Triangle
- draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th); x += sz*0.4f + spacing; // Thin triangle
- draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
- draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
- draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line
+ draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon
+ draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle
+ draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, corners_none, th); x += sz + spacing; // Square
+ draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_all, th); x += sz + spacing; // Square with all rounded corners
+ draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners
+ draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle
+ //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle
+ draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
+ draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
+ draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line
draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col, th);
x = p.x + 4;
y += sz + spacing;
}
- draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz*0.5f, col, ngon_sides); x += sz + spacing; // N-gon
- draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments);x += sz + spacing; // Circle
- draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square
- draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners
- draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners
- draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle
- draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle
- draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness)
- draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing*2.0f; // Vertical line (faster than AddLine, but only handle integer thickness)
- draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine)
+ draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz*0.5f, col, ngon_sides); x += sz + spacing; // N-gon
+ draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments); x += sz + spacing; // Circle
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners
+ draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col); x += sz + spacing; // Triangle
+ //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness)
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness)
+ draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine)
draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255));
- ImGui::Dummy(ImVec2((sz + spacing) * 9.8f, (sz + spacing) * 3));
+ ImGui::Dummy(ImVec2((sz + spacing) * 8.8f, (sz + spacing) * 3.0f));
ImGui::PopItemWidth();
ImGui::EndTabItem();
}
@@ -5020,55 +5116,97 @@ static void ShowExampleAppCustomRendering(bool* p_open)
if (ImGui::BeginTabItem("Canvas"))
{
static ImVector<ImVec2> points;
+ static ImVec2 scrolling(0.0f, 0.0f);
+ static bool opt_enable_grid = true;
+ static bool opt_enable_context_menu = true;
static bool adding_line = false;
- if (ImGui::Button("Clear")) points.clear();
- if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } }
- ImGui::Text("Left-click and drag to add lines,\nRight-click to undo");
-
- // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use
- // IsItemHovered(). But you can also draw directly and poll mouse/keyboard by yourself.
- // You can manipulate the cursor using GetCursorPos() and SetCursorPos().
- // If you only use the ImDrawList API, you can notify the owner window of its extends with SetCursorPos(max).
- ImVec2 canvas_p = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
+
+ ImGui::Checkbox("Enable grid", &opt_enable_grid);
+ ImGui::Checkbox("Enable context menu", &opt_enable_context_menu);
+ ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
+
+ // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
+ // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
+ // To use a child window instead we could use, e.g:
+ // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
+ // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
+ // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_NoMove);
+ // ImGui::PopStyleColor();
+ // ImGui::PopStyleVar();
+ // [...]
+ // ImGui::EndChild();
+
+ // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
+ ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
- draw_list->AddRectFilledMultiColor(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255));
- draw_list->AddRect(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), IM_COL32(255, 255, 255, 255));
+ ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
- bool adding_preview = false;
- ImGui::InvisibleButton("canvas", canvas_sz);
- ImVec2 mouse_pos_global = ImGui::GetIO().MousePos;
- ImVec2 mouse_pos_canvas = ImVec2(mouse_pos_global.x - canvas_p.x, mouse_pos_global.y - canvas_p.y);
+ // Draw border and background color
+ ImGuiIO& io = ImGui::GetIO();
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));
+ draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));
+
+ // This will catch our interactions
+ ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
+ const bool is_hovered = ImGui::IsItemHovered(); // Hovered
+ const bool is_active = ImGui::IsItemActive(); // Held
+ const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
+ const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
+
+ // Add first and second point
+ if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
+ {
+ points.push_back(mouse_pos_in_canvas);
+ points.push_back(mouse_pos_in_canvas);
+ adding_line = true;
+ }
if (adding_line)
{
- adding_preview = true;
- points.push_back(mouse_pos_canvas);
- if (!ImGui::IsMouseDown(0))
- adding_line = adding_preview = false;
+ points.back() = mouse_pos_in_canvas;
+ if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
+ adding_line = false;
}
- if (ImGui::IsItemHovered())
+
+ // Pan (we use a zero mouse threshold when there's no context menu)
+ // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
+ const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;
+ if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan))
{
- if (!adding_line && ImGui::IsMouseClicked(0))
- {
- points.push_back(mouse_pos_canvas);
- adding_line = true;
- }
- if (ImGui::IsMouseClicked(1) && !points.empty())
- {
- adding_line = adding_preview = false;
- points.pop_back();
- points.pop_back();
- }
+ scrolling.x += io.MouseDelta.x;
+ scrolling.y += io.MouseDelta.y;
}
- // Draw all lines in the canvas (with a clipping rectangle so they don't stray out of it).
- draw_list->PushClipRect(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), true);
- for (int i = 0; i < points.Size - 1; i += 2)
- draw_list->AddLine(ImVec2(canvas_p.x + points[i].x, canvas_p.y + points[i].y), ImVec2(canvas_p.x + points[i + 1].x, canvas_p.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
+ // Context menu (under default mouse threshold)
+ ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
+ if (opt_enable_context_menu && ImGui::IsMouseReleased(ImGuiMouseButton_Right) && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
+ ImGui::OpenPopupContextItem("context");
+ if (ImGui::BeginPopup("context"))
+ {
+ if (adding_line)
+ points.resize(points.size() - 2);
+ adding_line = false;
+ if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); }
+ if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0)) { points.clear(); }
+ ImGui::EndPopup();
+ }
+
+ // Draw grid + all lines in the canvas
+ draw_list->PushClipRect(canvas_p0, canvas_p1, true);
+ if (opt_enable_grid)
+ {
+ const float GRID_STEP = 64.0f;
+ for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
+ draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));
+ for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
+ draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));
+ }
+ for (int n = 0; n < points.Size; n += 2)
+ draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
draw_list->PopClipRect();
- if (adding_preview)
- points.pop_back();
+
ImGui::EndTabItem();
}
@@ -5217,7 +5355,7 @@ struct MyDocument
void DoForceClose() { Open = false; Dirty = false; }
void DoSave() { Dirty = false; }
- // Display dummy contents for the Document
+ // Display placeholder contents for the Document
static void DisplayContents(MyDocument* doc)
{
ImGui::PushID(doc);
diff --git a/imgui/imgui_draw.cpp b/imgui/imgui_draw.cpp
index 7fd5e2f4..ceb9079d 100644
--- a/imgui/imgui_draw.cpp
+++ b/imgui/imgui_draw.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.77
+// dear imgui, v1.78
// (drawing and font code)
/*
@@ -364,6 +364,7 @@ ImDrawListSharedData::ImDrawListSharedData()
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
}
memset(CircleSegmentCounts, 0, sizeof(CircleSegmentCounts)); // This will be set by SetCircleSegmentMaxError()
+ TexUvLines = NULL;
}
void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error)
@@ -522,7 +523,7 @@ void ImDrawList::_OnChangedVtxOffset()
// We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this.
_VtxCurrentIdx = 0;
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
- IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset);
+ //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349
if (curr_cmd->ElemCount != 0)
{
AddDrawCmd();
@@ -587,6 +588,9 @@ void ImDrawList::PrimReserve(int idx_count, int vtx_count)
IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset))
{
+ // FIXME: In theory we should be testing that vtx_count <64k here.
+ // In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us
+ // to not make that check until we rework the text functions to handle clipping and large horizontal lines better.
_CmdHeader.VtxOffset = VtxBuffer.Size;
_OnChangedVtxOffset();
}
@@ -672,25 +676,38 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
return;
const ImVec2 opaque_uv = _Data->TexUvWhitePixel;
- int count = points_count;
- if (!closed)
- count = points_count - 1;
-
+ const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw
const bool thick_line = (thickness > 1.0f);
+
if (Flags & ImDrawListFlags_AntiAliasedLines)
{
// Anti-aliased stroke
const float AA_SIZE = 1.0f;
const ImU32 col_trans = col & ~IM_COL32_A_MASK;
- const int idx_count = thick_line ? count * 18 : count * 12;
- const int vtx_count = thick_line ? points_count * 4 : points_count * 3;
+ // Thicknesses <1.0 should behave like thickness 1.0
+ thickness = ImMax(thickness, 1.0f);
+ const int integer_thickness = (int)thickness;
+ const float fractional_thickness = thickness - integer_thickness;
+
+ // Do we want to draw this line using a texture?
+ // - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved.
+ // - If AA_SIZE is not 1.0f we cannot use the texture path.
+ const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f);
+
+ // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off
+ IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
+
+ const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);
+ const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);
PrimReserve(idx_count, vtx_count);
// Temporary buffer
- ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); //-V630
+ // The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point
+ ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630
ImVec2* temp_points = temp_normals + points_count;
+ // Calculate normals (tangents) for each line segment
for (int i1 = 0; i1 < count; i1++)
{
const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
@@ -703,59 +720,111 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
if (!closed)
temp_normals[points_count - 1] = temp_normals[points_count - 2];
- if (!thick_line)
+ // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point
+ if (use_texture || !thick_line)
{
+ // [PATH 1] Texture-based lines (thick or non-thick)
+ // [PATH 2] Non texture-based lines (non-thick)
+
+ // The width of the geometry we need to draw - this is essentially <thickness> pixels for the line itself, plus "one pixel" for AA.
+ // - In the texture-based path, we don't use AA_SIZE here because the +1 is tied to the generated texture
+ // (see ImFontAtlasBuildRenderLinesTexData() function), and so alternate values won't work without changes to that code.
+ // - In the non texture-based paths, we would allow AA_SIZE to potentially be != 1.0f with a patch (e.g. fringe_scale patch to
+ // allow scaling geometry while preserving one-screen-pixel AA fringe).
+ const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE;
+
+ // If line is not closed, the first and last points need to be generated differently as there are no normals to blend
if (!closed)
{
- temp_points[0] = points[0] + temp_normals[0] * AA_SIZE;
- temp_points[1] = points[0] - temp_normals[0] * AA_SIZE;
- temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE;
- temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE;
+ temp_points[0] = points[0] + temp_normals[0] * half_draw_size;
+ temp_points[1] = points[0] - temp_normals[0] * half_draw_size;
+ temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size;
+ temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size;
}
+ // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges
+ // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)
// FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.
- unsigned int idx1 = _VtxCurrentIdx;
- for (int i1 = 0; i1 < count; i1++)
+ unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment
+ for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment
{
- const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
- unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3;
+ const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment
+ const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment
// Average normals
float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
IM_FIXNORMAL2F(dm_x, dm_y);
- dm_x *= AA_SIZE;
- dm_y *= AA_SIZE;
+ dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area
+ dm_y *= half_draw_size;
- // Add temporary vertices
+ // Add temporary vertexes for the outer edges
ImVec2* out_vtx = &temp_points[i2 * 2];
out_vtx[0].x = points[i2].x + dm_x;
out_vtx[0].y = points[i2].y + dm_y;
out_vtx[1].x = points[i2].x - dm_x;
out_vtx[1].y = points[i2].y - dm_y;
- // Add indexes
- _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
- _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0);
- _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0);
- _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1);
- _IdxWritePtr += 12;
+ if (use_texture)
+ {
+ // Add indices for two triangles
+ _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri
+ _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri
+ _IdxWritePtr += 6;
+ }
+ else
+ {
+ // Add indexes for four triangles
+ _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1
+ _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2
+ _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1
+ _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2
+ _IdxWritePtr += 12;
+ }
idx1 = idx2;
}
- // Add vertices
- for (int i = 0; i < points_count; i++)
+ // Add vertexes for each point on the line
+ if (use_texture)
{
- _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;
- _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans;
- _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans;
- _VtxWritePtr += 3;
+ // If we're using textures we only need to emit the left/right edge vertices
+ ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness];
+ if (fractional_thickness != 0.0f)
+ {
+ const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1];
+ tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp()
+ tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness;
+ tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness;
+ tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness;
+ }
+ ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y);
+ ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w);
+ for (int i = 0; i < points_count; i++)
+ {
+ _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge
+ _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge
+ _VtxWritePtr += 2;
+ }
+ }
+ else
+ {
+ // If we're not using a texture, we need the center vertex as well
+ for (int i = 0; i < points_count; i++)
+ {
+ _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col; // Center of line
+ _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge
+ _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge
+ _VtxWritePtr += 3;
+ }
}
}
else
{
+ // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point
const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;
+
+ // If line is not closed, the first and last points need to be generated differently as there are no normals to blend
if (!closed)
{
const int points_last = points_count - 1;
@@ -769,9 +838,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE);
}
+ // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges
+ // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)
// FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.
- unsigned int idx1 = _VtxCurrentIdx;
- for (int i1 = 0; i1 < count; i1++)
+ unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment
+ for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment
{
const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment
const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment
@@ -822,7 +893,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
}
else
{
- // Non Anti-aliased Stroke
+ // [PATH 4] Non texture-based, Non anti-aliased lines
const int idx_count = count * 6;
const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges
PrimReserve(idx_count, vtx_count);
@@ -1176,7 +1247,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
if (num_segments == 12)
- PathArcToFast(center, radius - 0.5f, 0, 12);
+ PathArcToFast(center, radius - 0.5f, 0, 12 - 1);
else
PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
PathStroke(col, true, thickness);
@@ -1206,7 +1277,7 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col,
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
if (num_segments == 12)
- PathArcToFast(center, radius, 0, 12);
+ PathArcToFast(center, radius, 0, 12 - 1);
else
PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);
PathFillConvex(col);
@@ -1602,10 +1673,10 @@ ImFontConfig::ImFontConfig()
//-----------------------------------------------------------------------------
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
-// The white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.
-const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 108;
-const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;
-static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =
+// The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.
+const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 108; // Actual texture will be 2 times that + 1 spacing.
+const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;
+static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =
{
"..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX "
"..- -X.....X- X.X - X.X -X.....X - X.....X- X..X "
@@ -1662,8 +1733,7 @@ ImFontAtlas::ImFontAtlas()
TexWidth = TexHeight = 0;
TexUvScale = ImVec2(0.0f, 0.0f);
TexUvWhitePixel = ImVec2(0.0f, 0.0f);
- for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++)
- CustomRectIds[n] = -1;
+ PackIdMouseCursors = PackIdLines = -1;
}
ImFontAtlas::~ImFontAtlas()
@@ -1691,8 +1761,7 @@ void ImFontAtlas::ClearInputData()
}
ConfigData.clear();
CustomRects.clear();
- for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++)
- CustomRectIds[n] = -1;
+ PackIdMouseCursors = PackIdLines = -1;
}
void ImFontAtlas::ClearTexData()
@@ -1932,15 +2001,15 @@ bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* ou
if (Flags & ImFontAtlasFlags_NoMouseCursors)
return false;
- IM_ASSERT(CustomRectIds[0] != -1);
- ImFontAtlasCustomRect& r = CustomRects[CustomRectIds[0]];
- ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r.X, (float)r.Y);
+ IM_ASSERT(PackIdMouseCursors != -1);
+ ImFontAtlasCustomRect* r = GetCustomRectByIndex(PackIdMouseCursors);
+ ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r->X, (float)r->Y);
ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1];
*out_size = size;
*out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2];
out_uv_border[0] = (pos) * TexUvScale;
out_uv_border[1] = (pos + size) * TexUvScale;
- pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
+ pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;
out_uv_fill[0] = (pos) * TexUvScale;
out_uv_fill[1] = (pos + size) * TexUvScale;
return true;
@@ -2222,8 +2291,11 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
if (src_tmp.GlyphsCount == 0)
continue;
+ // When merging fonts with MergeMode=true:
+ // - We can have multiple input fonts writing into a same destination font.
+ // - dst_font->ConfigData is != from cfg which is our source configuration.
ImFontConfig& cfg = atlas->ConfigData[src_i];
- ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
+ ImFont* dst_font = cfg.DstFont;
const float font_scale = stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels);
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
@@ -2237,20 +2309,13 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
{
+ // Register glyph
const int codepoint = src_tmp.GlyphsList[glyph_i];
const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i];
-
- const float char_advance_x_org = pc.xadvance;
- const float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
- float char_off_x = font_off_x;
- if (char_advance_x_org != char_advance_x_mod)
- char_off_x += cfg.PixelSnapH ? ImFloor((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f;
-
- // Register glyph
stbtt_aligned_quad q;
- float dummy_x = 0.0f, dummy_y = 0.0f;
- stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &dummy_x, &dummy_y, &q, 0);
- dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod);
+ float unused_x = 0.0f, unused_y = 0.0f;
+ stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0);
+ dst_font->AddGlyph(&cfg, (ImWchar)codepoint, q.x0 + font_off_x, q.y0 + font_off_y, q.x1 + font_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance);
}
}
@@ -2262,17 +2327,6 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
return true;
}
-// Register default custom rectangles (this is called/shared by both the stb_truetype and the FreeType builder)
-void ImFontAtlasBuildInit(ImFontAtlas* atlas)
-{
- if (atlas->CustomRectIds[0] >= 0)
- return;
- if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors))
- atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H);
- else
- atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(2, 2);
-}
-
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
{
if (!font_config->MergeMode)
@@ -2280,6 +2334,7 @@ void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* f
font->ClearOutputData();
font->FontSize = font_config->SizePixels;
font->ConfigData = font_config;
+ font->ConfigDataCount = 0;
font->ContainerAtlas = atlas;
font->Ascent = ascent;
font->Descent = descent;
@@ -2314,52 +2369,113 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa
}
}
+void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value)
+{
+ IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth);
+ IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight);
+ unsigned char* out_pixel = atlas->TexPixelsAlpha8 + x + (y * atlas->TexWidth);
+ for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w)
+ for (int off_x = 0; off_x < w; off_x++)
+ out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : 0x00;
+}
+
static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
{
- IM_ASSERT(atlas->CustomRectIds[0] >= 0);
- IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
- ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->CustomRectIds[0]];
- IM_ASSERT(r.IsPacked());
+ ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors);
+ IM_ASSERT(r->IsPacked());
const int w = atlas->TexWidth;
if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors))
{
// Render/copy pixels
- IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1 && r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);
- for (int y = 0, n = 0; y < FONT_ATLAS_DEFAULT_TEX_DATA_H; y++)
- for (int x = 0; x < FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF; x++, n++)
- {
- const int offset0 = (int)(r.X + x) + (int)(r.Y + y) * w;
- const int offset1 = offset0 + FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
- atlas->TexPixelsAlpha8[offset0] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == '.' ? 0xFF : 0x00;
- atlas->TexPixelsAlpha8[offset1] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == 'X' ? 0xFF : 0x00;
- }
+ IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);
+ const int x_for_white = r->X;
+ const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;
+ ImFontAtlasBuildRender1bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF);
+ ImFontAtlasBuildRender1bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF);
}
else
{
- IM_ASSERT(r.Width == 2 && r.Height == 2);
- const int offset = (int)(r.X) + (int)(r.Y) * w;
+ // Render 4 white pixels
+ IM_ASSERT(r->Width == 2 && r->Height == 2);
+ const int offset = (int)r->X + (int)r->Y * w;
atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF;
}
- atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * atlas->TexUvScale.x, (r.Y + 0.5f) * atlas->TexUvScale.y);
+ atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y);
+}
+
+static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
+{
+ if (atlas->Flags & ImFontAtlasFlags_NoBakedLines)
+ return;
+
+ // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them
+ ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdLines);
+ IM_ASSERT(r->IsPacked());
+ for (unsigned int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row
+ {
+ // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle
+ unsigned int y = n;
+ unsigned int line_width = n;
+ unsigned int pad_left = (r->Width - line_width) / 2;
+ unsigned int pad_right = r->Width - (pad_left + line_width);
+
+ // Write each slice
+ IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels
+ unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)];
+ memset(write_ptr, 0x00, pad_left);
+ memset(write_ptr + pad_left, 0xFF, line_width);
+ memset(write_ptr + pad_left + line_width, 0x00, pad_right);
+
+ // Calculate UVs for this line
+ ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale;
+ ImVec2 uv1 = ImVec2((float)(r->X + pad_left + line_width + 1), (float)(r->Y + y + 1)) * atlas->TexUvScale;
+ float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts
+ atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v);
+ }
}
+// Note: this is called / shared by both the stb_truetype and the FreeType builder
+void ImFontAtlasBuildInit(ImFontAtlas* atlas)
+{
+ // Register texture region for mouse cursors or standard white pixels
+ if (atlas->PackIdMouseCursors < 0)
+ {
+ if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors))
+ atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H);
+ else
+ atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2);
+ }
+
+ // Register texture region for thick lines
+ // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row
+ if (atlas->PackIdLines < 0)
+ {
+ if (!(atlas->Flags & ImFontAtlasFlags_NoBakedLines))
+ atlas->PackIdLines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1);
+ }
+}
+
+// This is called/shared by both the stb_truetype and the FreeType builder.
void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
{
- // Render into our custom data block
+ // Render into our custom data blocks
+ IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
ImFontAtlasBuildRenderDefaultTexData(atlas);
+ ImFontAtlasBuildRenderLinesTexData(atlas);
// Register custom rectangle glyphs
for (int i = 0; i < atlas->CustomRects.Size; i++)
{
- const ImFontAtlasCustomRect& r = atlas->CustomRects[i];
- if (r.Font == NULL || r.GlyphID == 0)
+ const ImFontAtlasCustomRect* r = &atlas->CustomRects[i];
+ if (r->Font == NULL || r->GlyphID == 0)
continue;
- IM_ASSERT(r.Font->ContainerAtlas == atlas);
+ // Will ignore ImFontConfig settings: GlyphMinAdvanceX, GlyphMinAdvanceY, GlyphExtraSpacing, PixelSnapH
+ IM_ASSERT(r->Font->ContainerAtlas == atlas);
ImVec2 uv0, uv1;
- atlas->CalcCustomRectUV(&r, &uv0, &uv1);
- r.Font->AddGlyph((ImWchar)r.GlyphID, r.GlyphOffset.x, r.GlyphOffset.y, r.GlyphOffset.x + r.Width, r.GlyphOffset.y + r.Height, uv0.x, uv0.y, uv1.x, uv1.y, r.GlyphAdvanceX);
+ atlas->CalcCustomRectUV(r, &uv0, &uv1);
+ r->Font->AddGlyph(NULL, (ImWchar)r->GlyphID, r->GlyphOffset.x, r->GlyphOffset.y, r->GlyphOffset.x + r->Width, r->GlyphOffset.y + r->Height, uv0.x, uv0.y, uv1.x, uv1.y, r->GlyphAdvanceX);
}
// Build all fonts lookup tables
@@ -2769,8 +2885,29 @@ void ImFont::GrowIndex(int new_size)
// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.
// Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis).
-void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x)
+// 'cfg' is not necessarily == 'this->ConfigData' because multiple source fonts+configs can be used to build one target font.
+void ImFont::AddGlyph(ImFontConfig* cfg, ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x)
{
+ if (cfg != NULL)
+ {
+ // Clamp & recenter if needed
+ const float advance_x_original = advance_x;
+ advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX);
+ if (advance_x != advance_x_original)
+ {
+ float char_off_x = cfg->PixelSnapH ? ImFloor((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;
+ x0 += char_off_x;
+ x1 += char_off_x;
+ }
+
+ // Snap to pixel
+ if (cfg->PixelSnapH)
+ advance_x = IM_ROUND(advance_x);
+
+ // Bake spacing
+ advance_x += cfg->GlyphExtraSpacing.x;
+ }
+
Glyphs.resize(Glyphs.Size + 1);
ImFontGlyph& glyph = Glyphs.back();
glyph.Codepoint = (unsigned int)codepoint;
@@ -2783,14 +2920,13 @@ void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1,
glyph.V0 = v0;
glyph.U1 = u1;
glyph.V1 = v1;
- glyph.AdvanceX = advance_x + ConfigData->GlyphExtraSpacing.x; // Bake spacing into AdvanceX
-
- if (ConfigData->PixelSnapH)
- glyph.AdvanceX = IM_ROUND(glyph.AdvanceX);
+ glyph.AdvanceX = advance_x;
// Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round)
+ // We use (U1-U0)*TexWidth instead of X1-X0 to account for oversampling.
+ float pad = ContainerAtlas->TexGlyphPadding + 0.99f;
DirtyLookupTables = true;
- MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + 1.99f);
+ MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + pad) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + pad);
}
void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)
@@ -3399,8 +3535,6 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
draw_list->PathFillConvex(col);
}
-// For CTRL+TAB within a docking node we need to render the dimming background in 8 steps
-// (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted)
void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding)
{
const bool fill_L = (inner.Min.x > outer.Min.x);
diff --git a/imgui/imgui_internal.h b/imgui/imgui_internal.h
index 16b41d11..3f769602 100644
--- a/imgui/imgui_internal.h
+++ b/imgui/imgui_internal.h
@@ -1,4 +1,4 @@
-// dear imgui, v1.77
+// dear imgui, v1.78
// (internal structures/api)
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
@@ -100,7 +100,7 @@ struct ImGuiDockNode; // Docking system node (hold a list of Windo
struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session)
struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup()
struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box
-struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() internal data
+struct ImGuiLastItemDataBackup; // Backup and restore IsItemHovered() internal data
struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only
struct ImGuiNavMoveResult; // Result of a gamepad/keyboard directional navigation move query result
struct ImGuiNextWindowData; // Storage for SetNextWindow** functions
@@ -119,7 +119,6 @@ typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // E
typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical
typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior()
typedef int ImGuiColumnsFlags; // -> enum ImGuiColumnsFlags_ // Flags: BeginColumns()
-typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior()
typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag()
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
@@ -128,7 +127,6 @@ typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // F
typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions
typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions
typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx()
-typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior()
typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx()
typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx()
@@ -360,6 +358,12 @@ IMGUI_API void* ImFileLoadToMemory(const char* filename, const char*
#define ImCeil(X) ceilf(X)
static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
static inline double ImPow(double x, double y) { return pow(x, y); }
+static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
+static inline double ImLog(double x) { return log(x); }
+static inline float ImAbs(float x) { return fabsf(x); }
+static inline double ImAbs(double x) { return fabs(x); }
+static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument
+static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : ((x > 0.0) ? 1.0 : 0.0); }
#endif
// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
// (Exceptionally using templates here but we could also redefine them for those types)
@@ -560,6 +564,7 @@ struct IMGUI_API ImDrawListSharedData
// [Internal] Lookup tables
ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas
ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius (array index + 1) before we calculate it dynamically (to avoid calculation overhead)
+ const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
ImDrawListSharedData();
void SetCircleSegmentMaxError(float max_error);
@@ -590,6 +595,7 @@ enum ImGuiItemFlags_
ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false
ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window
ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
+ ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags_Default_ = 0
};
@@ -614,46 +620,34 @@ enum ImGuiItemStatusFlags_
#endif
};
-enum ImGuiButtonFlags_
-{
- ImGuiButtonFlags_None = 0,
- ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat
- ImGuiButtonFlags_PressedOnClick = 1 << 1, // return true on click (mouse down event)
- ImGuiButtonFlags_PressedOnClickRelease = 1 << 2, // [Default] return true on click + release on same item <-- this is what the majority of Button are using
- ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 3, // return true on click + release even if the release event is not done while hovering the item
- ImGuiButtonFlags_PressedOnRelease = 1 << 4, // return true on release (default requires click+release)
- ImGuiButtonFlags_PressedOnDoubleClick = 1 << 5, // return true on double-click (default requires click+release)
- ImGuiButtonFlags_PressedOnDragDropHold = 1 << 6, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)
- ImGuiButtonFlags_FlattenChildren = 1 << 7, // allow interactions even if a child window is overlapping
- ImGuiButtonFlags_AllowItemOverlap = 1 << 8, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap()
- ImGuiButtonFlags_DontClosePopups = 1 << 9, // disable automatically closing parent popup on press // [UNUSED]
- ImGuiButtonFlags_Disabled = 1 << 10, // disable interactions
- ImGuiButtonFlags_AlignTextBaseLine = 1 << 11, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
- ImGuiButtonFlags_NoKeyModifiers = 1 << 12, // disable mouse interaction if a key modifier is held
- ImGuiButtonFlags_NoHoldingActiveId = 1 << 13, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)
- ImGuiButtonFlags_NoNavFocus = 1 << 14, // don't override navigation focus when activated
- ImGuiButtonFlags_NoHoveredOnFocus = 1 << 15, // don't report as hovered when nav focus is on this item
- ImGuiButtonFlags_MouseButtonLeft = 1 << 16, // [Default] react on left mouse button
- ImGuiButtonFlags_MouseButtonRight = 1 << 17, // react on right mouse button
- ImGuiButtonFlags_MouseButtonMiddle = 1 << 18, // react on center mouse button
-
- ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle,
- ImGuiButtonFlags_MouseButtonShift_ = 16,
- ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft,
+// Extend ImGuiButtonFlags_
+enum ImGuiButtonFlagsPrivate_
+{
+ ImGuiButtonFlags_PressedOnClick = 1 << 4, // return true on click (mouse down event)
+ ImGuiButtonFlags_PressedOnClickRelease = 1 << 5, // [Default] return true on click + release on same item <-- this is what the majority of Button are using
+ ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item
+ ImGuiButtonFlags_PressedOnRelease = 1 << 7, // return true on release (default requires click+release)
+ ImGuiButtonFlags_PressedOnDoubleClick = 1 << 8, // return true on double-click (default requires click+release)
+ ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)
+ ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat
+ ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping
+ ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap()
+ ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED]
+ ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions
+ ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
+ ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held
+ ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)
+ ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated
+ ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item
ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,
ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease
};
-enum ImGuiSliderFlags_
+// Extend ImGuiSliderFlags_
+enum ImGuiSliderFlagsPrivate_
{
- ImGuiSliderFlags_None = 0,
- ImGuiSliderFlags_Vertical = 1 << 0
-};
-
-enum ImGuiDragFlags_
-{
- ImGuiDragFlags_None = 0,
- ImGuiDragFlags_Vertical = 1 << 0
+ ImGuiSliderFlags_Vertical = 1 << 20, // Should this slider be orientated vertically?
+ ImGuiSliderFlags_ReadOnly = 1 << 21
};
// Extend ImGuiSelectableFlags_
@@ -1040,7 +1034,7 @@ struct ImGuiColumns
float HostCursorMaxPosX; // Backup of CursorMaxPos at the time of BeginColumns()
ImRect HostInitialClipRect; // Backup of ClipRect at the time of BeginColumns()
ImRect HostBackupClipRect; // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground()
- ImRect HostWorkRect; // Backup of WorkRect at the time of BeginColumns()
+ ImRect HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns()
ImVector<ImGuiColumnData> Columns;
ImDrawListSplitter Splitter;
@@ -1086,10 +1080,17 @@ enum ImGuiDockNodeFlagsPrivate_
ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local, Saved // Disable window/docking menu (that one that appears instead of the collapse button)
ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Local, Saved //
ImGuiDockNodeFlags_NoDocking = 1 << 16, // Local, Saved // Disable any form of docking in this dockspace or individual node. (On a whole dockspace, this pretty much defeat the purpose of using a dockspace at all). Note: when turned on, existing docked nodes will be preserved.
+ ImGuiDockNodeFlags_NoDockingSplitMe = 1 << 17, // [EXPERIMENTAL] Prevent another window/node from splitting this node.
+ ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 18, // [EXPERIMENTAL] Prevent this node from splitting another window/node.
+ ImGuiDockNodeFlags_NoDockingOverMe = 1 << 19, // [EXPERIMENTAL] Prevent another window/node to be docked over this node.
+ ImGuiDockNodeFlags_NoDockingOverOther = 1 << 20, // [EXPERIMENTAL] Prevent this node to be docked over another window/node.
+ ImGuiDockNodeFlags_NoResizeX = 1 << 21, // [EXPERIMENTAL]
+ ImGuiDockNodeFlags_NoResizeY = 1 << 22, // [EXPERIMENTAL]
ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0,
- ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking,
+ ImGuiDockNodeFlags_NoResizeFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY,
+ ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking,
ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace, // When splitting those flags are moved to the inheriting child, never duplicated
- ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking
+ ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking
};
// Store the source authority (dock node vs window) of a field
@@ -1291,18 +1292,20 @@ struct ImGuiContext
ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow*
int WindowsActiveCount; // Number of unique windows submitted by frame
ImGuiWindow* CurrentWindow; // Window being drawn into
- ImGuiWindow* HoveredWindow; // Will catch mouse inputs
- ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only)
+ ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs.
+ ImGuiWindow* HoveredRootWindow; // == HoveredWindow ? HoveredWindow->RootWindow : NULL, merely a shortcut to avoid null test in some situation.
ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
- ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow.
+ ImGuiDockNode* HoveredDockNode;
+ ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow.
ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
ImVec2 WheelingWindowRefMousePos;
float WheelingWindowTimer;
// Item/widgets state and tracking information
ImGuiID HoveredId; // Hovered widget
- bool HoveredIdAllowOverlap;
ImGuiID HoveredIdPreviousFrame;
+ bool HoveredIdAllowOverlap;
+ bool HoveredIdDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.
float HoveredIdTimer; // Measure contiguous hovering time
float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active
ImGuiID ActiveId; // Active widget
@@ -1310,6 +1313,7 @@ struct ImGuiContext
float ActiveIdTimer;
bool ActiveIdIsJustActivated; // Set at the time of activation for one frame
bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)
+ bool ActiveIdNoClearOnFocusLoss; // Disable losing active id if the active id window gets unfocused.
bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state.
bool ActiveIdHasBeenEditedThisFrame;
@@ -1443,6 +1447,8 @@ struct ImGuiContext
float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips
float ColorEditLastColor[3];
ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker.
+ float SliderCurrentAccum; // Accumulated slider delta when using navigation controls.
+ bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it?
bool DragCurrentAccumDirty;
float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings
float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
@@ -1512,19 +1518,21 @@ struct ImGuiContext
HoveredWindow = NULL;
HoveredRootWindow = NULL;
HoveredWindowUnderMovingWindow = NULL;
+ HoveredDockNode = NULL;
MovingWindow = NULL;
WheelingWindow = NULL;
WheelingWindowTimer = 0.0f;
- HoveredId = 0;
+ HoveredId = HoveredIdPreviousFrame = 0;
HoveredIdAllowOverlap = false;
- HoveredIdPreviousFrame = 0;
+ HoveredIdDisabled = false;
HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
ActiveId = 0;
ActiveIdIsAlive = 0;
ActiveIdTimer = 0.0f;
ActiveIdIsJustActivated = false;
ActiveIdAllowOverlap = false;
+ ActiveIdNoClearOnFocusLoss = false;
ActiveIdHasBeenPressedBefore = false;
ActiveIdHasBeenEditedBefore = false;
ActiveIdHasBeenEditedThisFrame = false;
@@ -1605,6 +1613,8 @@ struct ImGuiContext
ColorEditOptions = ImGuiColorEditFlags__OptionsDefault;
ColorEditLastHue = ColorEditLastSat = 0.0f;
ColorEditLastColor[0] = ColorEditLastColor[1] = ColorEditLastColor[2] = FLT_MAX;
+ SliderCurrentAccum = 0.0f;
+ SliderCurrentAccumDirty = false;
DragCurrentAccumDirty = false;
DragCurrentAccum = 0.0f;
DragSpeedDefaultRatio = 1.0f / 100.0f;
@@ -1798,10 +1808,12 @@ struct IMGUI_API ImGuiWindow
ImRect OuterRectClipped; // == Window->Rect() just after setup in Begin(). == window->Rect() for root window.
ImRect InnerRect; // Inner rectangle (omit title bar, menu bar, scroll bar)
ImRect InnerClipRect; // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect.
- ImRect WorkRect; // Cover the whole scrolling region, shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward).
+ ImRect WorkRect; // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward).
+ ImRect ParentWorkRect; // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack?
ImRect ClipRect; // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back().
ImRect ContentRegionRect; // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on.
- ImVec2ih HitTestHoleSize, HitTestHoleOffset;
+ ImVec2ih HitTestHoleSize; // Define an optional rectangular hole where mouse will pass-through the window.
+ ImVec2ih HitTestHoleOffset;
int LastFrameActive; // Last frame number the window was Active.
int LastFrameJustFocused; // Last frame number the window was made Focused.
@@ -1816,7 +1828,7 @@ struct IMGUI_API ImGuiWindow
ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)
ImDrawList DrawListInst;
ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL.
- ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window.
+ ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window == Top-level window.
ImGuiWindow* RootWindowDockStop; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state.
ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag.
@@ -1862,14 +1874,14 @@ public:
};
// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data.
-struct ImGuiItemHoveredDataBackup
+struct ImGuiLastItemDataBackup
{
ImGuiID LastItemId;
ImGuiItemStatusFlags LastItemStatusFlags;
ImRect LastItemRect;
ImRect LastItemDisplayRect;
- ImGuiItemHoveredDataBackup() { Backup(); }
+ ImGuiLastItemDataBackup() { Backup(); }
void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; }
void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; }
};
@@ -1902,12 +1914,13 @@ struct ImGuiTabItem
ImGuiWindow* Window; // When TabItem is part of a DockNode's TabBar, we hold on to a window.
int LastFrameVisible;
int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance
- int NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames
float Offset; // Position relative to beginning of tab
float Width; // Width currently displayed
float ContentWidth; // Width of actual contents, stored during BeginTabItem() call
+ ImS16 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames
+ bool WantClose; // Marked as closed by SetTabItemClosed()
- ImGuiTabItem() { ID = 0; Flags = ImGuiTabItemFlags_None; Window = NULL; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = ContentWidth = 0.0f; }
+ ImGuiTabItem() { ID = 0; Flags = ImGuiTabItemFlags_None; Window = NULL; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = ContentWidth = 0.0f; WantClose = false; }
};
// Storage for a tab bar (sizeof() 92~96 bytes)
@@ -1944,7 +1957,7 @@ struct ImGuiTabBar
{
if (tab->Window)
return tab->Window->Name;
- IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size);
+ IM_ASSERT(tab->NameOffset != -1 && (int)tab->NameOffset < TabsNames.Buf.Size);
return TabsNames.Buf.Data + tab->NameOffset;
}
};
@@ -2049,6 +2062,7 @@ namespace ImGui
IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL);
IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id);
IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged);
+ IMGUI_API void SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);
IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested
IMGUI_API void FocusableItemUnregister(ImGuiWindow* window);
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h);
@@ -2230,8 +2244,8 @@ namespace ImGui
// Widgets low-level behaviors
IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);
- IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, float power, ImGuiDragFlags flags);
- IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb);
+ IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags);
+ IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f);
IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextItemOpen() data, if any. May return true when logging
@@ -2240,9 +2254,10 @@ namespace ImGui
// Template functions are instantiated in imgui_widgets.cpp for a finite number of types.
// To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).
// e.g. " extern template IMGUI_API float RoundScalarWithFormatT<float, float>(const char* format, ImGuiDataType data_type, float v); "
- template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, float power, ImGuiDragFlags flags);
- template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb);
- template<typename T, typename FLOAT_T> IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos);
+ template<typename T, typename FLOAT_T> IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);
+ template<typename T, typename FLOAT_T> IMGUI_API T ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);
+ template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);
+ template<typename T, typename SIGNED_T, typename FLOAT_T> IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
template<typename T, typename SIGNED_T> IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
// Data type helpers
@@ -2287,6 +2302,7 @@ IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas);
+IMGUI_API void ImFontAtlasBuildRender1bppRectFromString(ImFontAtlas* atlas, int atlas_x, int atlas_y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
diff --git a/imgui/imgui_widgets.cpp b/imgui/imgui_widgets.cpp
index bac3898e..8d700a01 100644
--- a/imgui/imgui_widgets.cpp
+++ b/imgui/imgui_widgets.cpp
@@ -1,4 +1,4 @@
-// dear imgui, v1.77
+// dear imgui, v1.78
// (widgets code)
/*
@@ -595,6 +595,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
}
}
+ // Process while held
bool held = false;
if (g.ActiveId == id)
{
@@ -615,6 +616,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0;
if ((release_in || release_anywhere) && !g.DragDropActive)
{
+ // Report as pressed when releasing the mouse (this is the most common path)
bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownWasDoubleClick[mouse_button];
bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release>
if (!is_double_click_release && !is_repeating_already)
@@ -627,6 +629,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
}
else if (g.ActiveIdSource == ImGuiInputSource_Nav)
{
+ // When activated using Nav, we hold on the ActiveID until activation button is released
if (g.NavActivateDownId != id)
ClearActiveID();
}
@@ -682,7 +685,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
bool ImGui::Button(const char* label, const ImVec2& size_arg)
{
- return ButtonEx(label, size_arg, 0);
+ return ButtonEx(label, size_arg, ImGuiButtonFlags_None);
}
// Small buttons fits within text without additional vertical spacing.
@@ -698,7 +701,7 @@ bool ImGui::SmallButton(const char* label)
// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
-bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
+bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -715,7 +718,7 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
return false;
bool hovered, held;
- bool pressed = ButtonBehavior(bb, id, &hovered, &held);
+ bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
return pressed;
}
@@ -1914,10 +1917,11 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
}
template<typename T>
-static bool ClampBehaviorT(T* v, T v_min, T v_max)
+static bool ClampBehaviorT(T* v, const T* v_min, const T* v_max)
{
- if (*v < v_min) { *v = v_min; return true; }
- if (*v > v_max) { *v = v_max; return true; }
+ // Clamp, both sides are optional
+ if (v_min && *v < *v_min) { *v = *v_min; return true; }
+ if (v_max && *v > *v_max) { *v = *v_max; return true; }
return false;
}
@@ -1925,16 +1929,16 @@ bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_m
{
switch (data_type)
{
- case ImGuiDataType_S8: return ClampBehaviorT<ImS8 >((ImS8* )p_data, *(const ImS8* )p_min, *(const ImS8* )p_max);
- case ImGuiDataType_U8: return ClampBehaviorT<ImU8 >((ImU8* )p_data, *(const ImU8* )p_min, *(const ImU8* )p_max);
- case ImGuiDataType_S16: return ClampBehaviorT<ImS16 >((ImS16* )p_data, *(const ImS16* )p_min, *(const ImS16* )p_max);
- case ImGuiDataType_U16: return ClampBehaviorT<ImU16 >((ImU16* )p_data, *(const ImU16* )p_min, *(const ImU16* )p_max);
- case ImGuiDataType_S32: return ClampBehaviorT<ImS32 >((ImS32* )p_data, *(const ImS32* )p_min, *(const ImS32* )p_max);
- case ImGuiDataType_U32: return ClampBehaviorT<ImU32 >((ImU32* )p_data, *(const ImU32* )p_min, *(const ImU32* )p_max);
- case ImGuiDataType_S64: return ClampBehaviorT<ImS64 >((ImS64* )p_data, *(const ImS64* )p_min, *(const ImS64* )p_max);
- case ImGuiDataType_U64: return ClampBehaviorT<ImU64 >((ImU64* )p_data, *(const ImU64* )p_min, *(const ImU64* )p_max);
- case ImGuiDataType_Float: return ClampBehaviorT<float >((float* )p_data, *(const float* )p_min, *(const float* )p_max);
- case ImGuiDataType_Double: return ClampBehaviorT<double>((double*)p_data, *(const double*)p_min, *(const double*)p_max);
+ case ImGuiDataType_S8: return ClampBehaviorT<ImS8 >((ImS8* )p_data, (const ImS8* )p_min, (const ImS8* )p_max);
+ case ImGuiDataType_U8: return ClampBehaviorT<ImU8 >((ImU8* )p_data, (const ImU8* )p_min, (const ImU8* )p_max);
+ case ImGuiDataType_S16: return ClampBehaviorT<ImS16 >((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max);
+ case ImGuiDataType_U16: return ClampBehaviorT<ImU16 >((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max);
+ case ImGuiDataType_S32: return ClampBehaviorT<ImS32 >((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max);
+ case ImGuiDataType_U32: return ClampBehaviorT<ImU32 >((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max);
+ case ImGuiDataType_S64: return ClampBehaviorT<ImS64 >((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max);
+ case ImGuiDataType_U64: return ClampBehaviorT<ImU64 >((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max);
+ case ImGuiDataType_Float: return ClampBehaviorT<float >((float* )p_data, (const float* )p_min, (const float* )p_max);
+ case ImGuiDataType_Double: return ClampBehaviorT<double>((double*)p_data, (const double*)p_min, (const double*)p_max);
case ImGuiDataType_COUNT: break;
}
IM_ASSERT(0);
@@ -2001,16 +2005,13 @@ TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type,
// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
-bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiDragFlags flags)
+bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags)
{
ImGuiContext& g = *GImGui;
- const ImGuiAxis axis = (flags & ImGuiDragFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
+ const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
const bool is_clamped = (v_min < v_max);
- const bool is_power = (power != 1.0f && is_decimal && is_clamped && (v_max - v_min < FLT_MAX));
- const bool is_locked = (v_min > v_max);
- if (is_locked)
- return false;
+ const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) && is_decimal;
// Default tweak speed
if (v_speed == 0.0f && is_clamped && (v_max - v_min < FLT_MAX))
@@ -2038,12 +2039,15 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
if (axis == ImGuiAxis_Y)
adjust_delta = -adjust_delta;
+ // For logarithmic use our range is effectively 0..1 so scale the delta into that range
+ if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0
+ adjust_delta /= (float)(v_max - v_min);
+
// Clear current value on activation
// Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300.
bool is_just_activated = g.ActiveIdIsJustActivated;
bool is_already_past_limits_and_pushing_outward = is_clamped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));
- bool is_drag_direction_change_with_power = is_power && ((adjust_delta < 0 && g.DragCurrentAccum > 0) || (adjust_delta > 0 && g.DragCurrentAccum < 0));
- if (is_just_activated || is_already_past_limits_and_pushing_outward || is_drag_direction_change_with_power)
+ if (is_just_activated || is_already_past_limits_and_pushing_outward)
{
g.DragCurrentAccum = 0.0f;
g.DragCurrentAccumDirty = false;
@@ -2060,13 +2064,19 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
TYPE v_cur = *v;
FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;
- if (is_power)
+ float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true
+ const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense)
+ if (is_logarithmic)
{
- // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range
- FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
- FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min));
- v_cur = v_min + (SIGNEDTYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min);
- v_old_ref_for_accum_remainder = v_old_norm_curved;
+ // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.
+ const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 1;
+ logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);
+
+ // Convert to parametric space, apply delta, convert back
+ float v_old_parametric = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
+ float v_new_parametric = v_old_parametric + g.DragCurrentAccum;
+ v_cur = ScaleValueFromRatioT<TYPE, FLOATTYPE>(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
+ v_old_ref_for_accum_remainder = v_old_parametric;
}
else
{
@@ -2074,14 +2084,16 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
}
// Round to user desired precision based on format string
- v_cur = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
+ if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
+ v_cur = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_cur);
// Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
g.DragCurrentAccumDirty = false;
- if (is_power)
+ if (is_logarithmic)
{
- FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
- g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder);
+ // Convert to parametric space, apply delta, convert back
+ float v_new_parametric = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
+ g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder);
}
else
{
@@ -2108,8 +2120,11 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const
return true;
}
-bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, float power, ImGuiDragFlags flags)
+bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
{
+ // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
+ IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
+
ImGuiContext& g = *GImGui;
if (g.ActiveId == id)
{
@@ -2120,19 +2135,21 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v
}
if (g.ActiveId != id)
return false;
+ if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
+ return false;
switch (data_type)
{
- case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN, p_max ? *(const ImS8*)p_max : IM_S8_MAX, format, power, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }
- case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN, p_max ? *(const ImU8*)p_max : IM_U8_MAX, format, power, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; }
- case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, power, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }
- case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, power, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }
- case ImGuiDataType_S32: return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)p_v, v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, power, flags);
- case ImGuiDataType_U32: return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)p_v, v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, power, flags);
- case ImGuiDataType_S64: return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)p_v, v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, power, flags);
- case ImGuiDataType_U64: return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)p_v, v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, power, flags);
- case ImGuiDataType_Float: return DragBehaviorT<float, float, float >(data_type, (float*)p_v, v_speed, p_min ? *(const float* )p_min : -FLT_MAX, p_max ? *(const float* )p_max : FLT_MAX, format, power, flags);
- case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX, p_max ? *(const double*)p_max : DBL_MAX, format, power, flags);
+ case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN, p_max ? *(const ImS8*)p_max : IM_S8_MAX, format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }
+ case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN, p_max ? *(const ImU8*)p_max : IM_U8_MAX, format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; }
+ case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }
+ case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }
+ case ImGuiDataType_S32: return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)p_v, v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags);
+ case ImGuiDataType_U32: return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)p_v, v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags);
+ case ImGuiDataType_S64: return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)p_v, v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags);
+ case ImGuiDataType_U64: return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)p_v, v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags);
+ case ImGuiDataType_Float: return DragBehaviorT<float, float, float >(data_type, (float*)p_v, v_speed, p_min ? *(const float* )p_min : -FLT_MAX, p_max ? *(const float* )p_max : FLT_MAX, format, flags);
+ case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX, p_max ? *(const double*)p_max : DBL_MAX, format, flags);
case ImGuiDataType_COUNT: break;
}
IM_ASSERT(0);
@@ -2140,16 +2157,13 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v
}
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional.
-// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
-bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power)
+// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
+bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
- if (power != 1.0f)
- IM_ASSERT(p_min != NULL && p_max != NULL); // When using a power curve the drag needs to have known bounds
-
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
@@ -2170,11 +2184,11 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
// Tabbing or CTRL-clicking on Drag turns it into an input box
const bool hovered = ItemHoverable(frame_bb, id);
- bool temp_input_is_active = TempInputIsActive(id);
- bool temp_input_start = false;
+ const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
+ bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active)
{
- const bool focus_requested = FocusableItemRegister(window, id);
+ const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id);
const bool clicked = (hovered && g.IO.MouseClicked[0]);
const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]);
if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id)
@@ -2183,17 +2197,20 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
SetFocusID(id, window);
FocusWindow(window);
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
- if (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)
+ if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id))
{
- temp_input_start = true;
+ temp_input_is_active = true;
FocusableItemUnregister(window);
}
}
}
- // Our current specs do NOT clamp when using CTRL+Click manual input, but we should eventually add a flag for that..
- if (temp_input_is_active || temp_input_start)
- return TempInputScalar(frame_bb, id, label, data_type, p_data, format);// , p_min, p_max);
+ if (temp_input_is_active)
+ {
+ // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set
+ const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0;
+ return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL);
+ }
// Draw frame
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
@@ -2201,7 +2218,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
// Drag behavior
- const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, power, ImGuiDragFlags_None);
+ const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags);
if (value_changed)
MarkItemEdited(id);
@@ -2217,7 +2234,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
return value_changed;
}
-bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power)
+bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -2234,7 +2251,7 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data
PushID(i);
if (i > 0)
SameLine(0, g.Style.ItemInnerSpacing.x);
- value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, power);
+ value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags);
PopID();
PopItemWidth();
p_data = (void*)((char*)p_data + type_size);
@@ -2252,27 +2269,28 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data
return value_changed;
}
-bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power)
+bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power);
+ return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power)
+bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power);
+ return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power)
+bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power);
+ return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power)
+bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power);
+ return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power)
+// NB: You likely want to specify the ImGuiSliderFlags_ClampOnInput when using this.
+bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -2283,10 +2301,17 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu
BeginGroup();
PushMultiItemsWidths(2, CalcItemWidth());
- bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power);
+ float min_min = (v_min >= v_max) ? -FLT_MAX : v_min;
+ float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);
+ ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);
+ bool value_changed = DragScalar("##min", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
- value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power);
+
+ float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);
+ float max_max = (v_min >= v_max) ? FLT_MAX : v_max;
+ ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);
+ value_changed |= DragScalar("##max", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
@@ -2297,27 +2322,28 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu
}
// NB: v_speed is float to allow adjusting the drag speed with more precision
-bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format)
+bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format);
+ return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format)
+bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format);
+ return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format)
+bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format);
+ return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format)
+bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format);
+ return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags);
}
-bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max)
+// NB: You likely want to specify the ImGuiSliderFlags_ClampOnInput when using this.
+bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -2328,10 +2354,17 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_
BeginGroup();
PushMultiItemsWidths(2, CalcItemWidth());
- bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format);
+ int min_min = (v_min >= v_max) ? INT_MIN : v_min;
+ int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);
+ ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);
+ bool value_changed = DragInt("##min", v_current_min, v_speed, min_min, min_max, format, min_flags);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
- value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format);
+
+ int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);
+ int max_max = (v_min >= v_max) ? INT_MAX : v_max;
+ ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);
+ value_changed |= DragInt("##max", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags);
PopItemWidth();
SameLine(0, g.Style.ItemInnerSpacing.x);
@@ -2342,9 +2375,40 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_
return value_changed;
}
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+
+// Obsolete versions with power parameter. See https://github.com/ocornut/imgui/issues/3361 for details.
+bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power)
+{
+ ImGuiSliderFlags drag_flags = ImGuiSliderFlags_None;
+ if (power != 1.0f)
+ {
+ IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!");
+ IM_ASSERT(p_min != NULL && p_max != NULL); // When using a power curve the drag needs to have known bounds
+ drag_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths
+ }
+ return DragScalar(label, data_type, p_data, v_speed, p_min, p_max, format, drag_flags);
+}
+
+bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power)
+{
+ ImGuiSliderFlags drag_flags = ImGuiSliderFlags_None;
+ if (power != 1.0f)
+ {
+ IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!");
+ IM_ASSERT(p_min != NULL && p_max != NULL); // When using a power curve the drag needs to have known bounds
+ drag_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths
+ }
+ return DragScalarN(label, data_type, p_data, components, v_speed, p_min, p_max, format, drag_flags);
+}
+
+#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+
//-------------------------------------------------------------------------
// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
//-------------------------------------------------------------------------
+// - ScaleRatioFromValueT<> [Internal]
+// - ScaleValueFromRatioT<> [Internal]
// - SliderBehaviorT<>() [Internal]
// - SliderBehavior() [Internal]
// - SliderScalar()
@@ -2363,42 +2427,147 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_
// - VSliderInt()
//-------------------------------------------------------------------------
+// Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT)
template<typename TYPE, typename FLOATTYPE>
-float ImGui::SliderCalcRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos)
+float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)
{
if (v_min == v_max)
return 0.0f;
+ IM_UNUSED(data_type);
- const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
- if (is_power)
+ if (is_logarithmic)
{
- if (v_clamped < 0.0f)
+ bool flipped = v_max < v_min;
+
+ if (flipped) // Handle the case where the range is backwards
+ ImSwap(v_min, v_max);
+
+ // Fudge min/max to avoid getting close to log(0)
+ FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;
+ FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;
+
+ // Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)
+ if ((v_min == 0.0f) && (v_max < 0.0f))
+ v_min_fudged = -logarithmic_zero_epsilon;
+ else if ((v_max == 0.0f) && (v_min < 0.0f))
+ v_max_fudged = -logarithmic_zero_epsilon;
+
+ float result;
+
+ if (v_clamped <= v_min_fudged)
+ result = 0.0f; // Workaround for values that are in-range but below our fudge
+ else if (v_clamped >= v_max_fudged)
+ result = 1.0f; // Workaround for values that are in-range but above our fudge
+ else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions
{
- const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min));
- return (1.0f - ImPow(f, 1.0f / power)) * linear_zero_pos;
+ float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space. There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine)
+ float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;
+ float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;
+ if (v == 0.0f)
+ result = zero_point_center; // Special case for exactly zero
+ else if (v < 0.0f)
+ result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L;
+ else
+ result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R));
}
+ else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider
+ result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged));
else
- {
- const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min)));
- return linear_zero_pos + ImPow(f, 1.0f / power) * (1.0f - linear_zero_pos);
- }
+ result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged));
+
+ return flipped ? (1.0f - result) : result;
}
// Linear slider
return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min));
}
-// FIXME: Move some of the code into SliderBehavior(). Current responsibility is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc.
+// Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT)
+template<typename TYPE, typename FLOATTYPE>
+TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)
+{
+ if (v_min == v_max)
+ return (TYPE)0.0f;
+ const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
+
+ TYPE result;
+ if (is_logarithmic)
+ {
+ // We special-case the extents because otherwise our fudging can lead to "mathematically correct" but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value
+ if (t <= 0.0f)
+ result = v_min;
+ else if (t >= 1.0f)
+ result = v_max;
+ else
+ {
+ bool flipped = v_max < v_min; // Check if range is "backwards"
+
+ // Fudge min/max to avoid getting silly results close to zero
+ FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;
+ FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;
+
+ if (flipped)
+ ImSwap(v_min_fudged, v_max_fudged);
+
+ // Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)
+ if ((v_max == 0.0f) && (v_min < 0.0f))
+ v_max_fudged = -logarithmic_zero_epsilon;
+
+ float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range
+
+ if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts
+ {
+ float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space
+ float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;
+ float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;
+ if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R)
+ result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise)
+ else if (t_with_flip < zero_point_center)
+ result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L))));
+ else
+ result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R))));
+ }
+ else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider
+ result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip)));
+ else
+ result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip));
+ }
+ }
+ else
+ {
+ // Linear slider
+ if (is_decimal)
+ {
+ result = ImLerp(v_min, v_max, t);
+ }
+ else
+ {
+ // For integer values we want the clicking position to match the grab box so we round above
+ // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
+ FLOATTYPE v_new_off_f = (v_max - v_min) * t;
+ TYPE v_new_off_floor = (TYPE)(v_new_off_f);
+ TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);
+ if (v_new_off_floor < v_new_off_round)
+ result = v_min + v_new_off_round;
+ else
+ result = v_min + v_new_off_floor;
+ }
+ }
+
+ return result;
+}
+
+// FIXME: Move more of the code into SliderBehavior()
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
-bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb)
+bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)
{
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
- const bool is_power = (power != 1.0f) && is_decimal;
+ const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) && is_decimal;
const float grab_padding = 2.0f;
const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
@@ -2411,19 +2580,14 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;
const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f;
- // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f
- float linear_zero_pos; // 0.0->1.0f
- if (is_power && v_min * v_max < 0.0f)
+ float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true
+ float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true
+ if (is_logarithmic)
{
- // Different sign
- const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f / power);
- const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f / power);
- linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0));
- }
- else
- {
- // Same sign
- linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f;
+ // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.
+ const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 1;
+ logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);
+ zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f);
}
// Process interacting with the slider
@@ -2449,87 +2613,80 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
}
else if (g.ActiveIdSource == ImGuiInputSource_Nav)
{
- const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f);
- float delta = (axis == ImGuiAxis_X) ? delta2.x : -delta2.y;
- if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
+ if (g.ActiveIdIsJustActivated)
{
- ClearActiveID();
+ g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation
+ g.SliderCurrentAccumDirty = false;
}
- else if (delta != 0.0f)
+
+ const ImVec2 input_delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f);
+ float input_delta = (axis == ImGuiAxis_X) ? input_delta2.x : -input_delta2.y;
+ if (input_delta != 0.0f)
{
- clicked_t = SliderCalcRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0;
- if ((decimal_precision > 0) || is_power)
+ if (decimal_precision > 0)
{
- delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds
+ input_delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds
if (IsNavInputDown(ImGuiNavInput_TweakSlow))
- delta /= 10.0f;
+ input_delta /= 10.0f;
}
else
{
if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow))
- delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps
+ input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps
else
- delta /= 100.0f;
+ input_delta /= 100.0f;
}
if (IsNavInputDown(ImGuiNavInput_TweakFast))
- delta *= 10.0f;
- set_new_value = true;
- if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits
- set_new_value = false;
- else
- clicked_t = ImSaturate(clicked_t + delta);
+ input_delta *= 10.0f;
+
+ g.SliderCurrentAccum += input_delta;
+ g.SliderCurrentAccumDirty = true;
}
- }
- if (set_new_value)
- {
- TYPE v_new;
- if (is_power)
+ float delta = g.SliderCurrentAccum;
+ if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
{
- // Account for power curve scale on both sides of the zero
- if (clicked_t < linear_zero_pos)
- {
- // Negative: rescale to the negative range before powering
- float a = 1.0f - (clicked_t / linear_zero_pos);
- a = ImPow(a, power);
- v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a);
- }
- else
- {
- // Positive: rescale to the positive range before powering
- float a;
- if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f)
- a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos);
- else
- a = clicked_t;
- a = ImPow(a, power);
- v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a);
- }
+ ClearActiveID();
}
- else
+ else if (g.SliderCurrentAccumDirty)
{
- // Linear slider
- if (is_decimal)
+ clicked_t = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
+
+ if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits
{
- v_new = ImLerp(v_min, v_max, clicked_t);
+ set_new_value = false;
+ g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate
}
else
{
- // For integer values we want the clicking position to match the grab box so we round above
- // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
- FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t;
- TYPE v_new_off_floor = (TYPE)(v_new_off_f);
- TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);
- if (v_new_off_floor < v_new_off_round)
- v_new = v_min + v_new_off_round;
+ set_new_value = true;
+ float old_clicked_t = clicked_t;
+ clicked_t = ImSaturate(clicked_t + delta);
+
+ // Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator
+ TYPE v_new = ScaleValueFromRatioT<TYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
+ if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
+ v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
+ float new_clicked_t = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
+
+ if (delta > 0)
+ g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta);
else
- v_new = v_min + v_new_off_floor;
+ g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta);
}
+
+ g.SliderCurrentAccumDirty = false;
}
+ }
+
+ if (set_new_value)
+ {
+ TYPE v_new = ScaleValueFromRatioT<TYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
// Round to user desired precision based on format string
- v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
+ if (!(flags & ImGuiSliderFlags_NoRoundToFormat))
+ v_new = RoundScalarWithFormatT<TYPE, SIGNEDTYPE>(format, data_type, v_new);
// Apply result
if (*v != v_new)
@@ -2547,7 +2704,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
else
{
// Output grab position so it can be displayed by the caller
- float grab_t = SliderCalcRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, power, linear_zero_pos);
+ float grab_t = ScaleRatioFromValueT<TYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
if (axis == ImGuiAxis_Y)
grab_t = 1.0f - grab_t;
const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
@@ -2563,32 +2720,39 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ
// For 32-bit and larger types, slider bounds are limited to half the natural type range.
// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok.
// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.
-bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb)
+bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)
{
+ // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
+ IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
+
+ ImGuiContext& g = *GImGui;
+ if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
+ return false;
+
switch (data_type)
{
- case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, power, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }
- case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min, *(const ImU8*)p_max, format, power, flags, out_grab_bb); if (r) *(ImU8*)p_v = (ImU8)v32; return r; }
- case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, power, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }
- case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, power, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }
+ case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }
+ case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min, *(const ImU8*)p_max, format, flags, out_grab_bb); if (r) *(ImU8*)p_v = (ImU8)v32; return r; }
+ case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }
+ case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }
case ImGuiDataType_S32:
IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2);
- return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)p_v, *(const ImS32*)p_min, *(const ImS32*)p_max, format, power, flags, out_grab_bb);
+ return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)p_v, *(const ImS32*)p_min, *(const ImS32*)p_max, format, flags, out_grab_bb);
case ImGuiDataType_U32:
IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2);
- return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)p_v, *(const ImU32*)p_min, *(const ImU32*)p_max, format, power, flags, out_grab_bb);
+ return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)p_v, *(const ImU32*)p_min, *(const ImU32*)p_max, format, flags, out_grab_bb);
case ImGuiDataType_S64:
IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2);
- return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)p_v, *(const ImS64*)p_min, *(const ImS64*)p_max, format, power, flags, out_grab_bb);
+ return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)p_v, *(const ImS64*)p_min, *(const ImS64*)p_max, format, flags, out_grab_bb);
case ImGuiDataType_U64:
IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2);
- return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)p_v, *(const ImU64*)p_min, *(const ImU64*)p_max, format, power, flags, out_grab_bb);
+ return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)p_v, *(const ImU64*)p_min, *(const ImU64*)p_max, format, flags, out_grab_bb);
case ImGuiDataType_Float:
IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f);
- return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)p_v, *(const float*)p_min, *(const float*)p_max, format, power, flags, out_grab_bb);
+ return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)p_v, *(const float*)p_min, *(const float*)p_max, format, flags, out_grab_bb);
case ImGuiDataType_Double:
IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f);
- return SliderBehaviorT<double, double, double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, power, flags, out_grab_bb);
+ return SliderBehaviorT<double, double, double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb);
case ImGuiDataType_COUNT: break;
}
IM_ASSERT(0);
@@ -2597,7 +2761,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required.
// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
-bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power)
+bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -2624,11 +2788,11 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
// Tabbing or CTRL-clicking on Slider turns it into an input box
const bool hovered = ItemHoverable(frame_bb, id);
- bool temp_input_is_active = TempInputIsActive(id);
- bool temp_input_start = false;
+ const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
+ bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active)
{
- const bool focus_requested = FocusableItemRegister(window, id);
+ const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id);
const bool clicked = (hovered && g.IO.MouseClicked[0]);
if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id)
{
@@ -2636,17 +2800,20 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
SetFocusID(id, window);
FocusWindow(window);
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
- if (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)
+ if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id))
{
- temp_input_start = true;
+ temp_input_is_active = true;
FocusableItemUnregister(window);
}
}
}
- // Our current specs do NOT clamp when using CTRL+Click manual input, but we should eventually add a flag for that..
- if (temp_input_is_active || temp_input_start)
- return TempInputScalar(frame_bb, id, label, data_type, p_data, format);// , p_min, p_max);
+ if (temp_input_is_active)
+ {
+ // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set
+ const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0;
+ return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL);
+ }
// Draw frame
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
@@ -2655,7 +2822,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
// Slider behavior
ImRect grab_bb;
- const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, power, ImGuiSliderFlags_None, &grab_bb);
+ const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb);
if (value_changed)
MarkItemEdited(id);
@@ -2676,7 +2843,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
}
// Add multiple sliders on 1 line for compact edition of multiple components
-bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power)
+bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -2693,7 +2860,7 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i
PushID(i);
if (i > 0)
SameLine(0, g.Style.ItemInnerSpacing.x);
- value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, power);
+ value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags);
PopID();
PopItemWidth();
v = (void*)((char*)v + type_size);
@@ -2711,57 +2878,57 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i
return value_changed;
}
-bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power)
+bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
+ return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);
}
-bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power)
+bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power);
+ return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags);
}
-bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power)
+bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power);
+ return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags);
}
-bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power)
+bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power);
+ return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags);
}
-bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format)
+bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags)
{
if (format == NULL)
format = "%.0f deg";
float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
- bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, 1.0f);
+ bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags);
*v_rad = v_deg * (2 * IM_PI) / 360.0f;
return value_changed;
}
-bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format)
+bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format);
+ return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);
}
-bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format)
+bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format);
+ return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags);
}
-bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format)
+bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format);
+ return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags);
}
-bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format)
+bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format);
+ return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags);
}
-bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power)
+bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -2801,7 +2968,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
// Slider behavior
ImRect grab_bb;
- const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb);
+ const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb);
if (value_changed)
MarkItemEdited(id);
@@ -2820,16 +2987,43 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
return value_changed;
}
-bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power)
+bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
{
- return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power);
+ return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);
}
-bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format)
+bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
{
- return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format);
+ return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);
}
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+
+// Obsolete versions with power parameter. See https://github.com/ocornut/imgui/issues/3361 for details.
+bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power)
+{
+ ImGuiSliderFlags slider_flags = ImGuiSliderFlags_None;
+ if (power != 1.0f)
+ {
+ IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!");
+ slider_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths
+ }
+ return SliderScalar(label, data_type, p_data, p_min, p_max, format, slider_flags);
+}
+
+bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power)
+{
+ ImGuiSliderFlags slider_flags = ImGuiSliderFlags_None;
+ if (power != 1.0f)
+ {
+ IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!");
+ slider_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths
+ }
+ return SliderScalarN(label, data_type, v, components, v_min, v_max, format, slider_flags);
+}
+
+#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+
//-------------------------------------------------------------------------
// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
//-------------------------------------------------------------------------
@@ -2945,20 +3139,10 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
return value_changed;
}
-// Note that Drag/Slider functions are currently NOT forwarding the min/max values clamping values!
+// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the
+// ImGuiSliderFlags_ClampOnInput / ImGuiSliderFlags_ClampOnInput flag is set!
// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility.
// However this may not be ideal for all uses, as some user code may break on out of bound values.
-// In the future we should add flags to Slider/Drag to specify how to enforce min/max values with CTRL+Click.
-// See GitHub issues #1829 and #3209
-// In the meanwhile, you can easily "wrap" those functions to enforce clamping, using wrapper functions, e.g.
-// bool SliderFloatClamp(const char* label, float* v, float v_min, float v_max)
-// {
-// float v_backup = *v;
-// if (!SliderFloat(label, v, v_min, v_max))
-// return false;
-// *v = ImClamp(*v, v_min, v_max);
-// return v_backup != *v;
-// }
bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max)
{
ImGuiContext& g = *GImGui;
@@ -2981,7 +3165,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
// Apply new value (or operations) then clamp
DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL);
- if (p_clamp_min && p_clamp_max)
+ if (p_clamp_min || p_clamp_max)
DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max);
// Only mark as edited if new value is different
@@ -5194,9 +5378,9 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl
if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask);
SetCursorScreenPos(backup_pos);
- ImVec4 dummy_ref_col;
- memcpy(&dummy_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));
- ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags);
+ ImVec4 previewing_ref_col;
+ memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));
+ ColorPicker4("##previewing_picker", &previewing_ref_col.x, picker_flags);
PopID();
}
PopItemWidth();
@@ -5650,7 +5834,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags
// FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
// FIXME: CloseButton can overlap into text, need find a way to clip the text somehow.
ImGuiContext& g = *GImGui;
- ImGuiItemHoveredDataBackup last_item_backup;
+ ImGuiLastItemDataBackup last_item_backup;
float button_size = g.FontSize;
float button_x = ImMax(window->DC.LastItemRect.Min.x, window->DC.LastItemRect.Max.x - g.Style.FramePadding.x * 2.0f - button_size);
float button_y = window->DC.LastItemRect.Min.y;
@@ -5668,7 +5852,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags
// - Selectable()
//-------------------------------------------------------------------------
-// Tip: pass a non-visible label (e.g. "##dummy") then you can use the space to draw other text or image.
+// Tip: pass a non-visible label (e.g. "##hello") 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 or use ##unique_id.
// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowItemOverlap are also frequently used flags.
// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.
@@ -5693,8 +5877,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
ItemSize(size, 0.0f);
// Fill horizontal space
- const float min_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ContentRegionRect.Min.x : pos.x;
- const float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ContentRegionRect.Max.x : GetContentRegionMaxAbs().x;
+ const float min_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ParentWorkRect.Min.x : pos.x;
+ const float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth))
size.x = ImMax(label_size.x, max_x - min_x);
@@ -6668,9 +6852,9 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
tab_bar->CurrFrameVisible = g.FrameCount;
tab_bar->FramePadding = g.Style.FramePadding;
- // Layout
- ItemSize(ImVec2(tab_bar->OffsetMaxIdeal, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y);
+ // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap
window->DC.CursorPos.x = tab_bar->BarRect.Min.x;
+ window->DC.CursorPos.y = tab_bar->BarRect.Max.y + g.Style.ItemSpacing.y;
// Draw separator
const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive);
@@ -6703,7 +6887,7 @@ void ImGui::EndTabBar()
IM_ASSERT_USER_ERROR(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!");
return;
}
- if (tab_bar->WantLayout)
+ if (tab_bar->WantLayout) // Fallback in case no TabItem have been submitted
TabBarLayout(tab_bar);
// Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed().
@@ -6727,15 +6911,17 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
ImGuiContext& g = *GImGui;
tab_bar->WantLayout = false;
- // Garbage collect
+ // Garbage collect by compacting list
int tab_dst_n = 0;
for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
{
ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
- if (tab->LastFrameVisible < tab_bar->PrevFrameVisible)
+ if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose)
{
- if (tab->ID == tab_bar->SelectedTabId)
- tab_bar->SelectedTabId = 0;
+ // Remove tab
+ if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; }
+ if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; }
+ if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; }
continue;
}
if (tab_dst_n != tab_src_n)
@@ -6893,6 +7079,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
// Clear name buffers
if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
tab_bar->TabsNames.Buf.resize(0);
+
+ // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame)
+ ImGuiWindow* window = g.CurrentWindow;
+ window->DC.CursorPos = tab_bar->BarRect.Min;
+ ItemSize(ImVec2(tab_bar->OffsetMaxIdeal, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y);
}
// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack.
@@ -7088,7 +7279,7 @@ static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
arrow_col.w *= 0.5f;
PushStyleColor(ImGuiCol_Text, arrow_col);
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
- bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview);
+ bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest);
PopStyleColor(2);
ImGuiTabItem* tab_to_select = NULL;
@@ -7175,7 +7366,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
const ImGuiStyle& style = g.Style;
const ImGuiID id = TabBarCalcTabID(tab_bar, label);
- // If the user called us with *p_open == false, we early out and don't render. We make a dummy call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.
+ // If the user called us with *p_open == false, we early out and don't render.
+ // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
if (p_open && !*p_open)
{
@@ -7224,7 +7416,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
else
{
IM_ASSERT(tab->Window == NULL);
- tab->NameOffset = tab_bar->TabsNames.size();
+ tab->NameOffset = (ImS16)tab_bar->TabsNames.size();
tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Append name _with_ the zero-terminator.
}
@@ -7368,8 +7560,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
// Undock
DockContextQueueUndockWindow(&g, docked_window);
g.MovingWindow = docked_window;
- g.ActiveId = g.MovingWindow->MoveId;
+ SetActiveID(g.MovingWindow->MoveId, g.MovingWindow);
g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min;
+ g.ActiveIdNoClearOnFocusLoss = true;
}
}
}
@@ -7422,9 +7615,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
}
// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.
-// To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem().
+// To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar().
// Tabs closed by the close button will automatically be flagged to avoid this issue.
-// FIXME: We should aim to support calling SetTabItemClosed() after the tab submission (for next frame)
void ImGui::SetTabItemClosed(const char* label)
{
ImGuiContext& g = *GImGui;
@@ -7432,9 +7624,9 @@ void ImGui::SetTabItemClosed(const char* label)
if (is_within_manual_tab_bar)
{
ImGuiTabBar* tab_bar = g.CurrentTabBar;
- IM_ASSERT(tab_bar->WantLayout); // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem()
ImGuiID tab_id = TabBarCalcTabID(tab_bar, label);
- TabBarRemoveTab(tab_bar, tab_id);
+ if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
+ tab->WantClose = true; // Will be processed by next call to TabBarLayout()
}
else if (ImGuiWindow* window = FindWindowByName(label))
{
@@ -7526,7 +7718,7 @@ bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
close_button_visible = true;
if (close_button_visible)
{
- ImGuiItemHoveredDataBackup last_item_backup;
+ ImGuiLastItemDataBackup last_item_backup;
const float close_button_sz = g.FontSize;
PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding);
if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x * 2.0f - close_button_sz, bb.Min.y)))
@@ -7786,7 +7978,8 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag
columns->HostCursorPosY = window->DC.CursorPos.y;
columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x;
columns->HostInitialClipRect = window->ClipRect;
- columns->HostWorkRect = window->WorkRect;
+ columns->HostBackupParentWorkRect = window->ParentWorkRect;
+ window->ParentWorkRect = window->WorkRect;
// Set state for first column
// We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect
@@ -7966,7 +8159,8 @@ void ImGui::EndColumns()
}
columns->IsBeingResized = is_being_resized;
- window->WorkRect = columns->HostWorkRect;
+ window->WorkRect = window->ParentWorkRect;
+ window->ParentWorkRect = columns->HostBackupParentWorkRect;
window->DC.CurrentColumns = NULL;
window->DC.ColumnsOffset.x = 0.0f;
window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);