Tab Bar: Various fixes. Tried to reduce code complexity. (#3291)
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index d1f5fb2..4be7d31 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -65,9 +65,9 @@
 - InputText: Fixed minor scrolling glitch when erasing trailing lines in InputTextMultiline().
 - InputText: Fixed cursor being partially covered after using Ctrl+End key.
 - InputText: Fixed callback's helper DeleteChars() function when cursor is inside the deleted block. (#3454)
-- InputText: Made pressing Down arrow on the last line when it doesn't have a carriage return not move to the end
-  of the line (so it is consistent with Up arrow, and behave same as Notepad and Visual Studio. Note that some 
-  other text editors instead would move the crusor to the end of the line). [@Xipiryon]
+- InputText: Made pressing Down arrow on the last line when it doesn't have a carriage return not move to
+  the end of the line (so it is consistent with Up arrow, and behave same as Notepad and Visual Studio. 
+  Note that some other text editors instead would move the crusor to the end of the line). [@Xipiryon]
 - DragFloat, DragScalar: Fixed ImGuiSliderFlags_ClampOnInput not being honored in the special case
   where v_min == v_max. (#3361)
 - SliderInt, SliderScalar: Fixed reaching of maximum value with inverted integer min/max ranges, both
@@ -80,17 +80,17 @@
   rather than the Mouse Down+Up sequence, even if the _OpenOnArrow flag isn't set. This is standard behavior
   and amends the change done in 1.76 which only affected cases were _OpenOnArrow flag was set.
   (This is also necessary to support full multi/range-select/drag and drop operations.)
+- Tab Bar: Added TabItemButton() to submit tab that behave like a button. (#3291) [@Xipiryon]
+- Tab Bar: Added ImGuiTabItemFlags_Leading and ImGuiTabItemFlags_Trailing flags to position tabs or button 
+  at either end of the tab bar. Those tabs won't be part of the scrolling region, and when reordering cannot
+  be moving outside of their section. Most often used with TabItemButton(). (#3291) [@Xipiryon]
+- Tab Bar: Added ImGuiTabItemFlags_NoReorder flag to disable reordering a given tab.
 - Tab Bar: Keep tab item close button visible while dragging a tab (independent of hovering state).
 - Tab Bar: Fixed a small bug where closing a tab that is not selected would leave a tab hole for a frame.
 - Tab Bar: Fixed a small bug where scrolling buttons (with ImGuiTabBarFlags_FittingPolicyScroll) would
   generate an unnecessary extra draw call.
 - Tab Bar: Fixed a small bug where toggling a tab bar from Reorderable to not Reorderable would leave
   tabs reordered in the tab list popup. [@Xipiryon]
-- Tab Bar: Added TabItemButton() to submit tab that behave like a button. (#3291) [@Xipiryon]
-- Tab Bar: Added ImGuiTabItemFlags_Leading and ImGuiTabItemFlags_Trailing flags to position tabs or button at
-  either end of the tab bar. Those tabs won't be part of the scrolling region, won't shrink down, and when
-  reordering cannot be moving outside of their section. Most often used with TabItemButton(). (#3291) [@Xipiryon]
-- Tab Bar: Added ImGuiTabItemFlags_NoReorder flag to disable reordering a given tab.
 - Columns: Fix inverted ClipRect being passed to renderer when using certain primitives inside of
   a fully clipped column. (#3475) [@szreder]
 - Popups, Tooltips: Fix edge cases issues with positionning popups and tooltips when they are larger than
diff --git a/imgui.h b/imgui.h
index 91a0b16..c723434 100644
--- a/imgui.h
+++ b/imgui.h
@@ -957,8 +957,8 @@
     ImGuiTabItemFlags_NoPushId                      = 1 << 3,   // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()
     ImGuiTabItemFlags_NoTooltip                     = 1 << 4,   // Disable tooltip for the given tab
     ImGuiTabItemFlags_NoReorder                     = 1 << 5,   // Disable reordering this tab or having another tab cross over this tab
-    ImGuiTabItemFlags_Leading                       = 1 << 6,   // Enforce the tab position to the left of the tab bar (after the tab list popup button) and disable resizing down
-    ImGuiTabItemFlags_Trailing                      = 1 << 7    // Enforce the tab position to the right of the tab bar (before the scrolling buttons) and disable resizing down
+    ImGuiTabItemFlags_Leading                       = 1 << 6,   // Enforce the tab position to the left of the tab bar (after the tab list popup button)
+    ImGuiTabItemFlags_Trailing                      = 1 << 7    // Enforce the tab position to the right of the tab bar (before the scrolling buttons)
 };
 
 // Flags for ImGui::IsWindowFocused()
diff --git a/imgui_internal.h b/imgui_internal.h
index e61fbed..796e03e 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -1704,7 +1704,7 @@
 enum ImGuiTabItemFlagsPrivate_
 {
     ImGuiTabItemFlags_NoCloseButton             = 1 << 20,  // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)
-    ImGuiTabItemFlags_Button                    = 1 << 21   // [Internal] Used by TabItemButton, change the tab item behavior to mimic a button
+    ImGuiTabItemFlags_Button                    = 1 << 21   // Used by TabItemButton, change the tab item behavior to mimic a button
 };
 
 // Storage for one active tab item (sizeof() 28~32 bytes)
@@ -1732,6 +1732,7 @@
     float               Width;
     float               WidthIdeal;
     float               InnerSpacing;   // Horizontal ItemInnerSpacing, used by Leading/Trailing section, to correctly offset from Central section
+    float               WidthWithSpacing() const { return Width + InnerSpacing; }
     ImGuiTabBarSection(){ memset(this, 0, sizeof(*this)); }
 };
 
@@ -1761,6 +1762,7 @@
     bool                WantLayout;
     bool                VisibleTabWasSubmitted;
     short               LastTabItemIdx;         // Index of last BeginTabItem() tab for use by EndTabItem() 
+    bool                TabsAddedNew;           // Set to true when a new tab item or button has been added to the tab bar during last frame
     ImVec2              FramePadding;           // style.FramePadding locked at the time of BeginTabBar()
     ImGuiTextBuffer     TabsNames;              // For non-docking tab bar we re-append names in a contiguous buffer.
 
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 3628b16..2ddbbb8 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -6785,7 +6785,6 @@
 // - BeginTabBarEx() [Internal]
 // - EndTabBar()
 // - TabBarLayout() [Internal]
-// - TabBarLayoutComputeTabsWidth() [Internal]
 // - TabBarCalcTabID() [Internal]
 // - TabBarCalcMaxTabWidth() [Internal]
 // - TabBarFindTabById() [Internal]
@@ -6801,7 +6800,6 @@
 namespace ImGui
 {
     static void             TabBarLayout(ImGuiTabBar* tab_bar);
-    static void             TabBarLayoutComputeTabsWidth(ImGuiTabBar* tab_bar, bool scrolling_buttons, float scrolling_buttons_width);
     static ImU32            TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label);
     static float            TabBarCalcMaxTabWidth();
     static float            TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
@@ -6824,6 +6822,7 @@
     TabsActiveCount = 0;
     WantLayout = VisibleTabWasSubmitted = false;
     LastTabItemIdx = -1;
+    TabsAddedNew = false;
 }
 
 static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs)
@@ -6899,9 +6898,11 @@
         return true;
     }
 
-    // When toggling ImGuiTabBarFlags_Reorderable flag, ensure tabs are ordered based on their submission order.
-    if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1)
-        ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
+    // When toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable, ensure tabs are ordered based on their submission order.
+    if (((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) || tab_bar->TabsAddedNew)
+        if (tab_bar->Tabs.Size > 1)
+            ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
+    tab_bar->TabsAddedNew = false;
 
     // Flags
     if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
@@ -6992,6 +6993,8 @@
         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;
 
+        // We will need sorting if either current tab is leading (section_n == 0), but not the previous one,
+        // or if the current is not trailing (section_n == 2), but the previous one is.
         if (tab_dst_n > 0 && curr_tab_section_n == 0 && prev_tab_section_n != 0)
             need_sort_trailing_or_leading = true;
         if (tab_dst_n > 0 && prev_tab_section_n == 2 && curr_tab_section_n != 2)
@@ -7011,6 +7014,10 @@
     tab_bar->Sections[1].TabStartIndex = tab_bar->Sections[0].TabStartIndex + tab_bar->Sections[0].TabCount;
     tab_bar->Sections[2].TabStartIndex = tab_bar->Sections[1].TabStartIndex + tab_bar->Sections[1].TabCount;
 
+    tab_bar->Sections[0].InnerSpacing = tab_bar->Sections[0].TabCount > 0 && (tab_bar->Sections[1].TabCount + tab_bar->Sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
+    tab_bar->Sections[1].InnerSpacing = tab_bar->Sections[1].TabCount > 0 && tab_bar->Sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
+    tab_bar->Sections[2].InnerSpacing = 0.0f;
+
     // Setup next selected tab
     ImGuiID scroll_track_selected_tab_id = 0;
     if (tab_bar->NextSelectedTabId)
@@ -7040,6 +7047,7 @@
 
     // Compute ideal widths
     tab_bar->Sections[0].Width = tab_bar->Sections[1].Width = tab_bar->Sections[2].Width = 0.0f;
+    tab_bar->Sections[0].WidthIdeal = tab_bar->Sections[1].WidthIdeal = tab_bar->Sections[2].WidthIdeal = 0.0f;
     const float tab_max_width = TabBarCalcMaxTabWidth();
 
     ImGuiTabItem* most_recently_selected_tab = NULL;
@@ -7065,8 +7073,8 @@
 
         int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
         ImGuiTabBarSection* section = &tab_bar->Sections[section_n];
-        float width = ImMin(tab->ContentWidth, tab_max_width) + (tab_n > section->TabStartIndex) ? g.Style.ItemInnerSpacing.x : 0.0f;
-        section->Width = section->WidthIdeal = section->Width + width;
+        float width = ImMin(tab->ContentWidth, tab_max_width);
+        section->Width = section->WidthIdeal = section->Width + width + (tab_n > section->TabStartIndex ? g.Style.ItemInnerSpacing.x : 0.0f);
 
         // Store data so we can build an array sorted by width if we need to shrink tabs down
         g.ShrinkWidthBuffer[tab_n].Index = tab_n;
@@ -7076,14 +7084,46 @@
         tab->Width = width;
     }
 
+    tab_bar->WidthAllTabsIdeal = 0.0f;
+    for (int section_n = 0; section_n < 3; section_n++)
+        tab_bar->WidthAllTabsIdeal += tab_bar->Sections[section_n].WidthIdeal + tab_bar->Sections[section_n].InnerSpacing;
+
     // We want to know here if we'll need the scrolling buttons, to adjust available width with resizable leading/trailing
     bool scrolling_buttons = (tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll);
     float scrolling_buttons_width = GetTabBarScrollingButtonSize().x * 2.0f;
-    TabBarLayoutComputeTabsWidth(tab_bar, scrolling_buttons, scrolling_buttons_width);
+
+    // Compute width
+    bool central_section_is_visible = tab_bar->Sections[0].WidthWithSpacing() + tab_bar->Sections[2].WidthWithSpacing() < tab_bar->BarRect.GetWidth() - (scrolling_buttons ? scrolling_buttons_width : 0.0f);
+    float width_excess = central_section_is_visible
+        ? ImMax(tab_bar->Sections[1].WidthWithSpacing() - (tab_bar->BarRect.GetWidth() - tab_bar->Sections[0].WidthWithSpacing() - tab_bar->Sections[2].WidthWithSpacing()), 0.0f)
+        : (tab_bar->Sections[0].WidthWithSpacing() + tab_bar->Sections[2].WidthWithSpacing()) - (tab_bar->BarRect.GetWidth() - (scrolling_buttons ? scrolling_buttons_width : 0.0f));
+
+    if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown || !central_section_is_visible))
+    {
+        // All tabs are in the ShrinkWidthBuffer, but we will only resize leading/trailing or central tabs, so rearrange internal data
+        if (central_section_is_visible)
+            memmove(g.ShrinkWidthBuffer.Data, g.ShrinkWidthBuffer.Data + tab_bar->Sections[0].TabCount, sizeof(ImGuiShrinkWidthItem) * tab_bar->Sections[1].TabCount); // Move central section tabs
+        else
+            memmove(g.ShrinkWidthBuffer.Data + tab_bar->Sections[0].TabCount, g.ShrinkWidthBuffer.Data + tab_bar->Sections[0].TabCount + tab_bar->Sections[1].TabCount, sizeof(ImGuiShrinkWidthItem) * tab_bar->Sections[2].TabCount); // Replace central section tabs with trailing
+        int tab_n_shrinkable = (central_section_is_visible ? tab_bar->Sections[1].TabCount : tab_bar->Sections[0].TabCount + tab_bar->Sections[2].TabCount);
+
+        ShrinkWidths(g.ShrinkWidthBuffer.Data, tab_n_shrinkable, width_excess);
+
+        // Update each section width with shrink values
+        for (int tab_n = 0; tab_n < tab_n_shrinkable; tab_n++)
+        {
+            float shrinked_width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width);
+            ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];
+            int section_n = tab->Flags & ImGuiTabItemFlags_Leading ? 0 : tab->Flags & ImGuiTabItemFlags_Trailing ? 2 : 1;
+
+            tab_bar->Sections[section_n].Width -= (tab->Width - shrinked_width);
+            tab->Width = shrinked_width;
+        }
+    }
 
     // Layout all active tabs
     float next_tab_offset = 0.0f;
