From fbabf651f4285b9b46428f7d9accb21cedbf9624 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 25 Sep 2020 13:20:57 +0200 Subject: [PATCH 1/6] Style: Renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. Fixed README links. --- docs/CHANGELOG.txt | 1 + docs/README.md | 4 ++-- imgui.cpp | 6 +++--- imgui.h | 2 +- imgui_widgets.cpp | 3 ++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4be7d31d..1a3980cb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,6 +42,7 @@ Breaking Changes: It was also getting in the way of better font scaling, so let's get rid of it now! If you used DisplayOffset it was probably in association to rasterizing a font at a specific size, in which case the corresponding offset may be reported into GlyphOffset. (#1619) +- Style: Renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. - Renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), REVERTED CHANGE FROM 1.77. For variety of reason this is more self-explanatory. - Removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it diff --git a/docs/README.md b/docs/README.md index 41279f60..f696b274 100644 --- a/docs/README.md +++ b/docs/README.md @@ -85,7 +85,7 @@ Dear ImGui allows you to **create elaborate tools** as well as very short-lived ### How it works -Check out the Wiki's [About the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#About-the-IMGUI-paradigm) section if you want to understand the core principles behind the IMGUI paradigm. An IMGUI tries to minimize superfluous state duplication, state synchronization and state retention from the user's point of view. It is less error prone (less code and less bugs) than traditional retained-mode interfaces, and lends itself to create dynamic user interfaces. +Check out the Wiki's [About the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) section if you want to understand the core principles behind the IMGUI paradigm. An IMGUI tries to minimize superfluous state duplication, state synchronization and state retention from the user's point of view. It is less error prone (less code and less bugs) than traditional retained-mode interfaces, and lends itself to create dynamic user interfaces. Dear ImGui outputs vertex buffers and command lists that you can easily render in your application. The number of draw calls and state changes required to render them is fairly small. Because Dear ImGui doesn't know or touch graphics state directly, you can call its functions anywhere in your code (e.g. in the middle of a running algorithm, or in the middle of your own rendering process). Refer to the sample applications in the examples/ folder for instructions on how to integrate Dear ImGui with your existing codebase. @@ -149,7 +149,7 @@ See: [Frequently Asked Questions (FAQ)](https://github.com/ocornut/imgui/blob/ma See: [Wiki](https://github.com/ocornut/imgui/wiki) for many links, references, articles. -See: [Articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#Articles-about-the-IMGUI-paradigm) to read/learn about the Immediate Mode GUI paradigm. +See: [Articles about the IMGUI paradigm](https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm) to read/learn about the Immediate Mode GUI paradigm. If you are new to Dear ImGui and have issues with: compiling, linking, adding fonts, wiring inputs, running or displaying Dear ImGui: you can use [Discord server](http://discord.dearimgui.org). diff --git a/imgui.cpp b/imgui.cpp index f66cd751..9b337556 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -372,6 +372,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. + - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory. - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result. - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. It was also getting in the way of better font scaling, so let's get rid of it now! @@ -942,7 +943,7 @@ ImGuiStyle::ImGuiStyle() 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. + TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. @@ -981,8 +982,7 @@ void ImGuiStyle::ScaleAllSizes(float 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); + TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); diff --git a/imgui.h b/imgui.h index 4fc23e83..439429cc 100644 --- a/imgui.h +++ b/imgui.h @@ -1474,7 +1474,7 @@ struct ImGuiStyle 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. + float TabMinWidthForCloseButton; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2fef410d..a3630747 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5943,6 +5943,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ItemSize(size, 0.0f); // Fill horizontal space + // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitely right-aligned sizes not visibly match other widgets. const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) @@ -7804,7 +7805,7 @@ bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, bool close_button_pressed = false; bool close_button_visible = false; if (close_button_id != 0) - if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForUnselectedCloseButton) + if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForCloseButton) if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) close_button_visible = true; if (close_button_visible) From 324e0310ad39f7467184631493eccfd11d161ddd Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 25 Sep 2020 13:34:31 +0200 Subject: [PATCH 2/6] Renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. (#1829, #3209, #946, #413) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 1 + imgui.h | 7 ++++++- imgui_demo.cpp | 10 +++++----- imgui_widgets.cpp | 15 +++++++-------- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1a3980cb..d3dad8a0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,7 @@ Breaking Changes: If you used DisplayOffset it was probably in association to rasterizing a font at a specific size, in which case the corresponding offset may be reported into GlyphOffset. (#1619) - Style: Renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. +- Renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete). - Renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), REVERTED CHANGE FROM 1.77. For variety of reason this is more self-explanatory. - Removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it @@ -161,6 +162,7 @@ Other Changes: which was obsoleted. (#1823, #1316, #642) [@Shironekoben, @AndrewBelt] - Added ImGuiSliderFlags_ClampOnInput flag to force clamping value when using CTRL+Click to type in a value manually. (#1829, #3209, #946, #413). + [note: RENAMED to ImGuiSliderFlags_AlwaysClamp in 1.79]. - Added ImGuiSliderFlags_NoRoundToFormat flag to disable rounding underlying value to match precision of the display format string. (#642) - Added ImGuiSliderFlags_NoInput flag to disable turning widget into a text input diff --git a/imgui.cpp b/imgui.cpp index 9b337556..b1a04674 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -372,6 +372,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. + - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently). - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory. - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result. diff --git a/imgui.h b/imgui.h index 439429cc..8d0ca495 100644 --- a/imgui.h +++ b/imgui.h @@ -1302,11 +1302,16 @@ enum ImGuiColorEditFlags_ 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_AlwaysClamp = 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. + + // Obsolete names (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + , ImGuiSliderFlags_ClampOnInput = ImGuiSliderFlags_AlwaysClamp, // [renamed in 1.79] +#endif }; // Identify a mouse button. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c728bbe1..749d1160 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -617,7 +617,7 @@ static void ShowDemoWindowWidgets() "Hold SHIFT/ALT for faster/slower edit.\n" "Double-click or CTRL+click to input value."); - ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_ClampOnInput); + ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp); static float f1 = 1.00f, f2 = 0.0067f; ImGui::DragFloat("drag float", &f1, 0.005f); @@ -1558,7 +1558,7 @@ static void ShowDemoWindowWidgets() { // 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::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", (unsigned int*)&flags, ImGuiSliderFlags_AlwaysClamp); 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)."); @@ -1591,7 +1591,7 @@ static void ShowDemoWindowWidgets() { static float begin = 10, end = 90; static int begin_i = 100, end_i = 1000; - ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_ClampOnInput); + ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_AlwaysClamp); 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(); @@ -4029,9 +4029,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", ImGuiSliderFlags_ClampOnInput)) // Scale only this window + if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // 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::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything ImGui::PopItemWidth(); ImGui::EndTabItem(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a3630747..fd802737 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2242,8 +2242,8 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (temp_input_is_active) { - // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set - const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0 && (p_min == NULL || p_max == NULL || DataTypeCompare(data_type, p_min, p_max) < 0); + // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set + const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0 && (p_min == NULL || p_max == NULL || DataTypeCompare(data_type, p_min, p_max) < 0); return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); } @@ -2324,7 +2324,7 @@ bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags); } -// NB: You likely want to specify the ImGuiSliderFlags_ClampOnInput when using this. +// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp 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(); @@ -2377,7 +2377,7 @@ bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags); } -// NB: You likely want to specify the ImGuiSliderFlags_ClampOnInput when using this. +// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp 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(); @@ -2849,8 +2849,8 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (temp_input_is_active) { - // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampInput is set - const bool is_clamp_input = (flags & ImGuiSliderFlags_ClampOnInput) != 0; + // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set + const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0; return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL); } @@ -3178,8 +3178,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* return value_changed; } -// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the -// ImGuiSliderFlags_ClampOnInput / ImGuiSliderFlags_ClampOnInput flag is set! +// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp 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. 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) From 52c0b1a3406abcbbb9df593b88bb2dd6ffe0290c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 24 Sep 2020 18:08:01 +0200 Subject: [PATCH 3/6] ImGuiListClipper: internal rework and tidying up to facilitate supporting frozen rows in tables + stop promoting using constructors parameters. --- imgui.cpp | 130 ++++++++++++++++++++++++++++++---------------- imgui.h | 9 ++-- imgui_demo.cpp | 9 ++-- imgui_widgets.cpp | 3 +- 4 files changed, 99 insertions(+), 52 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b1a04674..dac97c99 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2192,35 +2192,44 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly } -// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1 +ImGuiListClipper::ImGuiListClipper(int items_count, float items_height) +{ + DisplayStart = DisplayEnd = 0; + ItemsCount = -1; + StepNo = 0; + ItemsHeight = StartPosY = 0.0f; + Begin(items_count, items_height); +} + +ImGuiListClipper::~ImGuiListClipper() +{ + IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); +} + +// Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 // Use case B: Begin() called from constructor with items_height>0 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. -void ImGuiListClipper::Begin(int count, float items_height) +void ImGuiListClipper::Begin(int items_count, float items_height) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; StartPosY = window->DC.CursorPos.y; ItemsHeight = items_height; - ItemsCount = count; + ItemsCount = items_count; StepNo = 0; - DisplayEnd = DisplayStart = -1; - if (ItemsHeight > 0.0f) - { - ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display - if (DisplayStart > 0) - SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor - StepNo = 2; - } + DisplayStart = -1; + DisplayEnd = 0; } void ImGuiListClipper::End() { - if (ItemsCount < 0) + if (ItemsCount < 0) // Already ended 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) - SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor + if (ItemsCount < INT_MAX && DisplayStart >= 0) + SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); ItemsCount = -1; StepNo = 3; } @@ -2230,38 +2239,70 @@ bool ImGuiListClipper::Step() ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (ItemsCount == 0 || window->SkipItems) + // Reached end of list + if (DisplayEnd >= ItemsCount || window->SkipItems) { + End(); + return false; + } + + // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) + if (StepNo == 0) + { + StartPosY = window->DC.CursorPos.y; + if (ItemsHeight <= 0.0f) + { + // Submit the first item so we can measure its height (generally it is 0..1) + DisplayStart = 0; + DisplayEnd = 1; + StepNo = 1; + return true; + } + + // Already has item height (given by user in Begin): skip to calculating step + DisplayStart = DisplayEnd; + StepNo = 2; + } + + // Step 1: the clipper infer height from first element + if (StepNo == 1) + { + IM_ASSERT(ItemsHeight <= 0.0f); + ItemsHeight = window->DC.CursorPos.y - StartPosY; + IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); + StepNo = 2; + } + + // Step 2: calculate the actual range of elements to display, and position the cursor before the first element + if (StepNo == 2) + { + IM_ASSERT(ItemsHeight > 0.0f); + + int already_submitted = DisplayEnd; + ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); + DisplayStart += already_submitted; + DisplayEnd += already_submitted; + + // Seek cursor + if (DisplayStart > already_submitted) + SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); + + StepNo = 3; + return true; + } + + // 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. + if (StepNo == 3) + { + // Seek cursor + if (ItemsCount < INT_MAX) + SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor ItemsCount = -1; return false; } - if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height. - { - DisplayStart = 0; - DisplayEnd = 1; - StartPosY = window->DC.CursorPos.y; - StepNo = 1; - return true; - } - if (StepNo == 1) // 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. - { - if (ItemsCount == 1) { ItemsCount = -1; return false; } - float items_height = window->DC.CursorPos.y - StartPosY; - IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically - Begin(ItemsCount - 1, items_height); - DisplayStart++; - DisplayEnd++; - StepNo = 3; - return true; - } - 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; - return true; - } - if (StepNo == 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. - End(); + + IM_ASSERT(0); return false; } @@ -8062,7 +8103,7 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. if (policy == ImGuiPopupPositionPolicy_Tooltip) - return ref_pos + ImVec2(2, 2); + return ref_pos + ImVec2(2, 2); // Otherwise try to keep within display ImVec2 pos = ref_pos; @@ -10439,7 +10480,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false); // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. - ImGuiListClipper clipper(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. + ImGuiListClipper clipper; + clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. while (clipper.Step()) for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) { diff --git a/imgui.h b/imgui.h index 8d0ca495..67516adc 100644 --- a/imgui.h +++ b/imgui.h @@ -1900,7 +1900,8 @@ struct ImGuiStorage // - 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 { - int DisplayStart, DisplayEnd; + int DisplayStart; + int DisplayEnd; int ItemsCount; // [Internal] @@ -1911,12 +1912,12 @@ struct ImGuiListClipper // items_count: Use -1 to ignore (you can call Begin later). Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). // If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step(). - ImGuiListClipper(int items_count = -1, float items_height = -1.0f) { Begin(items_count, items_height); } // NB: Begin() initialize every fields (as we allow user to call Begin/End multiple times on a same instance if they want). - ~ImGuiListClipper() { IM_ASSERT(ItemsCount == -1); } // Assert if user forgot to call End() or Step() until false. + ImGuiListClipper(int items_count = -1, float items_height = -1.0f); + ~ImGuiListClipper(); - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. }; // Helpers macros to generate 32-bit encoded colors diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 749d1160..96c0034e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3340,7 +3340,8 @@ static void ShowDemoWindowColumns() ImGui::BeginChild("##ScrollingRegion", child_size, false, ImGuiWindowFlags_HorizontalScrollbar); ImGui::Columns(10); int ITEMS_COUNT = 2000; - ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list + ImGuiListClipper clipper; // Also demonstrate using the clipper for large list + clipper.Begin(ITEMS_COUNT); while (clipper.Step()) { for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) @@ -4330,7 +4331,8 @@ struct ExampleAppConsole // To use the clipper we can replace your standard loop: // for (int i = 0; i < Items.Size; i++) // With: - // ImGuiListClipper clipper(Items.Size); + // ImGuiListClipper clipper; + // clipper.Begin(Items.Size); // while (clipper.Step()) // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) // - That your items are evenly spaced (same height) @@ -4897,7 +4899,8 @@ static void ShowExampleAppLongText(bool* p_open) { // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGuiListClipper clipper(lines); + ImGuiListClipper clipper; + clipper.Begin(lines); while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index fd802737..c5b44f80 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6154,7 +6154,8 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. ImGuiContext& g = *GImGui; bool value_changed = false; - ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. + ImGuiListClipper clipper; + clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { From 179dc04d8a3969654def9c60c7db31864b07580c Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 30 Sep 2020 14:22:36 +0200 Subject: [PATCH 4/6] Examples: Added missing comments in example_apple_metal. (#3400) --- examples/example_apple_metal/README.md | 4 +- .../example_apple_metal/Shared/Renderer.mm | 37 ++++++++++++++++--- examples/example_apple_opengl2/main.mm | 4 +- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/examples/example_apple_metal/README.md b/examples/example_apple_metal/README.md index 4f620327..c13df2f1 100644 --- a/examples/example_apple_metal/README.md +++ b/examples/example_apple_metal/README.md @@ -4,5 +4,7 @@ This example shows how to integrate Dear ImGui with Metal. It is based on the "cross-platform" game template provided with Xcode as of Xcode 9. -(NB: you may still want to use GLFW or SDL which will also support Windows, Linux along with OSX.) +Consider basing your work off the example_glfw_metal/ or example_sdl_metal/ examples. They are better supported and will be portable unlike this one. + + diff --git a/examples/example_apple_metal/Shared/Renderer.mm b/examples/example_apple_metal/Shared/Renderer.mm index efc3332b..3f7e32d1 100644 --- a/examples/example_apple_metal/Shared/Renderer.mm +++ b/examples/example_apple_metal/Shared/Renderer.mm @@ -15,7 +15,7 @@ @implementation Renderer --(nonnull instancetype)initWithView:(nonnull MTKView *)view; +-(nonnull instancetype)initWithView:(nonnull MTKView*)view; { self = [super init]; if(self) @@ -23,17 +23,41 @@ _device = view.device; _commandQueue = [_device newCommandQueue]; + // Setup Dear ImGui context + // FIXME: This example doesn't have proper cleanup... IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGui::StyleColorsDark(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Setup Renderer bindings ImGui_ImplMetal_Init(_device); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Read 'docs/FONTS.txt' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); } return self; } -- (void)drawInMTKView:(MTKView *)view +- (void)drawInMTKView:(MTKView*)view { ImGuiIO &io = ImGui::GetIO(); io.DisplaySize.x = view.bounds.size.width; @@ -50,11 +74,12 @@ id commandBuffer = [self.commandQueue commandBuffer]; + // Our state (make them static = more or less global) as a convenience to keep the example terse. static bool show_demo_window = true; static bool show_another_window = false; static float clear_color[4] = { 0.28f, 0.36f, 0.5f, 1.0f }; - MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; + MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; if (renderPassDescriptor != nil) { renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); @@ -110,7 +135,7 @@ // Rendering ImGui::Render(); - ImDrawData *drawData = ImGui::GetDrawData(); + ImDrawData* drawData = ImGui::GetDrawData(); ImGui_ImplMetal_RenderDrawData(drawData, commandBuffer, renderEncoder); [renderEncoder popDebugGroup]; @@ -122,7 +147,7 @@ [commandBuffer commit]; } -- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size +- (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size { } diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 7cbf3409..133f928a 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -45,7 +45,7 @@ ImGui_ImplOSX_NewFrame(self); ImGui::NewFrame(); - // Global data for the demo + // Our state (make them static = more or less global) as a convenience to keep the example terse. static bool show_demo_window = true; static bool show_another_window = false; static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); @@ -243,10 +243,12 @@ NSLog(@"No OpenGL Context!"); // Setup Dear ImGui context + // FIXME: This example doesn't have proper cleanup... IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls // Setup Dear ImGui style ImGui::StyleColorsDark(); From 3be352fc80d38da2cb72e54fc76439c2a8d72720 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 28 Sep 2020 12:29:54 +0300 Subject: [PATCH 5/6] CI: Add discord notifications. --- .github/workflows/build.yml | 40 +++++++++++++++++++++++++++ .github/workflows/static-analysis.yml | 8 ++++++ 2 files changed, 48 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eea8eaa3..83c862da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -179,6 +179,14 @@ jobs: shell: cmd run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx12/example_win32_directx12.vcxproj /p:Platform=x64 /p:Configuration=Release' + - uses: sarisia/actions-status-discord@v1 + if: failure() + with: + webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} + username: GitHub Actions + color: 0xFF0000 + title: ${{ github.job }} + Linux: runs-on: ubuntu-20.04 steps: @@ -310,6 +318,14 @@ jobs: - name: Build example_sdl_opengl3 run: make -C examples/example_sdl_opengl3 + - uses: sarisia/actions-status-discord@v1 + if: failure() + with: + webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} + username: GitHub Actions + color: 0xFF0000 + title: ${{ github.job }} + MacOS: runs-on: macOS-latest steps: @@ -365,6 +381,14 @@ jobs: - name: Build example_apple_opengl2 run: xcodebuild -project examples/example_apple_opengl2/example_apple_opengl2.xcodeproj -target example_osx_opengl2 + - uses: sarisia/actions-status-discord@v1 + if: failure() + with: + webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} + username: GitHub Actions + color: 0xFF0000 + title: ${{ github.job }} + iOS: runs-on: macOS-latest steps: @@ -377,6 +401,14 @@ jobs: # Code signing is required, but we disable it because it is irrelevant for CI builds. xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_ios CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + - uses: sarisia/actions-status-discord@v1 + if: failure() + with: + webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} + username: GitHub Actions + color: 0xFF0000 + title: ${{ github.job }} + Emscripten: runs-on: ubuntu-18.04 steps: @@ -398,3 +430,11 @@ jobs: source ./emsdk_env.sh popd make -C examples/example_emscripten + + - uses: sarisia/actions-status-discord@v1 + if: failure() + with: + webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} + username: GitHub Actions + color: 0xFF0000 + title: ${{ github.job }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 7967d532..3681ecb2 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -41,3 +41,11 @@ jobs: pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1 pvs-studio-analyzer analyze -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log plog-converter -a 'GA:1,2;OP:1' -t errorfile -w pvs-studio.log + + - uses: sarisia/actions-status-discord@v1 + if: failure() + with: + webhook: ${{ secrets.DISCORD_CI_WEBHOOK }} + username: GitHub Actions + color: 0xFF0000 + title: ${{ github.job }} From 5f336ce8f83ff6924fa264b74b5405353f4cecb3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 1 Oct 2020 13:27:24 +0200 Subject: [PATCH 6/6] Tab Bar: Fixed buffer underflow in TabBarLayout, introduced by 4a57a982b (#3501, #3291) + Link to CI actions added in 3be352f --- .github/workflows/build.yml | 4 ++++ imgui_widgets.cpp | 15 +++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 83c862da..a15d5e23 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -318,6 +318,7 @@ jobs: - name: Build example_sdl_opengl3 run: make -C examples/example_sdl_opengl3 + # Use https://github.com/marketplace/actions/actions-status-discord to send status to Discord - uses: sarisia/actions-status-discord@v1 if: failure() with: @@ -381,6 +382,7 @@ jobs: - name: Build example_apple_opengl2 run: xcodebuild -project examples/example_apple_opengl2/example_apple_opengl2.xcodeproj -target example_osx_opengl2 + # Use https://github.com/marketplace/actions/actions-status-discord to send status to Discord - uses: sarisia/actions-status-discord@v1 if: failure() with: @@ -401,6 +403,7 @@ jobs: # Code signing is required, but we disable it because it is irrelevant for CI builds. xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_ios CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + # Use https://github.com/marketplace/actions/actions-status-discord to send status to Discord - uses: sarisia/actions-status-discord@v1 if: failure() with: @@ -431,6 +434,7 @@ jobs: popd make -C examples/example_emscripten + # Use https://github.com/marketplace/actions/actions-status-discord to send status to Discord - uses: sarisia/actions-status-discord@v1 if: failure() with: diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c5b44f80..33ddb376 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6999,13 +6999,16 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab->IndexDuringLayout = (ImS8)tab_dst_n; // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) - ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; int curr_tab_section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; - int prev_tab_section_n = (prev_tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (prev_tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; - if (tab_dst_n > 0 && curr_tab_section_n == 0 && prev_tab_section_n != 0) - need_sort_by_section = true; - if (tab_dst_n > 0 && prev_tab_section_n == 2 && curr_tab_section_n != 2) - need_sort_by_section = true; + if (tab_dst_n > 0) + { + ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; + int prev_tab_section_n = (prev_tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (prev_tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + if (curr_tab_section_n == 0 && prev_tab_section_n != 0) + need_sort_by_section = true; + if (prev_tab_section_n == 2 && curr_tab_section_n != 2) + need_sort_by_section = true; + } sections[curr_tab_section_n].TabCount++; tab_dst_n++;