From c5d83d8af205618d950527625838377a9853b3b8 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 26 Apr 2019 22:25:30 +0200 Subject: [PATCH 01/13] Separator: Declare its thickness (1.0f) to the layout, making items around separator more symmetrical. --- docs/CHANGELOG.txt | 3 ++- imgui_widgets.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dbe87c97..c51fcc0e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,7 +62,8 @@ Other Changes: - GetMouseDragDelta(): verify that mouse positions are valid otherwise returns zero. - Inputs: Also add support for horizontal scroll with Shift+Mouse Wheel. (#2424, #1463) [@LucaRood] - PlotLines, PlotHistogram: Ignore NaN values when calculating min/max bounds. (#2485) -- Columns: Fixed boundary of clipping being off by 1 pixel within the left column. +- Columns: Fixed boundary of clipping being off by 1 pixel within the left column. (#125) +- Separator: Declare its thickness (1.0f) to the layout, making items around separator more symmetrical. - Combo, Slider, Scrollbar: Improve rendering in situation when there's only a few pixels available (<3 pixels). - Nav: Fixed Drag/Slider functions going into text input mode when keyboard CTRL is held while pressing NavActivate. - Misc: Added IM_MALLOC/IM_FREE macros mimicking IM_NEW/IM_DELETE so user doesn't need to revert diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index bebbf3d8..82bef8fb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1203,7 +1203,7 @@ void ImGui::Separator() return; ImGuiContext& g = *GImGui; - // Those flags should eventually be overridable by the user + // Those flags should eventually be overrideable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected if (flags & ImGuiSeparatorFlags_Vertical) @@ -1222,7 +1222,7 @@ void ImGui::Separator() x1 += window->DC.Indent.x; const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); - ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. + ItemSize(ImVec2(0.0f, 1.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit if (!ItemAdd(bb, 0)) { if (window->DC.CurrentColumns) @@ -1252,7 +1252,7 @@ void ImGui::VerticalSeparator() float y1 = window->DC.CursorPos.y; float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y; const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2)); - ItemSize(ImVec2(bb.GetWidth(), 0.0f)); + ItemSize(ImVec2(1.0f, 0.0f)); if (!ItemAdd(bb, 0)) return; From 5d799d76ea78a2f3fcd87cd66bf76fd8ade61a7a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 26 Apr 2019 21:55:09 +0200 Subject: [PATCH 02/13] Internals: Nav scrolling uses InnerMainRect instead of InnerClipRect. --- imgui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2dbc4207..7c85e4e1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8101,14 +8101,14 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) { // Fallback manual-scroll when window has no navigable item if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) - SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight()); + SetWindowScrollY(window, window->Scroll.y - window->InnerMainRect.GetHeight()); else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) - SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight()); + SetWindowScrollY(window, window->Scroll.y + window->InnerMainRect.GetHeight()); } else { const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + const float page_offset_y = ImMax(0.0f, window->InnerMainRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); float nav_scoring_rect_offset_y = 0.0f; if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) { From 61d92580aa772cbe5b64211c738ce1fdf34672e6 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 26 Apr 2019 23:17:24 +0200 Subject: [PATCH 03/13] Window: Fixed contents region being off by WindowBorderSize amount on the right when scrollbar is active. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c51fcc0e..c33d9204 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -58,6 +58,7 @@ Other Changes: One of the noticeable minor side effect was that navigating menus would have had a tendency to disable highlight from parent menu items earlier than necessary while approaching the child menu. - Window: Close button is horizontally aligned with style.FramePadding.x. +- Window: Fixed contents region being off by WindowBorderSize amount on the right when scrollbar is active. - GetMouseDragDelta(): also returns the delta on the mouse button released frame. (#2419) - GetMouseDragDelta(): verify that mouse positions are valid otherwise returns zero. - Inputs: Also add support for horizontal scroll with Shift+Mouse Wheel. (#2424, #1463) [@LucaRood] diff --git a/imgui.cpp b/imgui.cpp index 7c85e4e1..7516081d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5414,8 +5414,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); - window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); - window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); + window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x + window->WindowBorderSize)); + window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y + window->WindowBorderSize)); // Setup drawing context // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) @@ -5517,10 +5517,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } - // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() - window->OuterRectClipped = window->Rect(); - window->OuterRectClipped.ClipWith(window->ClipRect); - // Pressing CTRL+C while holding on a window copy its content to the clipboard // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. // Maybe we can support CTRL+C on every element? @@ -5530,13 +5526,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) LogToClipboard(); */ + // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() + window->OuterRectClipped = window->Rect(); + window->OuterRectClipped.ClipWith(window->ClipRect); + // Inner rectangle // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize; window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize; - window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize; + window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - ImMax(window->ScrollbarSizes.x, window->WindowBorderSize); + window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - ImMax(window->ScrollbarSizes.y, window->WindowBorderSize); //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE); // Inner clipping rectangle From 00b3c830db849551a26dbaccf0cfc8bb2e7fa2b9 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 26 Apr 2019 23:21:30 +0200 Subject: [PATCH 04/13] Internals: Begin: Moved OuterRectClipped/InnerMainRect/InnerClipRect computation higher up in the function, next to ContentsRect/WorkRect code. Removed commented out debug drawing code which is now available in Metrics window. --- imgui.cpp | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7516081d..4f79cec8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5417,6 +5417,24 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x + window->WindowBorderSize)); window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y + window->WindowBorderSize)); + // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() + window->OuterRectClipped = window->Rect(); + window->OuterRectClipped.ClipWith(window->ClipRect); + + // Inner rectangle + // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame + // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. + window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize; + window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); + window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - ImMax(window->ScrollbarSizes.x, window->WindowBorderSize); + window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - ImMax(window->ScrollbarSizes.y, window->WindowBorderSize); + + // Inner clipping rectangle + // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. + window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - window->WindowBorderSize))); + window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y); + window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x * 0.5f - window->WindowBorderSize))); + window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y); // Setup drawing context // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; @@ -5526,26 +5544,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) LogToClipboard(); */ - // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() - window->OuterRectClipped = window->Rect(); - window->OuterRectClipped.ClipWith(window->ClipRect); - - // Inner rectangle - // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame - // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. - window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize; - window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - ImMax(window->ScrollbarSizes.x, window->WindowBorderSize); - window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - ImMax(window->ScrollbarSizes.y, window->WindowBorderSize); - //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE); - - // Inner clipping rectangle - // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. - window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); - window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y); - window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); - window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y); - // 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. window->DC.LastItemId = window->MoveId; From 3d363c91fd32667f9645d0fa84399da7db6b95e4 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 28 Apr 2019 14:49:15 +0200 Subject: [PATCH 05/13] Internals: Exposed ImGuiDataTypeInfo, DataTypeGetInfo(), DataTypeFormatString(). Comments. --- imgui_internal.h | 14 +++++++++++++- imgui_widgets.cpp | 47 ++++++++++++++++++++--------------------------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 0cf8f03b..28a98dc2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -65,6 +65,7 @@ struct ImGuiColorMod; // Stacked color modifier, backup of modifie struct ImGuiColumnData; // Storage data for a single column struct ImGuiColumns; // Storage data for a columns set struct ImGuiContext; // Main imgui context +struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum 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 @@ -501,7 +502,6 @@ struct ImVec1 ImVec1(float _x) { x = _x; } }; - // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -538,6 +538,14 @@ struct IMGUI_API ImRect bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } }; +// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). +struct ImGuiDataTypeInfo +{ + size_t Size; // Size in byte + const char* PrintFmt; // Default printf format for the type + const char* ScanFmt; // Default scanf format for the type +}; + // Stacked color modifier, backup of modified data so we can restore it struct ImGuiColorMod { @@ -1557,6 +1565,10 @@ namespace ImGui template IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos); template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); + // Data type helpers + IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); + IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); + // InputText IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool TempInputTextScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 82bef8fb..0b3d2400 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -105,7 +105,6 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); //------------------------------------------------------------------------- // Data Type helpers -static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); @@ -1539,6 +1538,7 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa // [SECTION] Data Type and Data Formatting Helpers [Internal] //------------------------------------------------------------------------- // - PatchFormatStringFloatToInt() +// - DataTypeGetInfo() // - DataTypeFormatString() // - DataTypeApplyOp() // - DataTypeApplyOpFromText() @@ -1546,13 +1546,6 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa // - RoundScalarWithFormat<>() //------------------------------------------------------------------------- -struct ImGuiDataTypeInfo -{ - size_t Size; - const char* PrintFmt; // Unused - const char* ScanFmt; -}; - static const ImGuiDataTypeInfo GDataTypeInfo[] = { { sizeof(char), "%d", "%d" }, // ImGuiDataType_S8 @@ -1597,7 +1590,13 @@ static const char* PatchFormatStringFloatToInt(const char* fmt) return fmt; } -static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) +const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) +{ + IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); + return &GDataTypeInfo[data_type]; +} + +int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) { // Signedness doesn't matter when pushing integer arguments if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) @@ -1696,11 +1695,12 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. IM_ASSERT(data_type < ImGuiDataType_COUNT); int data_backup[2]; - IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup)); - memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size); + const ImGuiDataTypeInfo* type_info = ImGui::DataTypeGetInfo(data_type); + IM_ASSERT(type_info->Size <= sizeof(data_backup)); + memcpy(data_backup, data_ptr, type_info->Size); if (format == NULL) - format = GDataTypeInfo[data_type].ScanFmt; + format = type_info->ScanFmt; // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. int arg1i = 0; @@ -1769,7 +1769,7 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b IM_ASSERT(0); } - return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0; + return memcmp(data_backup, data_ptr, type_info->Size) != 0; } static float GetMinimumStepAtDecimalPrecision(int decimal_precision) @@ -1990,11 +1990,9 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa return false; // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) + format = DataTypeGetInfo(data_type)->PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) format = PatchFormatStringFloatToInt(format); // Tabbing or CTRL-clicking on Drag turns it into an input box @@ -2435,11 +2433,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co return false; // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) + format = DataTypeGetInfo(data_type)->PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) format = PatchFormatStringFloatToInt(format); // Tabbing or CTRL-clicking on Slider turns it into an input box @@ -2591,11 +2587,9 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d return false; // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) + format = DataTypeGetInfo(data_type)->PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); @@ -2777,9 +2771,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; + format = DataTypeGetInfo(data_type)->PrintFmt; char buf[64]; DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format); From 4e81b2d09388ed9769eb3c5125095d0fd5a5d52c Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 28 Apr 2019 17:05:23 +0200 Subject: [PATCH 06/13] Internals: Renaming. Renamed ImGuiPopupRef to ImGuiPopupData for consistency and added constructor. --- imgui.cpp | 17 +++++++++-------- imgui_internal.h | 12 +++++++----- imgui_widgets.cpp | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4f79cec8..bc1a5025 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1068,6 +1068,7 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); +static int FindWindowFocusIndex(ImGuiWindow* window); // Misc static void UpdateMouseInputs(); @@ -3602,7 +3603,7 @@ void ImGui::NewFrame() // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) - FocusPreviousWindowIgnoringOne(NULL); + FocusTopMostWindowIgnoringOne(NULL); // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. @@ -5002,7 +5003,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); if (flags & ImGuiWindowFlags_Popup) { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; + ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window != popup_ref.Window); } @@ -5035,7 +5036,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) CheckStacksSize(window, true); if (flags & ImGuiWindowFlags_Popup) { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; + ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; popup_ref.Window = window; g.BeginPopupStack.push_back(popup_ref); window->PopupId = popup_ref.PopupId; @@ -5724,7 +5725,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) BringWindowToDisplayFront(window); } -void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window) +void ImGui::FocusTopMostWindowIgnoringOne(ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) @@ -6944,7 +6945,7 @@ void ImGui::OpenPopupEx(ImGuiID id) ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; int current_stack_size = g.BeginPopupStack.Size; - ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. + ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. popup_ref.PopupId = id; popup_ref.Window = NULL; popup_ref.ParentWindow = parent_window; @@ -7008,7 +7009,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) { - ImGuiPopupRef& popup = g.OpenPopupStack[popup_count_to_keep]; + ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep]; if (!popup.Window) continue; IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); @@ -8129,7 +8130,7 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) return 0.0f; } -static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) +static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) { ImGuiContext& g = *GImGui; for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--) @@ -8154,7 +8155,7 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) return; - const int i_current = FindWindowFocusIndex(g.NavWindowingTarget); + const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget); ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); if (!window_target) window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); diff --git a/imgui_internal.h b/imgui_internal.h index 28a98dc2..f76beb1e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -72,7 +72,7 @@ struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() intern struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only struct ImGuiNavMoveResult; // Result of a directional navigation move query result struct ImGuiNextWindowData; // Storage for SetNexWindow** functions -struct ImGuiPopupRef; // Storage for current popup stack +struct ImGuiPopupData; // Storage for current popup stack struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it struct ImGuiTabBar; // Storage for a tab bar @@ -648,7 +648,7 @@ struct ImGuiSettingsHandler }; // Storage for current popup stack -struct ImGuiPopupRef +struct ImGuiPopupData { ImGuiID PopupId; // Set on OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() @@ -657,6 +657,8 @@ struct ImGuiPopupRef ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup + + ImGuiPopupData() { PopupId = 0; Window = ParentWindow = NULL; OpenFrameCount = -1; OpenParentId = 0; } }; struct ImGuiColumnData @@ -856,8 +858,8 @@ struct ImGuiContext ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImVector FontStack; // Stack for PushFont()/PopFont() - ImVector OpenPopupStack; // Which popups are open (persistent) - ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + ImVectorOpenPopupStack; // Which popups are open (persistent) + ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions ImGuiCond NextTreeNodeOpenCond; @@ -1390,7 +1392,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window); + IMGUI_API void FocusTopMostWindowIgnoringOne(ImGuiWindow* ignore_window); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0b3d2400..5cb1975c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5824,7 +5824,7 @@ void ImGui::EndMainMenuBar() // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusPreviousWindowIgnoringOne(g.NavWindow); + FocusTopMostWindowIgnoringOne(g.NavWindow); End(); } From 09db2f6dec01c57e88d45a329550c1dc024ecbb4 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 28 Apr 2019 18:50:51 +0200 Subject: [PATCH 07/13] Fix 61d9258 when there is not scrollbar "Window: Fixed contents region being off by WindowBorderSize amount on the right when scrollbar is active." --- imgui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bc1a5025..61d5a2cf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5413,10 +5413,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update various regions. Variables they depends on are set above in this function. // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. + // NB: WindowBorderSize is included in WindowPadding _and_ ScrollbarSizes so we need to cancel one out. window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); - window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x + window->WindowBorderSize)); - window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y + window->WindowBorderSize)); + window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x + ImMin(window->ScrollbarSizes.x, window->WindowBorderSize))); + window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y + ImMin(window->ScrollbarSizes.y, window->WindowBorderSize))); // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() window->OuterRectClipped = window->Rect(); From 3276b127657ae6f4d813ba38a4366a006d1a73f2 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 28 Apr 2019 20:55:51 +0200 Subject: [PATCH 08/13] Internals: Added DataTypeApplyOp, DataTypeApplyOpFromText to imgui_internal.h --- imgui_internal.h | 2 ++ imgui_widgets.cpp | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index f76beb1e..075d18aa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1570,6 +1570,8 @@ namespace ImGui // Data type helpers IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); + IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); + IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); // InputText IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5cb1975c..c707e0ee 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -104,10 +104,6 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); // [SECTION] Forward Declarations //------------------------------------------------------------------------- -// Data Type helpers -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); - // For InputTextEx() static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); @@ -1619,7 +1615,7 @@ int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type return 0; } -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) +void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) { IM_ASSERT(op == '+' || op == '-'); switch (data_type) @@ -1671,7 +1667,7 @@ static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* // User can input math operators (e.g. +100) to edit a numerical values. // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format) +bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format) { while (ImCharIsBlankA(*buf)) buf++; From bda2cde68eed30c5c1992709c909ad220d226ba4 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 28 Apr 2019 21:52:12 +0200 Subject: [PATCH 09/13] Popups: Closing a popup restores the focused/nav window in place at the time of the popup opening, instead of restoring the window that was in the window stack at the time of the OpenPopup call. (#2517) Among other things, this allows opening a popup while no window are focused, and pressing Escape to clear the focus again. --- docs/CHANGELOG.txt | 4 ++++ docs/TODO.txt | 1 + imgui.cpp | 44 +++++++++++++++++++++++++++----------------- imgui_internal.h | 6 +++--- imgui_widgets.cpp | 5 +++-- 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c33d9204..e6d387b7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -59,6 +59,10 @@ Other Changes: highlight from parent menu items earlier than necessary while approaching the child menu. - Window: Close button is horizontally aligned with style.FramePadding.x. - Window: Fixed contents region being off by WindowBorderSize amount on the right when scrollbar is active. +- Popups: Closing a popup restores the focused/nav window in place at the time of the popup opening, + instead of restoring the window that was in the window stack at the time of the OpenPopup call. (#2517) + Among other things, this allows opening a popup while no window are focused, and pressing Escape to + clear the focus again. - GetMouseDragDelta(): also returns the delta on the mouse button released frame. (#2419) - GetMouseDragDelta(): verify that mouse positions are valid otherwise returns zero. - Inputs: Also add support for horizontal scroll with Shift+Mouse Wheel. (#2424, #1463) [@LucaRood] diff --git a/docs/TODO.txt b/docs/TODO.txt index 9e64b163..f900e7e1 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -291,6 +291,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav/menus: allow pressing Menu to leave a sub-menu. - nav/menus: a way to access the main menu bar with Alt? (currently needs CTRL+TAB) - nav/menus: when using the main menu bar, even though we restore focus after, the underlying window loses its title bar highlight during menu manipulation. could we prevent it? + - nav/menus: main menu bar currently cannot restore a NULL focus. Could save NavWindow at the time of being focused, similarly to what popup do? - nav: simulate right-click or context activation? (SHIFT+F10) - nav: tabs should go through most/all widgets (in submission order?). - nav: when CTRL-Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering. diff --git a/imgui.cpp b/imgui.cpp index 61d5a2cf..e465fdf1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3603,7 +3603,7 @@ void ImGui::NewFrame() // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) - FocusTopMostWindowIgnoringOne(NULL); + FocusTopMostWindowUnderOne(NULL, NULL); // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. @@ -5726,10 +5726,18 @@ void ImGui::FocusWindow(ImGuiWindow* window) BringWindowToDisplayFront(window); } -void ImGui::FocusTopMostWindowIgnoringOne(ImGuiWindow* ignore_window) +void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) + + int start_idx = g.WindowsFocusOrder.Size - 1; + if (under_this_window != NULL) + { + int under_this_window_idx = FindWindowFocusIndex(under_this_window); + if (under_this_window_idx != -1) + start_idx = under_this_window_idx - 1; + } + for (int i = start_idx; i >= 0; i--) { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; @@ -5741,6 +5749,7 @@ void ImGui::FocusTopMostWindowIgnoringOne(ImGuiWindow* ignore_window) return; } } + FocusWindow(NULL); } void ImGui::SetNextItemWidth(float item_width) @@ -6949,7 +6958,7 @@ void ImGui::OpenPopupEx(ImGuiID id) ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. popup_ref.PopupId = id; popup_ref.Window = NULL; - popup_ref.ParentWindow = parent_window; + popup_ref.SourceWindow = g.NavWindow; popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); @@ -7035,24 +7044,25 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under) { - IM_ASSERT(remaining >= 0); ImGuiContext& g = *GImGui; - ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; + IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); + ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; + ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; g.OpenPopupStack.resize(remaining); - // FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely. - // Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing. - // The current code will set focus to the parent of the popup window which is incorrect. - // It rarely manifested until now because UpdateMouseMovingWindowNewFrame() would call FocusWindow() again on the clicked window, - // leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again. - // However if the clicked window has the _NoMove flag set we would be left with B focused. - // For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway, - // but we should inspect and fix this properly. if (apply_focus_to_window_under) { - if (g.NavLayer == 0) - focus_window = NavRestoreLastChildNavWindow(focus_window); - FocusWindow(focus_window); + if (focus_window && !focus_window->WasActive && popup_window) + { + // Fallback + FocusTopMostWindowUnderOne(popup_window, NULL); + } + else + { + if (g.NavLayer == 0 && focus_window) + focus_window = NavRestoreLastChildNavWindow(focus_window); + FocusWindow(focus_window); + } } } diff --git a/imgui_internal.h b/imgui_internal.h index 075d18aa..415dfaea 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -652,13 +652,13 @@ struct ImGuiPopupData { ImGuiID PopupId; // Set on OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() - ImGuiWindow* ParentWindow; // Set on OpenPopup() + ImGuiWindow* SourceWindow; // Set on OpenPopup() copy of NavWindow at the time of opening the popup int OpenFrameCount; // Set on OpenPopup() ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup - ImGuiPopupData() { PopupId = 0; Window = ParentWindow = NULL; OpenFrameCount = -1; OpenParentId = 0; } + ImGuiPopupData() { PopupId = 0; Window = SourceWindow = NULL; OpenFrameCount = -1; OpenParentId = 0; } }; struct ImGuiColumnData @@ -1392,7 +1392,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void FocusTopMostWindowIgnoringOne(ImGuiWindow* ignore_window); + IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c707e0ee..f3e9ecd0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5818,9 +5818,10 @@ void ImGui::EndMainMenuBar() EndMenuBar(); // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window + // FIXME: With this strategy we won't be able to restore a NULL focus. ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusTopMostWindowIgnoringOne(g.NavWindow); + FocusTopMostWindowUnderOne(g.NavWindow, NULL); End(); } @@ -5951,7 +5952,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_within_opened_triangle = false; - if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) + if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) { if (ImGuiWindow* next_window = g.OpenPopupStack[g.BeginPopupStack.Size].Window) { From 842a720e72ff959ec393ae420819b8c05af679a1 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 28 Apr 2019 22:21:23 +0200 Subject: [PATCH 10/13] Popups: Closes popup at the time of FocusWindow(). Fixes right-click from closing all popups instead of aiming at the hovered popup level (regression in 1.67's ae76a1fd). --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 37 +++++++++++++++++++++---------------- imgui_demo.cpp | 7 +++++++ imgui_internal.h | 4 ++-- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e6d387b7..a753438e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -63,6 +63,8 @@ Other Changes: instead of restoring the window that was in the window stack at the time of the OpenPopup call. (#2517) Among other things, this allows opening a popup while no window are focused, and pressing Escape to clear the focus again. +- Popups: Fixed right-click from closing all popups instead of aiming at the hovered popup level + (regression in 1.67). - GetMouseDragDelta(): also returns the delta on the mouse button released frame. (#2419) - GetMouseDragDelta(): verify that mouse positions are valid otherwise returns zero. - Inputs: Also add support for horizontal scroll with Shift+Mouse Wheel. (#2424, #1463) [@LucaRood] diff --git a/imgui.cpp b/imgui.cpp index e465fdf1..d58ba779 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2880,7 +2880,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) return false; - // Test if interactions on this window are blocked by an active popup or modal + // Test if interactions on this window are blocked by an active popup or modal. + // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. if (!IsWindowContentHoverable(window, flags)) return false; @@ -3213,8 +3214,9 @@ void ImGui::UpdateMouseMovingWindowEndFrame() } } - // With right mouse button we close popups without changing focus - // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) + // With right mouse button we close popups without changing focus based on where the mouse is aimed + // Instead, focus will be restored to the window under the bottom-most closed popup. + // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) if (g.IO.MouseClicked[1]) { // Find the top-most window between HoveredWindow and the front most Modal Window. @@ -3231,7 +3233,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (window == g.HoveredWindow) hovered_window_above_modal = true; } - ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); + ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } @@ -3609,7 +3611,7 @@ void ImGui::NewFrame() // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); - ClosePopupsOverWindow(g.NavWindow); + ClosePopupsOverWindow(g.NavWindow, false); // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -5707,6 +5709,9 @@ void ImGui::FocusWindow(ImGuiWindow* window) //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } + // Close popups if any + ClosePopupsOverWindow(window, false); + // Passing NULL allow to disable keyboard focus if (!window) return; @@ -7005,7 +7010,7 @@ bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) return false; } -void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) +void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; if (g.OpenPopupStack.empty()) @@ -7026,23 +7031,23 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) continue; - // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) - bool popup_or_descendent_has_focus = false; - for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_has_focus; m++) + // 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 (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow) - popup_or_descendent_has_focus = true; - if (!popup_or_descendent_has_focus) + popup_or_descendent_is_ref_window = true; + if (!popup_or_descendent_is_ref_window) break; } } if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below { //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); - ClosePopupToLevel(popup_count_to_keep, false); + ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); } } -void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under) +void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); @@ -7050,7 +7055,7 @@ void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under) ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; g.OpenPopupStack.resize(remaining); - if (apply_focus_to_window_under) + if (restore_focus_to_window_under_popup) { if (focus_window && !focus_window->WasActive && popup_window) { @@ -8278,11 +8283,11 @@ static void ImGui::NavUpdateWindowing() // Apply final focus if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { + ClearActiveID(); g.NavDisableHighlight = false; g.NavDisableMouseHover = true; apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); - ClosePopupsOverWindow(apply_focus_window); - ClearActiveID(); + ClosePopupsOverWindow(apply_focus_window, false); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 695fd869..a57cfac4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2209,6 +2209,13 @@ static void ShowDemoWindowPopups() if (ImGui::BeginMenu("Sub-menu")) { ImGui::MenuItem("Click me"); + if (ImGui::Button("Stacked Popup")) + ImGui::OpenPopup("another popup"); + if (ImGui::BeginPopup("another popup")) + { + ImGui::Text("I am the last one here."); + ImGui::EndPopup(); + } ImGui::EndMenu(); } ImGui::EndPopup(); diff --git a/imgui_internal.h b/imgui_internal.h index 415dfaea..23b95257 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1466,8 +1466,8 @@ namespace ImGui // Popups, Modals, Tooltips IMGUI_API void OpenPopupEx(ImGuiID id); - IMGUI_API void ClosePopupToLevel(int remaining, bool apply_focus_to_window_under); - IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); + IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack! IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); From 4dec7447958402b3353a02bbbb7cc4d186b61bae Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 28 Apr 2019 23:49:57 +0200 Subject: [PATCH 11/13] Tidying up BeginMenu() code + comments. --- imgui.cpp | 5 +++-- imgui_widgets.cpp | 47 ++++++++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d58ba779..5a584c90 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7034,8 +7034,9 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to // 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 (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow) - popup_or_descendent_is_ref_window = true; + if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window) + if (popup_window->RootWindow == ref_window->RootWindow) + popup_or_descendent_is_ref_window = true; if (!popup_or_descendent_is_ref_window) break; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f3e9ecd0..874b038a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5947,31 +5947,36 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menuset_is_open) g.NavWindow = backed_nav_window; - bool want_open = false, want_close = false; + bool want_open = false; + bool want_close = false; if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) { + // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. - bool moving_within_opened_triangle = false; - if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) - { - if (ImGuiWindow* next_window = g.OpenPopupStack[g.BeginPopupStack.Size].Window) - { - // FIXME-DPI: Values should be derived from a master "scale" factor. - ImRect next_window_rect = next_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; - ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); - ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); - moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug - } - } + bool moving_toward_other_child_menu = false; - want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); - want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); + ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; + if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) + { + // FIXME-DPI: Values should be derived from a master "scale" factor. + ImRect next_window_rect = child_menu_window->Rect(); + ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + } + if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) + want_close = true; + + if (!menu_is_open && hovered && pressed) // Click to open + want_open = true; + else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open + want_open = true; if (g.NavActivateId == id) { From 4c0f34fd5da00056d003fc0cbd97e7219605caac Mon Sep 17 00:00:00 2001 From: Richard Mitton Date: Sat, 27 Apr 2019 15:38:25 -0700 Subject: [PATCH 12/13] Improved algorithm for mitre joints on thick lines --- imgui_demo.cpp | 2 ++ imgui_draw.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a57cfac4..05311c52 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4095,6 +4095,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_All, th); x += sz + spacing; draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight, th); x += sz + spacing; draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32, th); x += sz + spacing; + draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz*0.6f, y + sz - 0.5f), ImVec2(x + sz*0.4f, y + sz - 0.5f), col32, th); x += sz + spacing; draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col32, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, th); x += sz + spacing; // Diagonal line @@ -4108,6 +4109,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f); x += sz + spacing; draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight); x += sz + spacing; draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32); x += sz + spacing; + draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz*0.6f, y + sz - 0.5f), ImVec2(x + sz*0.4f, y + sz - 0.5f), col32); x += sz + spacing; draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col32); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col32); x += spacing + spacing; // Vertical line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col32); x += sz; // Pixel (faster than AddLine) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 290fadb8..95903660 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -664,6 +664,7 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c // Those macros expects l-values. #define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } #define IM_NORMALIZE2F_OVER_EPSILON_CLAMP(VX,VY,EPS,INVLENMAX) { float d2 = VX*VX + VY*VY; if (d2 > EPS) { float inv_len = 1.0f / ImSqrt(d2); if (inv_len > INVLENMAX) inv_len = INVLENMAX; VX *= inv_len; VY *= inv_len; } } +#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. @@ -725,7 +726,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // 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_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f) + IM_FIXNORMAL2F(dm_x, dm_y) dm_x *= AA_SIZE; dm_y *= AA_SIZE; @@ -780,7 +781,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // 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_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f); + IM_FIXNORMAL2F(dm_x, dm_y); float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE); float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE); float dm_in_x = dm_x * half_inner_thickness; @@ -900,7 +901,7 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun const ImVec2& n1 = temp_normals[i1]; float dm_x = (n0.x + n1.x) * 0.5f; float dm_y = (n0.y + n1.y) * 0.5f; - IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f); + IM_FIXNORMAL2F(dm_x, dm_y); dm_x *= AA_SIZE * 0.5f; dm_y *= AA_SIZE * 0.5f; From 0f2852806c7176a7017d3bbcdc711363651c696b Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Apr 2019 12:44:17 +0200 Subject: [PATCH 13/13] Amend 48a09a7 with changelog, breaking changes, tweak demo code for spacing. (#2518) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 1 + imgui_demo.cpp | 60 ++++++++++++++++++++++++---------------------- imgui_draw.cpp | 5 ++-- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a753438e..bc326aff 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -34,6 +34,8 @@ HOW TO UPDATE? ----------------------------------------------------------------------- Breaking Changes: +- ImDrawList: Fixed rectangles with thick lines (>1.0f) not being as thick as requested. (#2518) + If you have custom rendering using thick lines, they will appear thicker now. - Examples: Vulkan: Added MinImageCount/ImageCount fields in ImGui_ImplVulkan_InitInfo, required during initialization to specify the number of in-flight image requested by swap chains. (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). (#2071, #1677) [@nathanvoglsam] @@ -73,6 +75,8 @@ Other Changes: - Separator: Declare its thickness (1.0f) to the layout, making items around separator more symmetrical. - Combo, Slider, Scrollbar: Improve rendering in situation when there's only a few pixels available (<3 pixels). - Nav: Fixed Drag/Slider functions going into text input mode when keyboard CTRL is held while pressing NavActivate. +- ImDrawList: Improved algorithm for mitre joints on thick lines, preserving correct thickness up to 90 degrees + angles, also faster to output. (#2518) [@rmitton] - Misc: Added IM_MALLOC/IM_FREE macros mimicking IM_NEW/IM_DELETE so user doesn't need to revert to using the ImGui::MemAlloc()/MemFree() calls directly. - Metrics: Added "Show windows rectangles" tool to visualize the different rectangles. diff --git a/imgui.cpp b/imgui.cpp index 5a584c90..61f410a7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -369,6 +369,7 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2019/04/29 (1.70) - fixed ImDrawList rectangles with thick lines (>1.0f) not being as thick as requested. If you have custom rendering using rectangles with thick lines, they will appear thicker now. - 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! diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 05311c52..33f33af8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4077,44 +4077,48 @@ static void ShowExampleAppCustomRendering(bool* p_open) if (ImGui::BeginTabItem("Primitives")) { static float sz = 36.0f; - static float thickness = 4.0f; - static ImVec4 col = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); + static float thickness = 3.0f; + static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); - ImGui::ColorEdit4("Color", &col.x); + ImGui::ColorEdit4("Color", &colf.x); const ImVec2 p = ImGui::GetCursorScreenPos(); - const ImU32 col32 = ImColor(col); - float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; + const ImU32 col = ImColor(colf); + float x = p.x + 4.0f, y = p.y + 4.0f; + float spacing = 10.0f; + ImDrawCornerFlags corners_none = 0; + ImDrawCornerFlags corners_all = ImDrawCornerFlags_All; + ImDrawCornerFlags corners_tl_br = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight; for (int n = 0; n < 2; n++) { - // First line uses a thickness of 1.0, second line uses the configurable thickness + // First line uses a thickness of 1.0f, second line uses the configurable thickness float th = (n == 0) ? 1.0f : thickness; - draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6, th); x += sz + spacing; // Hexagon - draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 20, th); x += sz + spacing; // Circle - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 0.0f, ImDrawCornerFlags_All, th); x += sz + spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_All, th); x += sz + spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight, th); x += sz + spacing; - draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32, th); x += sz + spacing; - draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz*0.6f, y + sz - 0.5f), ImVec2(x + sz*0.4f, y + sz - 0.5f), col32, th); x += sz + spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col32, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 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), col32, th); + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 6, th); x += sz + spacing; // Hexagon + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 20, 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->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6); x += sz + spacing; // Hexagon - draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 32); x += sz + spacing; // Circle - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32); x += sz + spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f); x += sz + spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight); x += sz + spacing; - draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32); x += sz + spacing; - draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz*0.6f, y + sz - 0.5f), ImVec2(x + sz*0.4f, y + sz - 0.5f), col32); x += sz + spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col32); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col32); x += spacing + spacing; // Vertical line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col32); x += sz; // Pixel (faster than AddLine) + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 6); x += sz + spacing; // Hexagon + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, 32); 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.5f, (sz + spacing) * 3)); + ImGui::Dummy(ImVec2((sz + spacing) * 9.8f, (sz + spacing) * 3)); ImGui::EndTabItem(); } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 95903660..5d493600 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -662,9 +662,8 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds. // Those macros expects l-values. -#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } -#define IM_NORMALIZE2F_OVER_EPSILON_CLAMP(VX,VY,EPS,INVLENMAX) { float d2 = VX*VX + VY*VY; if (d2 > EPS) { float inv_len = 1.0f / ImSqrt(d2); if (inv_len > INVLENMAX) inv_len = INVLENMAX; VX *= inv_len; VY *= inv_len; } } -#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } +#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 < 0.5f) d2 = 0.5f; float inv_lensq = 1.0f / d2; VX *= inv_lensq; VY *= inv_lensq; } // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.