-    tab_bar->WidthAllTabs = tab_bar->WidthAllTabsIdeal = 0.0f;
+    tab_bar->WidthAllTabs = 0.0f;
     for (int section_n = 0; section_n < 3; section_n++)
     {
         // TabBarScrollingButtons will alter BarRect.Max.x, so we need to anticipate BarRect width reduction
@@ -7097,8 +7137,7 @@
             tab->Offset = next_tab_offset;
             next_tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);
         }
-        tab_bar->WidthAllTabs += ImMax(section->Width + section->InnerSpacing, 0.0f);
-        tab_bar->WidthAllTabsIdeal += ImMax(section->WidthIdeal + section->InnerSpacing, 0.0f);
+        tab_bar->WidthAllTabs += ImMax(section->WidthWithSpacing(), 0.0f);
         next_tab_offset += section->InnerSpacing;
     }
 
@@ -7150,70 +7189,6 @@
     ImGuiWindow* window = g.CurrentWindow;
     window->DC.CursorPos = tab_bar->BarRect.Min;
     ItemSize(ImVec2(tab_bar->WidthAllTabsIdeal, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y);
-
-#ifdef IMGUI_ENABLE_TEST_ENGINE
-    if (g.IO.KeyAlt)
-    {
-        window->DrawList->AddRect(tab_bar->BarRect.Min, ImVec2(tab_bar->BarRect.Min.x + tab_bar->Sections[0].Width, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255));
-        window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - tab_bar->Sections[2].Width, tab_bar->BarRect.Min.y), tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
-    }
-#endif
-}
-
-static void ImGui::TabBarLayoutComputeTabsWidth(ImGuiTabBar* tab_bar, bool scrolling_buttons, float scrolling_buttons_width)
-{
-    ImGuiContext& g = *GImGui;
-
-    // Compute Leading/Trailing relative additional horizontal inner spacing
-    float leading_trailing_common_inner_space = (tab_bar->Sections[0].TabCount > 0 && tab_bar->Sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f);
-    bool resizing_leading_trailing_only = (tab_bar->Sections[0].Width + tab_bar->Sections[2].Width + leading_trailing_common_inner_space) > (tab_bar->BarRect.GetWidth() - (scrolling_buttons ? scrolling_buttons_width : 0.0f));
-
-    tab_bar->Sections[0].InnerSpacing = tab_bar->Sections[0].TabCount > 0 && (tab_bar->Sections[1].TabCount + tab_bar->Sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
-    tab_bar->Sections[1].InnerSpacing = tab_bar->Sections[1].TabCount > 0 && tab_bar->Sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
-    tab_bar->Sections[2].InnerSpacing = 0.0f;
-
-    // Compute width
-    float width_excess = resizing_leading_trailing_only
-        ? (tab_bar->Sections[0].Width + tab_bar->Sections[2].Width + leading_trailing_common_inner_space) - (tab_bar->BarRect.GetWidth() - (scrolling_buttons ? scrolling_buttons_width : 0.0f))
-        : ImMax(tab_bar->Sections[1].Width + tab_bar->Sections[1].InnerSpacing - (tab_bar->BarRect.GetWidth() - tab_bar->Sections[0].Width - tab_bar->Sections[0].InnerSpacing - tab_bar->Sections[2].Width - tab_bar->Sections[2].InnerSpacing), 0.0f);
-
-    if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || resizing_leading_trailing_only)
-    {
-        // All tabs are in the ShrinkWidthBuffer, but we will only resize leading/trailing or central tabs, so rearrange internal data
-        if (resizing_leading_trailing_only)
-            memmove(g.ShrinkWidthBuffer.Data + tab_bar->Sections[0].TabCount, g.ShrinkWidthBuffer.Data + tab_bar->Sections[0].TabCount + tab_bar->Sections[1].TabCount, sizeof(ImGuiShrinkWidthItem) * tab_bar->Sections[2].TabCount);
-        else
-            memmove(g.ShrinkWidthBuffer.Data, g.ShrinkWidthBuffer.Data + tab_bar->Sections[0].TabCount, sizeof(ImGuiShrinkWidthItem) * tab_bar->Sections[1].TabCount);
-        int tab_n_shrinkable = (resizing_leading_trailing_only ? tab_bar->Sections[0].TabCount + tab_bar->Sections[2].TabCount : tab_bar->Sections[1].TabCount);
-
-        ShrinkWidths(g.ShrinkWidthBuffer.Data, tab_n_shrinkable, width_excess);
-
-        // Total Leading and Trailing shrink values can be different, we need to keep track of how much each section was shrinked
-        float leading_excess = 0.0f;
-        float trailing_excess = 0.0f;
-        for (int tab_n = 0; tab_n < tab_n_shrinkable; tab_n++)
-        {
-            float shrinked_width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width);
-            ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];
-
-            if (tab->Flags & ImGuiTabItemFlags_Leading)
-                leading_excess += (tab->Width - shrinked_width);
-            else if (tab->Flags & ImGuiTabItemFlags_Trailing)
-                trailing_excess += (tab->Width - shrinked_width);
-
-            tab->Width = shrinked_width;
-        }
-
-        if (resizing_leading_trailing_only)
-        {
-            tab_bar->Sections[0].Width -= leading_excess;
-            tab_bar->Sections[2].Width -= trailing_excess;
-        }
-        else
-        {
-            tab_bar->Sections[1].Width -= width_excess;
-        }
-    }
 }
 
 // Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack.
@@ -7407,7 +7382,6 @@
             }
         }
     window->DC.CursorPos = backup_cursor_pos;
-
     tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
 
     return tab_to_scroll_to;
@@ -7569,7 +7543,7 @@
         tab = &tab_bar->Tabs.back();
         tab->ID = id;
         tab->Width = size.x;
-        tab_is_new = true;
+        tab_bar->TabsAddedNew = tab_is_new = true;
     }
     tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab);
     tab->ContentWidth = size.x;