mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-31 13:11:05 +01:00 
			
		
		
		
	TabBar: Amend previous commit. Fix tab reordering when tab bar has scrolling.
Some tidying up with helpers + honor 16-bit offsets as with other tab bar features (unlikely single reorder can reach that but consistent)
This commit is contained in:
		| @@ -110,6 +110,8 @@ Other Changes: | ||||
| - Scrolling: Fix scroll tracking with e.g. SetScrollHereX/Y() when WindowPadding < ItemSpacing. | ||||
| - Scrolling: Fix scroll snapping on edge of scroll region when both scrollbars are enabled. | ||||
| - Tables: Expose TableSetColumnEnabled() in public api. (#3935) | ||||
| - TabBar: Fixed mouse reordering with very fast movements (e.g. crossing multiple tabs in a single | ||||
|   frame and then immediately standling still (would only affect automation/bots). [@rokups] | ||||
| - Drags, Sliders, Inputs: Specifying a NULL format to Float functions default them to "%.3f" to be | ||||
|   consistent with the compile-time default. (#3922) | ||||
| - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] | ||||
|   | ||||
| @@ -2098,6 +2098,7 @@ enum ImGuiTabBarFlagsPrivate_ | ||||
| // Extend ImGuiTabItemFlags_ | ||||
| enum ImGuiTabItemFlagsPrivate_ | ||||
| { | ||||
|     ImGuiTabItemFlags_SectionMask_              = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, | ||||
|     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,  // Used by TabItemButton, change the tab item behavior to mimic a button | ||||
|     ImGuiTabItemFlags_Unsorted                  = 1 << 22,  // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. | ||||
| @@ -2146,7 +2147,7 @@ struct IMGUI_API ImGuiTabBar | ||||
|     float               ScrollingRectMinX; | ||||
|     float               ScrollingRectMaxX; | ||||
|     ImGuiID             ReorderRequestTabId; | ||||
|     ImS8                ReorderRequestDir; | ||||
|     ImS16               ReorderRequestOffset; | ||||
|     ImS8                BeginCount; | ||||
|     bool                WantLayout; | ||||
|     bool                VisibleTabWasSubmitted; | ||||
| @@ -2696,7 +2697,7 @@ namespace ImGui | ||||
|     IMGUI_API void          TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window); | ||||
|     IMGUI_API void          TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); | ||||
|     IMGUI_API void          TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); | ||||
|     IMGUI_API void          TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); | ||||
|     IMGUI_API void          TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset); | ||||
|     IMGUI_API void          TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); | ||||
|     IMGUI_API bool          TabBarProcessReorder(ImGuiTabBar* tab_bar); | ||||
|     IMGUI_API bool          TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); | ||||
|   | ||||
| @@ -6975,12 +6975,17 @@ ImGuiTabBar::ImGuiTabBar() | ||||
|     LastTabItemIdx = -1; | ||||
| } | ||||
|  | ||||
| static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) | ||||
| { | ||||
|     return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; | ||||
| } | ||||
|  | ||||
| static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) | ||||
| { | ||||
|     const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; | ||||
|     const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; | ||||
|     const int a_section = (a->Flags & ImGuiTabItemFlags_Leading) ? 0 : (a->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; | ||||
|     const int b_section = (b->Flags & ImGuiTabItemFlags_Leading) ? 0 : (b->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; | ||||
|     const int a_section = TabItemGetSectionIdx(a); | ||||
|     const int b_section = TabItemGetSectionIdx(b); | ||||
|     if (a_section != b_section) | ||||
|         return a_section - b_section; | ||||
|     return (int)(a->IndexDuringLayout - b->IndexDuringLayout); | ||||
| @@ -7156,11 +7161,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) | ||||
|         tab->IndexDuringLayout = (ImS16)tab_dst_n; | ||||
|  | ||||
|         // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) | ||||
|         int curr_tab_section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; | ||||
|         int curr_tab_section_n = TabItemGetSectionIdx(tab); | ||||
|         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; | ||||
|             int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); | ||||
|             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) | ||||
| @@ -7232,7 +7237,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) | ||||
|         const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0; | ||||
|         tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x; | ||||
|  | ||||
|         int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; | ||||
|         int section_n = TabItemGetSectionIdx(tab); | ||||
|         ImGuiTabBarSection* section = §ions[section_n]; | ||||
|         section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); | ||||
|         curr_section_n = section_n; | ||||
| @@ -7287,7 +7292,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) | ||||
|             if (shrinked_width < 0.0f) | ||||
|                 continue; | ||||
|  | ||||
|             int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; | ||||
|             int section_n = TabItemGetSectionIdx(tab); | ||||
|             sections[section_n].Width -= (tab->Width - shrinked_width); | ||||
|             tab->Width = shrinked_width; | ||||
|         } | ||||
| @@ -7470,7 +7475,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui | ||||
|     ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); | ||||
|     if (tab == NULL) | ||||
|         return; | ||||
|     if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) | ||||
|     if (tab->Flags & ImGuiTabItemFlags_SectionMask_) | ||||
|         return; | ||||
|  | ||||
|     ImGuiContext& g = *GImGui; | ||||
| @@ -7499,56 +7504,48 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) | ||||
| void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset) | ||||
| { | ||||
|     IM_ASSERT(dir == -1 || dir == +1); | ||||
|     IM_ASSERT(offset != 0); | ||||
|     IM_ASSERT(tab_bar->ReorderRequestTabId == 0); | ||||
|     tab_bar->ReorderRequestTabId = tab->ID; | ||||
|     tab_bar->ReorderRequestDir = (ImS8)dir; | ||||
|     tab_bar->ReorderRequestOffset = (ImS16)offset; | ||||
| } | ||||
|  | ||||
| void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos) | ||||
| void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* src_tab, ImVec2 mouse_pos) | ||||
| { | ||||
|     ImGuiContext& g = *GImGui; | ||||
|     IM_ASSERT(tab_bar->ReorderRequestTabId == 0); | ||||
|  | ||||
|     if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) | ||||
|         return; | ||||
|  | ||||
|     int source_idx = tab_bar->Tabs.index_from_ptr(tab); | ||||
|     float bar_x = tab_bar->BarRect.Min.x; | ||||
|     int dir = bar_x + tab->Offset > mouse_pos.x ? -1 : +1; | ||||
|     int target_idx = source_idx; | ||||
|     const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; | ||||
|     const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); | ||||
|  | ||||
|     for (int i = source_idx; 0 <= i && i < tab_bar->Tabs.Size; i += dir) | ||||
|     // Count number of contiguous tabs we are crossing over | ||||
|     const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; | ||||
|     const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); | ||||
|     int dst_idx = src_idx; | ||||
|     for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) | ||||
|     { | ||||
|         const ImGuiTabItem* target_tab = &tab_bar->Tabs[i]; | ||||
|  | ||||
|         // Reorder only within tab groups with _Leading, _Trailing flag or without either of them. | ||||
|         if ((target_tab->Flags & ImGuiTabItemFlags_Leading) != (tab->Flags & ImGuiTabItemFlags_Leading)) | ||||
|         // Reordered tabs must share the same section | ||||
|         const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; | ||||
|         if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) | ||||
|             break; | ||||
|         if ((target_tab->Flags & ImGuiTabItemFlags_Trailing) != (tab->Flags & ImGuiTabItemFlags_Trailing)) | ||||
|         if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) | ||||
|             break; | ||||
|         dst_idx = i; | ||||
|  | ||||
|         // Do not reorder past tabs with _NoReorder flag. | ||||
|         if (target_tab->Flags & ImGuiTabItemFlags_NoReorder) | ||||
|             break; | ||||
|  | ||||
|         target_idx = i;     // target_tab can be swapped with dragged tab. | ||||
|  | ||||
|         // Current tab is destination tab under mouse position. Also include space after tab, so when mouse cursor is | ||||
|         // between tabs we would not continue checking further tabs that are not hovered. | ||||
|         if (dir > 0 && mouse_pos.x < bar_x + target_tab->Offset + target_tab->Width + g.Style.ItemInnerSpacing.x)       // End of tab is past mouse_pos. | ||||
|             break; | ||||
|         if (dir < 0 && mouse_pos.x > bar_x + target_tab->Offset - g.Style.ItemInnerSpacing.x)                           // Mouse pos is past start of tab. | ||||
|         // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. | ||||
|         const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; | ||||
|         const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; | ||||
|         //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); | ||||
|         if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     if (target_idx != source_idx) | ||||
|     { | ||||
|         tab_bar->ReorderRequestTabId = tab->ID; | ||||
|         tab_bar->ReorderRequestDir = (ImS8)(target_idx - source_idx); | ||||
|     } | ||||
|     if (dst_idx != src_idx) | ||||
|         TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); | ||||
| } | ||||
|  | ||||
| bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) | ||||
| @@ -7558,30 +7555,23 @@ bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) | ||||
|         return false; | ||||
|  | ||||
|     //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools | ||||
|     int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; | ||||
|     int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestOffset; | ||||
|     if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) | ||||
|         return false; | ||||
|  | ||||
|     // Reordered TabItem must share the same position flags than target | ||||
|     // Reordered tabs must share the same section | ||||
|     // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) | ||||
|     ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; | ||||
|     if (tab2->Flags & ImGuiTabItemFlags_NoReorder) | ||||
|         return false; | ||||
|     if ((tab1->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (tab2->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))) | ||||
|     if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) | ||||
|         return false; | ||||
|  | ||||
|     ImGuiTabItem item_tmp = *tab1; | ||||
|     ImGuiTabItem* src, *dst; | ||||
|     if (tab_bar->ReorderRequestDir > 0) | ||||
|     { | ||||
|         dst = tab1; | ||||
|         src = tab1 + 1; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         dst = tab2 + 1; | ||||
|         src = tab2; | ||||
|     } | ||||
|     memmove(dst, src, abs(tab_bar->ReorderRequestDir) * sizeof(ImGuiTabItem)); | ||||
|     ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; | ||||
|     ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; | ||||
|     const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; | ||||
|     memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); | ||||
|     *tab2 = item_tmp; | ||||
|  | ||||
|     if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) | ||||
| @@ -7874,7 +7864,7 @@ bool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, | ||||
|     const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; | ||||
|  | ||||
|     // Layout | ||||
|     const bool is_central_section = (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) == 0; | ||||
|     const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; | ||||
|     size.x = tab->Width; | ||||
|     if (is_central_section) | ||||
|         window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f); | ||||
| @@ -7964,7 +7954,7 @@ bool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, | ||||
|                 if (distance_from_edge_y >= threshold_y) | ||||
|                     undocking_tab = true; | ||||
|                 else if (drag_distance_from_edge_x > threshold_x) | ||||
|                     if ((tab_bar->ReorderRequestDir < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestDir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) | ||||
|                     if ((tab_bar->ReorderRequestOffset < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestOffset > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) | ||||
|                         undocking_tab = true; | ||||
|             } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user