mirror of
				https://github.com/ocornut/imgui.git
				synced 2025-10-26 12:27:30 +00:00 
			
		
		
		
	Added BETA api for Tab Bar/Tabs widgets. (#261, #351) (merged this feature from the from Docking branch so it can be used earlier as as standalone feature)
- Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. - Added ImGuiTabBarFlags flags for BeginTabBar(). - Added ImGuiTabItemFlags flags for BeginTabItem(). - Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. - Demo: Added Layout->Tabs demo code. - Demo: Added "Documents" example app showcasing possible use for tabs.
This commit is contained in:
		| @@ -39,6 +39,15 @@ Breaking Changes: | |||||||
|   The addition of new configuration options in the Docking branch is pushing for a little reorganization of those names. |   The addition of new configuration options in the Docking branch is pushing for a little reorganization of those names. | ||||||
|  |  | ||||||
| Other Changes: | Other Changes: | ||||||
|  | - Added BETA api for Tab Bar/Tabs widgets: (#261, #351) | ||||||
|  |   - Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. | ||||||
|  |   - Added ImGuiTabBarFlags flags for BeginTabBar(). | ||||||
|  |   - Added ImGuiTabItemFlags flags for BeginTabItem(). | ||||||
|  |   - Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. | ||||||
|  |   - Demo: Added Layout->Tabs demo code. | ||||||
|  |   - Demo: Added "Documents" example app showcasing possible use for tabs. | ||||||
|  |   This feature was merged from the Docking branch in order to allow the use of regular tabs in your code. | ||||||
|  |   (It does not provide the docking/splitting/merging of windows available in the Docking branch) | ||||||
| - Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering  | - Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering  | ||||||
|   the ID, as a convenience to avoid using the ### operator. |   the ID, as a convenience to avoid using the ### operator. | ||||||
| - Resizing windows from edge is now enabled by default (io.ConfigWindowsResizeFromEdges=true). Note that  | - Resizing windows from edge is now enabled by default (io.ConfigWindowsResizeFromEdges=true). Note that  | ||||||
|   | |||||||
| @@ -127,7 +127,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i | |||||||
|  - dock: docking extension |  - dock: docking extension | ||||||
|  - dock: dock out from a collapsing header? would work nicely but need emitting window to keep submitting the code. |  - dock: dock out from a collapsing header? would work nicely but need emitting window to keep submitting the code. | ||||||
|  |  | ||||||
|  - tabs: re-ordering, close buttons, context menu, persistent order (#261, #351) |  - tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing. | ||||||
|  |  - tabs: persistent order/focus in BeginTabBar() api (#261, #351) | ||||||
|  |  | ||||||
|  - ext: stl-ish friendly extension (imgui_stl.h) that has wrapper for std::string, std::vector etc. |  - ext: stl-ish friendly extension (imgui_stl.h) that has wrapper for std::string, std::vector etc. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										68
									
								
								imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								imgui.cpp
									
									
									
									
									
								
							| @@ -1015,6 +1015,8 @@ ImGuiStyle::ImGuiStyle() | |||||||
|     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar |     ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar | ||||||
|     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar |     GrabMinSize             = 10.0f;            // Minimum width/height of a grab box for slider/scrollbar | ||||||
|     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. |     GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. | ||||||
|  |     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. | ||||||
|     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. |     ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. | ||||||
|     DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. |     DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. | ||||||
|     DisplaySafeAreaPadding  = ImVec2(3,3);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. |     DisplaySafeAreaPadding  = ImVec2(3,3);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. | ||||||
| @@ -1038,6 +1040,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) | |||||||
|     PopupRounding = ImFloor(PopupRounding * scale_factor); |     PopupRounding = ImFloor(PopupRounding * scale_factor); | ||||||
|     FramePadding = ImFloor(FramePadding * scale_factor); |     FramePadding = ImFloor(FramePadding * scale_factor); | ||||||
|     FrameRounding = ImFloor(FrameRounding * scale_factor); |     FrameRounding = ImFloor(FrameRounding * scale_factor); | ||||||
|  |     TabRounding = ImFloor(TabRounding * scale_factor); | ||||||
|     ItemSpacing = ImFloor(ItemSpacing * scale_factor); |     ItemSpacing = ImFloor(ItemSpacing * scale_factor); | ||||||
|     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); |     ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); | ||||||
|     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); |     TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); | ||||||
| @@ -2160,17 +2163,8 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end | |||||||
|  |  | ||||||
| // Default clip_rect uses (pos_min,pos_max) | // Default clip_rect uses (pos_min,pos_max) | ||||||
| // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) | // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) | ||||||
| void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) | void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) | ||||||
| { | { | ||||||
|     // Hide anything after a '##' string |  | ||||||
|     const char* text_display_end = FindRenderedTextEnd(text, text_end); |  | ||||||
|     const int text_len = (int)(text_display_end - text); |  | ||||||
|     if (text_len == 0) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|     ImGuiContext& g = *GImGui; |  | ||||||
|     ImGuiWindow* window = g.CurrentWindow; |  | ||||||
|  |  | ||||||
|     // Perform CPU side clipping for single clipped element to avoid using scissor state |     // Perform CPU side clipping for single clipped element to avoid using scissor state | ||||||
|     ImVec2 pos = pos_min; |     ImVec2 pos = pos_min; | ||||||
|     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); |     const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); | ||||||
| @@ -2189,14 +2183,27 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons | |||||||
|     if (need_clipping) |     if (need_clipping) | ||||||
|     { |     { | ||||||
|         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); |         ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); | ||||||
|         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); |         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|         window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); |         draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) | ||||||
|  | { | ||||||
|  |     // Hide anything after a '##' string | ||||||
|  |     const char* text_display_end = FindRenderedTextEnd(text, text_end); | ||||||
|  |     const int text_len = (int)(text_display_end - text); | ||||||
|  |     if (text_len == 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImGuiWindow* window = g.CurrentWindow; | ||||||
|  |     RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); | ||||||
|     if (g.LogEnabled) |     if (g.LogEnabled) | ||||||
|         LogRenderedText(&pos, text, text_display_end); |         LogRenderedText(&pos_min, text, text_display_end); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Render a rectangle shaped with optional rounding and borders | // Render a rectangle shaped with optional rounding and borders | ||||||
| @@ -5511,6 +5518,7 @@ static const ImGuiStyleVarInfo GStyleVarInfo[] = | |||||||
|     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },  // ImGuiStyleVar_ScrollbarRounding |     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) },  // ImGuiStyleVar_ScrollbarRounding | ||||||
|     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },        // ImGuiStyleVar_GrabMinSize |     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) },        // ImGuiStyleVar_GrabMinSize | ||||||
|     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },       // ImGuiStyleVar_GrabRounding |     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) },       // ImGuiStyleVar_GrabRounding | ||||||
|  |     { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },        // ImGuiStyleVar_TabRounding | ||||||
|     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },    // ImGuiStyleVar_ButtonTextAlign |     { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },    // ImGuiStyleVar_ButtonTextAlign | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -5603,6 +5611,11 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) | |||||||
|     case ImGuiCol_ResizeGrip: return "ResizeGrip"; |     case ImGuiCol_ResizeGrip: return "ResizeGrip"; | ||||||
|     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; |     case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; | ||||||
|     case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; |     case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; | ||||||
|  |     case ImGuiCol_Tab: return "Tab"; | ||||||
|  |     case ImGuiCol_TabHovered: return "TabHovered"; | ||||||
|  |     case ImGuiCol_TabActive: return "TabActive"; | ||||||
|  |     case ImGuiCol_TabUnfocused: return "TabUnfocused"; | ||||||
|  |     case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; | ||||||
|     case ImGuiCol_PlotLines: return "PlotLines"; |     case ImGuiCol_PlotLines: return "PlotLines"; | ||||||
|     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; |     case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; | ||||||
|     case ImGuiCol_PlotHistogram: return "PlotHistogram"; |     case ImGuiCol_PlotHistogram: return "PlotHistogram"; | ||||||
| @@ -9056,6 +9069,29 @@ void ImGui::ShowMetricsWindow(bool* p_open) | |||||||
|             ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); |             ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); | ||||||
|             ImGui::TreePop(); |             ImGui::TreePop(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         static void NodeTabBar(ImGuiTabBar* tab_bar) | ||||||
|  |         { | ||||||
|  |             // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernable strings. | ||||||
|  |             char buf[256]; | ||||||
|  |             char* p = buf; | ||||||
|  |             const char* buf_end = buf + IM_ARRAYSIZE(buf); | ||||||
|  |             p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", | ||||||
|  |                 tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); | ||||||
|  |             if (ImGui::TreeNode(tab_bar, "%s", buf)) | ||||||
|  |             { | ||||||
|  |                 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) | ||||||
|  |                 { | ||||||
|  |                     const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; | ||||||
|  |                     ImGui::PushID(tab); | ||||||
|  |                     if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); | ||||||
|  |                     if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); | ||||||
|  |                     ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID); | ||||||
|  |                     ImGui::PopID(); | ||||||
|  |                 } | ||||||
|  |                 ImGui::TreePop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Access private state, we are going to display the draw lists from last frame |     // Access private state, we are going to display the draw lists from last frame | ||||||
| @@ -9076,6 +9112,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) | |||||||
|         } |         } | ||||||
|         ImGui::TreePop(); |         ImGui::TreePop(); | ||||||
|     } |     } | ||||||
|  |     if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) | ||||||
|  |     { | ||||||
|  |         for (int n = 0; n < g.TabBars.Data.Size; n++) | ||||||
|  |             Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); | ||||||
|  |         ImGui::TreePop(); | ||||||
|  |     } | ||||||
|     if (ImGui::TreeNode("Internal state")) |     if (ImGui::TreeNode("Internal state")) | ||||||
|     { |     { | ||||||
|         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); |         const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								imgui.h
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								imgui.h
									
									
									
									
									
								
							| @@ -135,6 +135,8 @@ typedef int ImGuiFocusedFlags;      // -> enum ImGuiFocusedFlags_    // Flags: f | |||||||
| typedef int ImGuiHoveredFlags;      // -> enum ImGuiHoveredFlags_    // Flags: for IsItemHovered(), IsWindowHovered() etc. | typedef int ImGuiHoveredFlags;      // -> enum ImGuiHoveredFlags_    // Flags: for IsItemHovered(), IsWindowHovered() etc. | ||||||
| typedef int ImGuiInputTextFlags;    // -> enum ImGuiInputTextFlags_  // Flags: for InputText*() | typedef int ImGuiInputTextFlags;    // -> enum ImGuiInputTextFlags_  // Flags: for InputText*() | ||||||
| typedef int ImGuiSelectableFlags;   // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() | typedef int ImGuiSelectableFlags;   // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() | ||||||
|  | typedef int ImGuiTabBarFlags;       // -> enum ImGuiTabBarFlags_     // Flags: for BeginTabBar() | ||||||
|  | typedef int ImGuiTabItemFlags;      // -> enum ImGuiTabItemFlags_    // Flags: for BeginTabItem() | ||||||
| typedef int ImGuiTreeNodeFlags;     // -> enum ImGuiTreeNodeFlags_   // Flags: for TreeNode*(),CollapsingHeader() | typedef int ImGuiTreeNodeFlags;     // -> enum ImGuiTreeNodeFlags_   // Flags: for TreeNode*(),CollapsingHeader() | ||||||
| typedef int ImGuiWindowFlags;       // -> enum ImGuiWindowFlags_     // Flags: for Begin*() | typedef int ImGuiWindowFlags;       // -> enum ImGuiWindowFlags_     // Flags: for Begin*() | ||||||
| typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); | typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); | ||||||
| @@ -528,6 +530,14 @@ namespace ImGui | |||||||
|     IMGUI_API void          SetColumnOffset(int column_index, float offset_x);                  // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column |     IMGUI_API void          SetColumnOffset(int column_index, float offset_x);                  // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column | ||||||
|     IMGUI_API int           GetColumnsCount(); |     IMGUI_API int           GetColumnsCount(); | ||||||
|  |  | ||||||
|  |     // Tab Bars, Tabs | ||||||
|  |     // [BETA API] API may evolve! | ||||||
|  |     IMGUI_API bool          BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0);        // create and append into a TabBar | ||||||
|  |     IMGUI_API void          EndTabBar(); | ||||||
|  |     IMGUI_API bool          BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0);// create a Tab. Returns true if the Tab is selected. | ||||||
|  |     IMGUI_API void          EndTabItem();                                                       // only call EndTabItem() if BeginTabItem() returns true! | ||||||
|  |     IMGUI_API void          SetTabItemClosed(const char* tab_or_docked_window_label);           // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. | ||||||
|  |  | ||||||
|     // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. |     // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. | ||||||
|     IMGUI_API void          LogToTTY(int max_depth = -1);                                       // start logging to tty |     IMGUI_API void          LogToTTY(int max_depth = -1);                                       // start logging to tty | ||||||
|     IMGUI_API void          LogToFile(int max_depth = -1, const char* filename = NULL);         // start logging to file |     IMGUI_API void          LogToFile(int max_depth = -1, const char* filename = NULL);         // start logging to file | ||||||
| @@ -760,6 +770,31 @@ enum ImGuiComboFlags_ | |||||||
|     ImGuiComboFlags_HeightMask_             = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest |     ImGuiComboFlags_HeightMask_             = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // Flags for ImGui::BeginTabBar() | ||||||
|  | enum ImGuiTabBarFlags_ | ||||||
|  | { | ||||||
|  |     ImGuiTabBarFlags_None                           = 0, | ||||||
|  |     ImGuiTabBarFlags_Reorderable                    = 1 << 0,   // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list | ||||||
|  |     ImGuiTabBarFlags_AutoSelectNewTabs              = 1 << 1,   // Automatically select new tabs when they appear | ||||||
|  |     ImGuiTabBarFlags_NoCloseWithMiddleMouseButton   = 1 << 2,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. | ||||||
|  |     ImGuiTabBarFlags_NoTabListPopupButton           = 1 << 3, | ||||||
|  |     ImGuiTabBarFlags_NoTabListScrollingButtons      = 1 << 4, | ||||||
|  |     ImGuiTabBarFlags_FittingPolicyResizeDown        = 1 << 5,   // Resize tabs when they don't fit | ||||||
|  |     ImGuiTabBarFlags_FittingPolicyScroll            = 1 << 6,   // Add scroll buttons when tabs don't fit | ||||||
|  |     ImGuiTabBarFlags_FittingPolicyMask_             = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, | ||||||
|  |     ImGuiTabBarFlags_FittingPolicyDefault_          = ImGuiTabBarFlags_FittingPolicyResizeDown | ||||||
|  | };   | ||||||
|  |  | ||||||
|  | // Flags for ImGui::BeginTabItem() | ||||||
|  | enum ImGuiTabItemFlags_ | ||||||
|  | { | ||||||
|  |     ImGuiTabItemFlags_None                          = 0, | ||||||
|  |     ImGuiTabItemFlags_UnsavedDocument               = 1 << 0,   // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. | ||||||
|  |     ImGuiTabItemFlags_SetSelected                   = 1 << 1,   // Trigger flag to programatically make the tab selected when calling BeginTabItem() | ||||||
|  |     ImGuiTabItemFlags_NoCloseWithMiddleMouseButton  = 1 << 2,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. | ||||||
|  |     ImGuiTabItemFlags_NoPushId                      = 1 << 3    // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() | ||||||
|  | }; | ||||||
|  |  | ||||||
| // Flags for ImGui::IsWindowFocused() | // Flags for ImGui::IsWindowFocused() | ||||||
| enum ImGuiFocusedFlags_ | enum ImGuiFocusedFlags_ | ||||||
| { | { | ||||||
| @@ -956,6 +991,11 @@ enum ImGuiCol_ | |||||||
|     ImGuiCol_ResizeGrip, |     ImGuiCol_ResizeGrip, | ||||||
|     ImGuiCol_ResizeGripHovered, |     ImGuiCol_ResizeGripHovered, | ||||||
|     ImGuiCol_ResizeGripActive, |     ImGuiCol_ResizeGripActive, | ||||||
|  |     ImGuiCol_Tab, | ||||||
|  |     ImGuiCol_TabHovered, | ||||||
|  |     ImGuiCol_TabActive, | ||||||
|  |     ImGuiCol_TabUnfocused, | ||||||
|  |     ImGuiCol_TabUnfocusedActive, | ||||||
|     ImGuiCol_PlotLines, |     ImGuiCol_PlotLines, | ||||||
|     ImGuiCol_PlotLinesHovered, |     ImGuiCol_PlotLinesHovered, | ||||||
|     ImGuiCol_PlotHistogram, |     ImGuiCol_PlotHistogram, | ||||||
| @@ -1003,6 +1043,7 @@ enum ImGuiStyleVar_ | |||||||
|     ImGuiStyleVar_ScrollbarRounding,   // float     ScrollbarRounding |     ImGuiStyleVar_ScrollbarRounding,   // float     ScrollbarRounding | ||||||
|     ImGuiStyleVar_GrabMinSize,         // float     GrabMinSize |     ImGuiStyleVar_GrabMinSize,         // float     GrabMinSize | ||||||
|     ImGuiStyleVar_GrabRounding,        // float     GrabRounding |     ImGuiStyleVar_GrabRounding,        // float     GrabRounding | ||||||
|  |     ImGuiStyleVar_TabRounding,         // float     TabRounding | ||||||
|     ImGuiStyleVar_ButtonTextAlign,     // ImVec2    ButtonTextAlign |     ImGuiStyleVar_ButtonTextAlign,     // ImVec2    ButtonTextAlign | ||||||
|     ImGuiStyleVar_COUNT |     ImGuiStyleVar_COUNT | ||||||
|  |  | ||||||
| @@ -1114,6 +1155,8 @@ struct ImGuiStyle | |||||||
|     float       ScrollbarRounding;          // Radius of grab corners for scrollbar. |     float       ScrollbarRounding;          // Radius of grab corners for scrollbar. | ||||||
|     float       GrabMinSize;                // Minimum width/height of a grab box for slider/scrollbar. |     float       GrabMinSize;                // Minimum width/height of a grab box for slider/scrollbar. | ||||||
|     float       GrabRounding;               // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. |     float       GrabRounding;               // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. | ||||||
|  |     float       TabRounding;                // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. | ||||||
|  |     float       TabBorderSize;              // Thickness of border around tabs.  | ||||||
|     ImVec2      ButtonTextAlign;            // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. |     ImVec2      ButtonTextAlign;            // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. | ||||||
|     ImVec2      DisplayWindowPadding;       // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. |     ImVec2      DisplayWindowPadding;       // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. | ||||||
|     ImVec2      DisplaySafeAreaPadding;     // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! |     ImVec2      DisplaySafeAreaPadding;     // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! | ||||||
|   | |||||||
							
								
								
									
										342
									
								
								imgui_demo.cpp
									
									
									
									
									
								
							
							
						
						
									
										342
									
								
								imgui_demo.cpp
									
									
									
									
									
								
							| @@ -40,6 +40,7 @@ Index of this file: | |||||||
| // [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() | // [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() | ||||||
| // [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() | // [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() | ||||||
| // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() | // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() | ||||||
|  | // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() | ||||||
|  |  | ||||||
| */ | */ | ||||||
|  |  | ||||||
| @@ -102,6 +103,7 @@ Index of this file: | |||||||
| #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) | #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) | ||||||
|  |  | ||||||
| // Forward Declarations | // Forward Declarations | ||||||
|  | static void ShowExampleAppDocuments(bool* p_open); | ||||||
| static void ShowExampleAppMainMenuBar(); | static void ShowExampleAppMainMenuBar(); | ||||||
| static void ShowExampleAppConsole(bool* p_open); | static void ShowExampleAppConsole(bool* p_open); | ||||||
| static void ShowExampleAppLog(bool* p_open); | static void ShowExampleAppLog(bool* p_open); | ||||||
| @@ -168,6 +170,7 @@ static void ShowDemoWindowMisc(); | |||||||
| void ImGui::ShowDemoWindow(bool* p_open) | void ImGui::ShowDemoWindow(bool* p_open) | ||||||
| { | { | ||||||
|     // Examples Apps (accessible from the "Examples" menu) |     // Examples Apps (accessible from the "Examples" menu) | ||||||
|  |     static bool show_app_documents = false; | ||||||
|     static bool show_app_main_menu_bar = false; |     static bool show_app_main_menu_bar = false; | ||||||
|     static bool show_app_console = false; |     static bool show_app_console = false; | ||||||
|     static bool show_app_log = false; |     static bool show_app_log = false; | ||||||
| @@ -180,6 +183,7 @@ void ImGui::ShowDemoWindow(bool* p_open) | |||||||
|     static bool show_app_window_titles = false; |     static bool show_app_window_titles = false; | ||||||
|     static bool show_app_custom_rendering = false; |     static bool show_app_custom_rendering = false; | ||||||
|  |  | ||||||
|  |     if (show_app_documents)           ShowExampleAppDocuments(&show_app_documents);     // Process the Document app next, as it may also use a DockSpace() | ||||||
|     if (show_app_main_menu_bar)       ShowExampleAppMainMenuBar(); |     if (show_app_main_menu_bar)       ShowExampleAppMainMenuBar(); | ||||||
|     if (show_app_console)             ShowExampleAppConsole(&show_app_console); |     if (show_app_console)             ShowExampleAppConsole(&show_app_console); | ||||||
|     if (show_app_log)                 ShowExampleAppLog(&show_app_log); |     if (show_app_log)                 ShowExampleAppLog(&show_app_log); | ||||||
| @@ -263,6 +267,7 @@ void ImGui::ShowDemoWindow(bool* p_open) | |||||||
|             ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); |             ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); | ||||||
|             ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); |             ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); | ||||||
|             ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); |             ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); | ||||||
|  |             ImGui::MenuItem("Documents", NULL, &show_app_documents); | ||||||
|             ImGui::EndMenu(); |             ImGui::EndMenu(); | ||||||
|         } |         } | ||||||
|         if (ImGui::BeginMenu("Help")) |         if (ImGui::BeginMenu("Help")) | ||||||
| @@ -1698,6 +1703,76 @@ static void ShowDemoWindowLayout() | |||||||
|         ImGui::TreePop(); |         ImGui::TreePop(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (ImGui::TreeNode("Tabs")) | ||||||
|  |     { | ||||||
|  |         if (ImGui::TreeNode("Basic")) | ||||||
|  |         { | ||||||
|  |             ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; | ||||||
|  |             if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) | ||||||
|  |             { | ||||||
|  |                 if (ImGui::BeginTabItem("Avocado")) | ||||||
|  |                 { | ||||||
|  |                     ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); | ||||||
|  |                     ImGui::EndTabItem(); | ||||||
|  |                 } | ||||||
|  |                 if (ImGui::BeginTabItem("Broccoli")) | ||||||
|  |                 { | ||||||
|  |                     ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); | ||||||
|  |                     ImGui::EndTabItem(); | ||||||
|  |                 } | ||||||
|  |                 if (ImGui::BeginTabItem("Cucumber")) | ||||||
|  |                 { | ||||||
|  |                     ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); | ||||||
|  |                     ImGui::EndTabItem(); | ||||||
|  |                 } | ||||||
|  |                 ImGui::EndTabBar(); | ||||||
|  |             } | ||||||
|  |             ImGui::Separator(); | ||||||
|  |             ImGui::TreePop(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (ImGui::TreeNode("Advanced & Close Button")) | ||||||
|  |         { | ||||||
|  |             // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). | ||||||
|  |             static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable; | ||||||
|  |             ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_Reorderable); | ||||||
|  |             ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs); | ||||||
|  |             ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); | ||||||
|  |             if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) | ||||||
|  |                 tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; | ||||||
|  |             if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) | ||||||
|  |                 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); | ||||||
|  |             if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) | ||||||
|  |                 tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); | ||||||
|  |  | ||||||
|  |             // Tab Bar | ||||||
|  |             const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; | ||||||
|  |             static bool opened[4] = { true, true, true, true }; // Persistent user state | ||||||
|  |             for (int n = 0; n < IM_ARRAYSIZE(opened); n++) | ||||||
|  |             { | ||||||
|  |                 if (n > 0) { ImGui::SameLine(); } | ||||||
|  |                 ImGui::Checkbox(names[n], &opened[n]); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): the underlying bool will be set to false when the tab is closed. | ||||||
|  |             if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) | ||||||
|  |             { | ||||||
|  |                 for (int n = 0; n < IM_ARRAYSIZE(opened); n++) | ||||||
|  |                     if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n])) | ||||||
|  |                     { | ||||||
|  |                         ImGui::Text("This is the %s tab!", names[n]); | ||||||
|  |                         if (n & 1) | ||||||
|  |                             ImGui::Text("I am an odd tab."); | ||||||
|  |                         ImGui::EndTabItem(); | ||||||
|  |                     } | ||||||
|  |                 ImGui::EndTabBar(); | ||||||
|  |             } | ||||||
|  |             ImGui::Separator(); | ||||||
|  |             ImGui::TreePop(); | ||||||
|  |         } | ||||||
|  |         ImGui::TreePop(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (ImGui::TreeNode("Groups")) |     if (ImGui::TreeNode("Groups")) | ||||||
|     { |     { | ||||||
|         ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); |         ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); | ||||||
| @@ -2679,12 +2754,14 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) | |||||||
|         ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); |         ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); | ||||||
|         ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); |         ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); | ||||||
|         ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); |         ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); | ||||||
|  |         ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); | ||||||
|         ImGui::Text("Rounding"); |         ImGui::Text("Rounding"); | ||||||
|         ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f"); |         ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f"); | ||||||
|         ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f"); |         ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f"); | ||||||
|         ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); |         ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); | ||||||
|         ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); |         ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); | ||||||
|         ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); |         ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); | ||||||
|  |         ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); | ||||||
|         ImGui::Text("Alignment"); |         ImGui::Text("Alignment"); | ||||||
|         ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); |         ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); | ||||||
|         ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); |         ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); | ||||||
| @@ -3758,6 +3835,271 @@ static void ShowExampleAppCustomRendering(bool* p_open) | |||||||
|         if (adding_preview) |         if (adding_preview) | ||||||
|             points.pop_back(); |             points.pop_back(); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //----------------------------------------------------------------------------- | ||||||
|  | // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() | ||||||
|  | //----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | // Simplified structure to mimic a Document model | ||||||
|  | struct MyDocument | ||||||
|  | { | ||||||
|  |     const char* Name;           // Document title | ||||||
|  |     bool        Open;           // Set when the document is open (in this demo, we keep an array of all available documents to simplify the demo) | ||||||
|  |     bool        OpenPrev;       // Copy of Open from last update. | ||||||
|  |     bool        Dirty;          // Set when the document has been modified | ||||||
|  |     bool        WantClose;      // Set when the document  | ||||||
|  |     ImVec4      Color;          // An arbitrary variable associated to the document | ||||||
|  |  | ||||||
|  |     MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f,1.0f,1.0f,1.0f)) | ||||||
|  |     {  | ||||||
|  |         Name = name;  | ||||||
|  |         Open = OpenPrev = open;  | ||||||
|  |         Dirty = false;  | ||||||
|  |         WantClose = false;  | ||||||
|  |         Color = color;  | ||||||
|  |     } | ||||||
|  |     void DoOpen()       { Open = true; } | ||||||
|  |     void DoQueueClose() { WantClose = true; } | ||||||
|  |     void DoForceClose() { Open = false; Dirty = false; } | ||||||
|  |     void DoSave()       { Dirty = false; } | ||||||
|  |  | ||||||
|  |     // Display dummy contents for the Document | ||||||
|  |     static void DisplayContents(MyDocument* doc) | ||||||
|  |     { | ||||||
|  |         ImGui::PushID(doc); | ||||||
|  |         ImGui::Text("Document \"%s\"", doc->Name); | ||||||
|  |         ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); | ||||||
|  |         ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); | ||||||
|  |         ImGui::PopStyleColor(); | ||||||
|  |         if (ImGui::Button("Modify", ImVec2(100, 0))) | ||||||
|  |             doc->Dirty = true; | ||||||
|  |         ImGui::SameLine(); | ||||||
|  |         if (ImGui::Button("Save", ImVec2(100, 0))) | ||||||
|  |             doc->DoSave(); | ||||||
|  |         ImGui::ColorEdit3("color", &doc->Color.x);  // Useful to test drag and drop and hold-dragged-to-open-tab behavior. | ||||||
|  |         ImGui::PopID(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Display context menu for the Document | ||||||
|  |     static void DisplayContextMenu(MyDocument* doc) | ||||||
|  |     { | ||||||
|  |         if (!ImGui::BeginPopupContextItem()) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|  |         char buf[256]; | ||||||
|  |         sprintf(buf, "Save %s", doc->Name); | ||||||
|  |         if (ImGui::MenuItem(buf, "CTRL+S", false, doc->Open)) | ||||||
|  |             doc->DoSave(); | ||||||
|  |         if (ImGui::MenuItem("Close", "CTRL+W", false, doc->Open)) | ||||||
|  |             doc->DoQueueClose(); | ||||||
|  |         ImGui::EndPopup(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct ExampleAppDocuments | ||||||
|  | { | ||||||
|  |     ImVector<MyDocument> Documents; | ||||||
|  |  | ||||||
|  |     ExampleAppDocuments() | ||||||
|  |     { | ||||||
|  |         Documents.push_back(MyDocument("Lettuce",             true,  ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); | ||||||
|  |         Documents.push_back(MyDocument("Eggplant",            true,  ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); | ||||||
|  |         Documents.push_back(MyDocument("Carrot",              true,  ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); | ||||||
|  |         Documents.push_back(MyDocument("Tomato",              false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); | ||||||
|  |         Documents.push_back(MyDocument("A Rather Long Title", false)); | ||||||
|  |         Documents.push_back(MyDocument("Some Document",       false)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface. | ||||||
|  | // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, as opposed | ||||||
|  | // to clicking on the regular tab closing button) and stops being submitted, it will take a frame for the tab bar to notice its absence.  | ||||||
|  | // During this frame there will be a gap in the tab bar, and if the tab that has disappeared was the selected one, the tab bar  | ||||||
|  | // will report no selected tab during the frame. This will effectively give the impression of a flicker for one frame. | ||||||
|  | // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. | ||||||
|  | // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. | ||||||
|  | static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app) | ||||||
|  | { | ||||||
|  |     for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) | ||||||
|  |     { | ||||||
|  |         MyDocument* doc = &app.Documents[doc_n]; | ||||||
|  |         if (!doc->Open && doc->OpenPrev) | ||||||
|  |             ImGui::SetTabItemClosed(doc->Name); | ||||||
|  |         doc->OpenPrev = doc->Open; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ShowExampleAppDocuments(bool* p_open) | ||||||
|  | { | ||||||
|  |     static ExampleAppDocuments app; | ||||||
|  |  | ||||||
|  |     ImGui::Begin("Examples: Documents", p_open, ImGuiWindowFlags_MenuBar); | ||||||
|  |  | ||||||
|  |     // Options | ||||||
|  |     static bool opt_reorderable = true; | ||||||
|  |     static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; | ||||||
|  |  | ||||||
|  |     // Menu | ||||||
|  |     if (ImGui::BeginMenuBar()) | ||||||
|  |     { | ||||||
|  |         if (ImGui::BeginMenu("File")) | ||||||
|  |         { | ||||||
|  |             int open_count = 0; | ||||||
|  |             for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) | ||||||
|  |                 open_count += app.Documents[doc_n].Open ? 1 : 0; | ||||||
|  |  | ||||||
|  |             if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) | ||||||
|  |             { | ||||||
|  |                 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) | ||||||
|  |                 { | ||||||
|  |                     MyDocument* doc = &app.Documents[doc_n]; | ||||||
|  |                     if (!doc->Open) | ||||||
|  |                         if (ImGui::MenuItem(doc->Name)) | ||||||
|  |                             doc->DoOpen(); | ||||||
|  |                 } | ||||||
|  |                 ImGui::EndMenu(); | ||||||
|  |             } | ||||||
|  |             if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) | ||||||
|  |                 for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) | ||||||
|  |                     app.Documents[doc_n].DoQueueClose(); | ||||||
|  |             if (ImGui::MenuItem("Exit", "Alt+F4")) {} | ||||||
|  |             ImGui::EndMenu(); | ||||||
|  |         } | ||||||
|  |         ImGui::EndMenuBar(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // [Debug] List documents with one checkbox for each | ||||||
|  |     for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) | ||||||
|  |     { | ||||||
|  |         MyDocument* doc = &app.Documents[doc_n]; | ||||||
|  |         if (doc_n > 0) | ||||||
|  |             ImGui::SameLine(); | ||||||
|  |         ImGui::PushID(doc); | ||||||
|  |         if (ImGui::Checkbox(doc->Name, &doc->Open)) | ||||||
|  |             if (!doc->Open) | ||||||
|  |                 doc->DoForceClose(); | ||||||
|  |         ImGui::PopID(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::Separator(); | ||||||
|  |  | ||||||
|  |     // Submit Tab Bar and Tabs | ||||||
|  |     { | ||||||
|  |         ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); | ||||||
|  |         if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) | ||||||
|  |         { | ||||||
|  |             if (opt_reorderable) | ||||||
|  |                 NotifyOfDocumentsClosedElsewhere(app); | ||||||
|  |  | ||||||
|  |             // [DEBUG] Stress tests | ||||||
|  |             //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1;            // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. | ||||||
|  |             //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name);  // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. | ||||||
|  |  | ||||||
|  |             // Submit Tabs | ||||||
|  |             for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) | ||||||
|  |             { | ||||||
|  |                 MyDocument* doc = &app.Documents[doc_n]; | ||||||
|  |                 if (!doc->Open) | ||||||
|  |                     continue; | ||||||
|  |  | ||||||
|  |                 ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); | ||||||
|  |                 bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); | ||||||
|  |  | ||||||
|  |                 // Cancel attempt to close when unsaved add to save queue so we can display a popup. | ||||||
|  |                 if (!doc->Open && doc->Dirty) | ||||||
|  |                 { | ||||||
|  |                     doc->Open = true; | ||||||
|  |                     doc->DoQueueClose(); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 MyDocument::DisplayContextMenu(doc); | ||||||
|  |                 if (visible) | ||||||
|  |                 { | ||||||
|  |                     MyDocument::DisplayContents(doc); | ||||||
|  |                     ImGui::EndTabItem(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             ImGui::EndTabBar(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Update closing queue | ||||||
|  |     static ImVector<MyDocument*> close_queue; | ||||||
|  |     if (close_queue.empty()) | ||||||
|  |     { | ||||||
|  |         // Close queue is locked once we started a popup | ||||||
|  |         for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) | ||||||
|  |         { | ||||||
|  |             MyDocument* doc = &app.Documents[doc_n]; | ||||||
|  |             if (doc->WantClose) | ||||||
|  |             { | ||||||
|  |                 doc->WantClose = false; | ||||||
|  |                 close_queue.push_back(doc); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Display closing confirmation UI | ||||||
|  |     if (!close_queue.empty()) | ||||||
|  |     { | ||||||
|  |         int close_queue_unsaved_documents = 0; | ||||||
|  |         for (int n = 0; n < close_queue.Size; n++) | ||||||
|  |             if (close_queue[n]->Dirty) | ||||||
|  |                 close_queue_unsaved_documents++; | ||||||
|  |  | ||||||
|  |         if (close_queue_unsaved_documents == 0) | ||||||
|  |         { | ||||||
|  |             // Close documents when all are unsaved | ||||||
|  |             for (int n = 0; n < close_queue.Size; n++) | ||||||
|  |                 close_queue[n]->DoForceClose(); | ||||||
|  |             close_queue.clear(); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             if (!ImGui::IsPopupOpen("Save?")) | ||||||
|  |                 ImGui::OpenPopup("Save?"); | ||||||
|  |             if (ImGui::BeginPopupModal("Save?")) | ||||||
|  |             { | ||||||
|  |                 ImGui::Text("Save change to the following items?"); | ||||||
|  |                 ImGui::PushItemWidth(-1.0f); | ||||||
|  |                 ImGui::ListBoxHeader("##", close_queue_unsaved_documents, 6); | ||||||
|  |                 for (int n = 0; n < close_queue.Size; n++) | ||||||
|  |                     if (close_queue[n]->Dirty) | ||||||
|  |                         ImGui::Text("%s", close_queue[n]->Name); | ||||||
|  |                 ImGui::ListBoxFooter(); | ||||||
|  |  | ||||||
|  |                 if (ImGui::Button("Yes", ImVec2(80, 0))) | ||||||
|  |                 { | ||||||
|  |                     for (int n = 0; n < close_queue.Size; n++) | ||||||
|  |                     { | ||||||
|  |                         if (close_queue[n]->Dirty) | ||||||
|  |                             close_queue[n]->DoSave(); | ||||||
|  |                         close_queue[n]->DoForceClose(); | ||||||
|  |                     } | ||||||
|  |                     close_queue.clear(); | ||||||
|  |                     ImGui::CloseCurrentPopup(); | ||||||
|  |                 } | ||||||
|  |                 ImGui::SameLine(); | ||||||
|  |                 if (ImGui::Button("No", ImVec2(80, 0))) | ||||||
|  |                 { | ||||||
|  |                     for (int n = 0; n < close_queue.Size; n++) | ||||||
|  |                         close_queue[n]->DoForceClose(); | ||||||
|  |                     close_queue.clear(); | ||||||
|  |                     ImGui::CloseCurrentPopup(); | ||||||
|  |                 } | ||||||
|  |                 ImGui::SameLine(); | ||||||
|  |                 if (ImGui::Button("Cancel", ImVec2(80, 0))) | ||||||
|  |                 { | ||||||
|  |                     close_queue.clear(); | ||||||
|  |                     ImGui::CloseCurrentPopup(); | ||||||
|  |                 } | ||||||
|  |                 ImGui::EndPopup(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     ImGui::End(); |     ImGui::End(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -205,6 +205,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) | |||||||
|     colors[ImGuiCol_ResizeGrip]             = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); |     colors[ImGuiCol_ResizeGrip]             = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); | ||||||
|     colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); |     colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); | ||||||
|     colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); |     colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); | ||||||
|  |     colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f); | ||||||
|  |     colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered]; | ||||||
|  |     colors[ImGuiCol_TabActive]              = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); | ||||||
|  |     colors[ImGuiCol_TabUnfocused]           = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f); | ||||||
|  |     colors[ImGuiCol_TabUnfocusedActive]     = ImLerp(colors[ImGuiCol_TabActive],    colors[ImGuiCol_TitleBg], 0.40f); | ||||||
|     colors[ImGuiCol_PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); |     colors[ImGuiCol_PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); | ||||||
|     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); |     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); | ||||||
|     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); |     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); | ||||||
| @@ -255,6 +260,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) | |||||||
|     colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.16f); |     colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.16f); | ||||||
|     colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); |     colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); | ||||||
|     colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); |     colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); | ||||||
|  |     colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f); | ||||||
|  |     colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered]; | ||||||
|  |     colors[ImGuiCol_TabActive]              = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); | ||||||
|  |     colors[ImGuiCol_TabUnfocused]           = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f); | ||||||
|  |     colors[ImGuiCol_TabUnfocusedActive]     = ImLerp(colors[ImGuiCol_TabActive],    colors[ImGuiCol_TitleBg], 0.40f); | ||||||
|     colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); |     colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); | ||||||
|     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); |     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); | ||||||
|     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); |     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); | ||||||
| @@ -306,6 +316,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) | |||||||
|     colors[ImGuiCol_ResizeGrip]             = ImVec4(0.80f, 0.80f, 0.80f, 0.56f); |     colors[ImGuiCol_ResizeGrip]             = ImVec4(0.80f, 0.80f, 0.80f, 0.56f); | ||||||
|     colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); |     colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); | ||||||
|     colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); |     colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); | ||||||
|  |     colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.90f); | ||||||
|  |     colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered]; | ||||||
|  |     colors[ImGuiCol_TabActive]              = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); | ||||||
|  |     colors[ImGuiCol_TabUnfocused]           = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f); | ||||||
|  |     colors[ImGuiCol_TabUnfocusedActive]     = ImLerp(colors[ImGuiCol_TabActive],    colors[ImGuiCol_TitleBg], 0.40f); | ||||||
|     colors[ImGuiCol_PlotLines]              = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); |     colors[ImGuiCol_PlotLines]              = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); | ||||||
|     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); |     colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); | ||||||
|     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); |     colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); | ||||||
| @@ -2821,6 +2836,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col | |||||||
| // - RenderMouseCursor() | // - RenderMouseCursor() | ||||||
| // - RenderArrowPointingAt() | // - RenderArrowPointingAt() | ||||||
| // - RenderRectFilledRangeH() | // - RenderRectFilledRangeH() | ||||||
|  | // - RenderPixelEllipsis() | ||||||
| //----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) | void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) | ||||||
| @@ -2929,6 +2945,15 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im | |||||||
|     draw_list->PathFillConvex(col); |     draw_list->PathFillConvex(col); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem for us... we cannot rely on font glyph having it,  | ||||||
|  | // and regular dot are typically too wide. If we render a dot/shape ourselves it comes with the risk that it wouldn't match  | ||||||
|  | // the boldness or positioning of what the font uses... | ||||||
|  | void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImFont* font, ImVec2 pos, int count, ImU32 col) | ||||||
|  | { | ||||||
|  |     pos.y += (float)(int)(font->DisplayOffset.y + font->Ascent + 0.5f - 1.0f); | ||||||
|  |     for (int dot_n = 0; dot_n < count; dot_n++) | ||||||
|  |         draw_list->AddRectFilled(ImVec2(pos.x + dot_n * 2.0f, pos.y), ImVec2(pos.x + dot_n * 2.0f + 1.0f, pos.y + 1.0f), col); | ||||||
|  | } | ||||||
|  |  | ||||||
| //----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||||
| // [SECTION] Decompression code | // [SECTION] Decompression code | ||||||
|   | |||||||
| @@ -49,6 +49,8 @@ struct ImGuiNextWindowData;         // Storage for SetNexWindow** functions | |||||||
| struct ImGuiPopupRef;               // Storage for current popup stack | struct ImGuiPopupRef;               // Storage for current popup stack | ||||||
| struct ImGuiSettingsHandler;        // Storage for one type registered in the .ini file | 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 ImGuiStyleMod;               // Stacked style modifier, backup of modified data so we can restore it | ||||||
|  | struct ImGuiTabBar;                 // Storage for a tab bar | ||||||
|  | struct ImGuiTabItem;                // Storage for a tab item (within a tab bar) | ||||||
| struct ImGuiWindow;                 // Storage for one window | struct ImGuiWindow;                 // Storage for one window | ||||||
| struct ImGuiWindowTempData;         // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) | struct ImGuiWindowTempData;         // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) | ||||||
| struct ImGuiWindowSettings;         // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) | struct ImGuiWindowSettings;         // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) | ||||||
| @@ -679,6 +681,12 @@ struct ImGuiNextWindowData | |||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct ImGuiTabBarSortItem | ||||||
|  | { | ||||||
|  |     int         Index; | ||||||
|  |     float       Width; | ||||||
|  | }; | ||||||
|  |  | ||||||
| //----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||||
| // Main imgui context | // Main imgui context | ||||||
| //----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||||
| @@ -804,6 +812,11 @@ struct ImGuiContext | |||||||
|     ImVector<unsigned char> DragDropPayloadBufHeap;             // We don't expose the ImVector<> directly |     ImVector<unsigned char> DragDropPayloadBufHeap;             // We don't expose the ImVector<> directly | ||||||
|     unsigned char           DragDropPayloadBufLocal[8];         // Local buffer for small payloads |     unsigned char           DragDropPayloadBufLocal[8];         // Local buffer for small payloads | ||||||
|  |  | ||||||
|  |     // Tab bars | ||||||
|  |     ImPool<ImGuiTabBar>     TabBars; | ||||||
|  |     ImVector<ImGuiTabBar*>  CurrentTabBar; | ||||||
|  |     ImVector<ImGuiTabBarSortItem>   TabSortByWidthBuffer; | ||||||
|  |  | ||||||
|     // Widget state |     // Widget state | ||||||
|     ImGuiInputTextState     InputTextState; |     ImGuiInputTextState     InputTextState; | ||||||
|     ImFont                  InputTextPasswordFont; |     ImFont                  InputTextPasswordFont; | ||||||
| @@ -1154,6 +1167,59 @@ struct ImGuiItemHoveredDataBackup | |||||||
|     void Restore() const    { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } |     void Restore() const    { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | //----------------------------------------------------------------------------- | ||||||
|  | // Tab Bar, Tab Item | ||||||
|  | //----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | enum ImGuiTabBarFlagsPrivate_ | ||||||
|  | { | ||||||
|  |     ImGuiTabBarFlags_DockNode                   = 1 << 20,  // [Docking: Unused in Master Branch] Part of a dock node | ||||||
|  |     ImGuiTabBarFlags_DockNodeIsDockSpace        = 1 << 21,  // [Docking: Unused in Master Branch] Part of an explicit dockspace node node | ||||||
|  |     ImGuiTabBarFlags_IsFocused                  = 1 << 22, | ||||||
|  |     ImGuiTabBarFlags_SaveSettings               = 1 << 23   // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Storage for one active tab item (sizeof() 26~32 bytes) | ||||||
|  | struct ImGuiTabItem | ||||||
|  | { | ||||||
|  |     ImGuiID             ID; | ||||||
|  |     ImGuiTabItemFlags   Flags; | ||||||
|  |     int                 LastFrameVisible; | ||||||
|  |     int                 LastFrameSelected;      // This allows us to infer an ordered list of the last activated tabs with little maintenance | ||||||
|  |     float               Offset;                 // Position relative to beginning of tab | ||||||
|  |     float               Width;                  // Width currently displayed | ||||||
|  |     float               WidthContents;          // Width of actual contents, stored during BeginTabItem() call | ||||||
|  |  | ||||||
|  |     ImGuiTabItem()      { ID = Flags = 0; LastFrameVisible = LastFrameSelected = -1; Offset = Width = WidthContents = 0.0f; } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Storage for a tab bar (sizeof() 92~96 bytes) | ||||||
|  | struct ImGuiTabBar | ||||||
|  | { | ||||||
|  |     ImVector<ImGuiTabItem> Tabs; | ||||||
|  |     ImGuiID             ID;                     // Zero for tab-bars used by docking | ||||||
|  |     ImGuiID             SelectedTabId;          // Selected tab | ||||||
|  |     ImGuiID             NextSelectedTabId; | ||||||
|  |     ImGuiID             VisibleTabId;           // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) | ||||||
|  |     int                 CurrFrameVisible; | ||||||
|  |     int                 PrevFrameVisible; | ||||||
|  |     ImRect              BarRect; | ||||||
|  |     float               ContentsHeight; | ||||||
|  |     float               OffsetMax;              // Distance from BarRect.Min.x, locked during layout | ||||||
|  |     float               OffsetNextTab;          // Distance from BarRect.Min.x, incremented with each BeginTabItem() call, not used if ImGuiTabBarFlags_Reorderable if set. | ||||||
|  |     float               ScrollingAnim; | ||||||
|  |     float               ScrollingTarget; | ||||||
|  |     ImGuiTabBarFlags    Flags; | ||||||
|  |     ImGuiID             ReorderRequestTabId; | ||||||
|  |     int                 ReorderRequestDir; | ||||||
|  |     bool                WantLayout; | ||||||
|  |     bool                VisibleTabWasSubmitted; | ||||||
|  |     short               LastTabItemIdx;         // For BeginTabItem()/EndTabItem() | ||||||
|  |  | ||||||
|  |     ImGuiTabBar(); | ||||||
|  |     int                 GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_pointer(tab); } | ||||||
|  | }; | ||||||
|  |  | ||||||
| //----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||||
| // Internal API | // Internal API | ||||||
| // No guarantee of forward compatibility here. | // No guarantee of forward compatibility here. | ||||||
| @@ -1270,12 +1336,24 @@ namespace ImGui | |||||||
|     IMGUI_API void          EndColumns();                                                             // close columns |     IMGUI_API void          EndColumns();                                                             // close columns | ||||||
|     IMGUI_API void          PushColumnClipRect(int column_index = -1); |     IMGUI_API void          PushColumnClipRect(int column_index = -1); | ||||||
|  |  | ||||||
|  |     // Tab Bars | ||||||
|  |     IMGUI_API bool          BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); | ||||||
|  |     IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); | ||||||
|  |     IMGUI_API void          TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); | ||||||
|  |     IMGUI_API void          TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); | ||||||
|  |     IMGUI_API void          TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); | ||||||
|  |     IMGUI_API bool          TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); | ||||||
|  |     IMGUI_API ImVec2        TabItemCalcSize(const char* label, bool has_close_button); | ||||||
|  |     IMGUI_API void          TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); | ||||||
|  |     IMGUI_API bool          TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, const char* label, ImGuiID tab_id, ImGuiID close_button_id); | ||||||
|  |  | ||||||
|     // Render helpers |     // Render helpers | ||||||
|     // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. |     // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. | ||||||
|     // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) |     // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) | ||||||
|     IMGUI_API void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); |     IMGUI_API void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); | ||||||
|     IMGUI_API void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); |     IMGUI_API void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); | ||||||
|     IMGUI_API void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); |     IMGUI_API void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); | ||||||
|  |     IMGUI_API void          RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); | ||||||
|     IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); |     IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); | ||||||
|     IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); |     IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); | ||||||
|     IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); |     IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); | ||||||
| @@ -1290,6 +1368,7 @@ namespace ImGui | |||||||
|     IMGUI_API void          RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); |     IMGUI_API void          RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); | ||||||
|     IMGUI_API void          RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); |     IMGUI_API void          RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); | ||||||
|     IMGUI_API void          RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); |     IMGUI_API void          RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); | ||||||
|  |     IMGUI_API void          RenderPixelEllipsis(ImDrawList* draw_list, ImFont* font, ImVec2 pos, int count, ImU32 col); | ||||||
|  |  | ||||||
|     // Widgets |     // Widgets | ||||||
|     IMGUI_API bool          ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); |     IMGUI_API bool          ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); | ||||||
|   | |||||||
| @@ -22,6 +22,8 @@ Index of this file: | |||||||
| // [SECTION] Widgets: PlotLines, PlotHistogram | // [SECTION] Widgets: PlotLines, PlotHistogram | ||||||
| // [SECTION] Widgets: Value helpers | // [SECTION] Widgets: Value helpers | ||||||
| // [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. | // [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. | ||||||
|  | // [SECTION] Widgets: BeginTabBar, EndTabBar, etc. | ||||||
|  | // [SECTION] Widgets: BeginTabItem, EndTabItem, etc. | ||||||
|  |  | ||||||
| */ | */ | ||||||
|  |  | ||||||
| @@ -5748,3 +5750,791 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, | |||||||
|     } |     } | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  | // [SECTION] Widgets: BeginTabBar, EndTabBar, etc. | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  | // [BETA API] API may evolve! This code has been extracted out of the Docking branch, | ||||||
|  | // and some of the construct which are not used in Master may be left here to facilitate merging. | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  | // - BeginTabBar() | ||||||
|  | // - BeginTabBarEx() [Internal] | ||||||
|  | // - EndTabBar() | ||||||
|  | // - TabBarLayout() [Internal] | ||||||
|  | // - TabBarCalcTabID() [Internal] | ||||||
|  | // - TabBarCalcMaxTabWidth() [Internal] | ||||||
|  | // - TabBarFindTabById() [Internal] | ||||||
|  | // - TabBarRemoveTab() [Internal] | ||||||
|  | // - TabBarCloseTab() [Internal] | ||||||
|  | // - TabBarScrollClamp()v | ||||||
|  | // - TabBarScrollToTab() [Internal] | ||||||
|  | // - TabBarQueueChangeTabOrder() [Internal] | ||||||
|  | // - TabBarScrollingButtons() [Internal] | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | namespace ImGui | ||||||
|  | { | ||||||
|  |     static void             TabBarLayout(ImGuiTabBar* tab_bar); | ||||||
|  |     static ImU32            TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); | ||||||
|  |     static float            TabBarCalcMaxTabWidth(); | ||||||
|  |     static float            TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); | ||||||
|  |     static void             TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); | ||||||
|  |     static ImGuiTabItem*    TabBarScrollingButtons(ImGuiTabBar* tab_bar); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ImGuiTabBar::ImGuiTabBar() | ||||||
|  | { | ||||||
|  |     ID = 0; | ||||||
|  |     SelectedTabId = NextSelectedTabId = VisibleTabId = 0; | ||||||
|  |     CurrFrameVisible = PrevFrameVisible = -1; | ||||||
|  |     OffsetMax = OffsetNextTab = 0.0f; | ||||||
|  |     ScrollingAnim = ScrollingTarget = 0.0f; | ||||||
|  |     Flags = ImGuiTabBarFlags_None; | ||||||
|  |     ReorderRequestTabId = 0; | ||||||
|  |     ReorderRequestDir = 0; | ||||||
|  |     WantLayout = VisibleTabWasSubmitted = false; | ||||||
|  |     LastTabItemIdx = -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs) | ||||||
|  | { | ||||||
|  |     const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; | ||||||
|  |     const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; | ||||||
|  |     return (int)(a->Offset - b->Offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs) | ||||||
|  | { | ||||||
|  |     const ImGuiTabBarSortItem* a = (const ImGuiTabBarSortItem*)lhs; | ||||||
|  |     const ImGuiTabBarSortItem* b = (const ImGuiTabBarSortItem*)rhs; | ||||||
|  |     if (int d = (int)(b->Width - a->Width)) | ||||||
|  |         return d; | ||||||
|  |     return (b->Index - a->Index); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool    ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImGuiWindow* window = g.CurrentWindow; | ||||||
|  |     if (window->SkipItems) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     ImGuiID id = window->GetID(str_id); | ||||||
|  |     ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); | ||||||
|  |     ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->InnerClipRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); | ||||||
|  |     tab_bar->ID = id; | ||||||
|  |     return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool    ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImGuiWindow* window = g.CurrentWindow; | ||||||
|  |     if (window->SkipItems) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     if ((flags & ImGuiTabBarFlags_DockNode) == 0) | ||||||
|  |         window->IDStack.push_back(tab_bar->ID); | ||||||
|  |  | ||||||
|  |     g.CurrentTabBar.push_back(tab_bar); | ||||||
|  |     if (tab_bar->CurrFrameVisible == g.FrameCount) | ||||||
|  |     { | ||||||
|  |         //printf("[%05d] BeginTabBarEx already called this frame\n", g.FrameCount); | ||||||
|  |         IM_ASSERT(0); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // When toggling back from ordered to manually-reorderable, shuffle tabs to enforce the last visible order.  | ||||||
|  |     // Otherwise, the most recently inserted tabs would move at the end of visible list which can be a little too confusing or magic for the user. | ||||||
|  |     if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1) | ||||||
|  |         ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset); | ||||||
|  |  | ||||||
|  |     // Flags | ||||||
|  |     if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) | ||||||
|  |         flags |= ImGuiTabBarFlags_FittingPolicyDefault_; | ||||||
|  |  | ||||||
|  |     tab_bar->Flags = flags; | ||||||
|  |     tab_bar->BarRect = tab_bar_bb; | ||||||
|  |     tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() | ||||||
|  |     tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; | ||||||
|  |     tab_bar->CurrFrameVisible = g.FrameCount; | ||||||
|  |  | ||||||
|  |     // Layout     | ||||||
|  |     ItemSize(ImVec2(tab_bar->OffsetMax, tab_bar->BarRect.GetHeight())); | ||||||
|  |     window->DC.CursorPos.x = tab_bar->BarRect.Min.x; | ||||||
|  |  | ||||||
|  |     // Draw separator | ||||||
|  |     const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab); | ||||||
|  |     const float y = tab_bar->BarRect.Max.y - 1.0f; | ||||||
|  |     { | ||||||
|  |         const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x); | ||||||
|  |         const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x); | ||||||
|  |         window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void    ImGui::EndTabBar() | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImGuiWindow* window = g.CurrentWindow; | ||||||
|  |     if (window->SkipItems) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     IM_ASSERT(!g.CurrentTabBar.empty());      // Mismatched BeginTabBar/EndTabBar | ||||||
|  |     ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); | ||||||
|  |     if (tab_bar->WantLayout) | ||||||
|  |         TabBarLayout(tab_bar); | ||||||
|  |  | ||||||
|  |     // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). | ||||||
|  |     const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); | ||||||
|  |     if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) | ||||||
|  |         tab_bar->ContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f); | ||||||
|  |     else | ||||||
|  |         window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->ContentsHeight; | ||||||
|  |  | ||||||
|  |     if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) | ||||||
|  |         PopID(); | ||||||
|  |     g.CurrentTabBar.pop_back(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // This is called only once a frame before by the first call to ItemTab() | ||||||
|  | // The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. | ||||||
|  | static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     tab_bar->WantLayout = false; | ||||||
|  |  | ||||||
|  |     // Garbage collect | ||||||
|  |     int tab_dst_n = 0; | ||||||
|  |     for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++) | ||||||
|  |     { | ||||||
|  |         ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n]; | ||||||
|  |         if (tab->LastFrameVisible < tab_bar->PrevFrameVisible) | ||||||
|  |         { | ||||||
|  |             if (tab->ID == tab_bar->SelectedTabId) | ||||||
|  |                 tab_bar->SelectedTabId = 0; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (tab_dst_n != tab_src_n) | ||||||
|  |             tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; | ||||||
|  |         tab_dst_n++; | ||||||
|  |     } | ||||||
|  |     if (tab_bar->Tabs.Size != tab_dst_n) | ||||||
|  |         tab_bar->Tabs.resize(tab_dst_n); | ||||||
|  |  | ||||||
|  |     // Setup next selected tab | ||||||
|  |     ImGuiID scroll_track_selected_tab_id = 0; | ||||||
|  |     if (tab_bar->NextSelectedTabId) | ||||||
|  |     { | ||||||
|  |         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; | ||||||
|  |         tab_bar->NextSelectedTabId = 0; | ||||||
|  |         scroll_track_selected_tab_id = tab_bar->SelectedTabId; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). | ||||||
|  |     if (tab_bar->ReorderRequestTabId != 0) | ||||||
|  |     { | ||||||
|  |         if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId)) | ||||||
|  |         { | ||||||
|  |             //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools | ||||||
|  |             int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; | ||||||
|  |             if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size) | ||||||
|  |             { | ||||||
|  |                 ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; | ||||||
|  |                 ImGuiTabItem item_tmp = *tab1; | ||||||
|  |                 *tab1 = *tab2; | ||||||
|  |                 *tab2 = item_tmp; | ||||||
|  |                 if (tab2->ID == tab_bar->SelectedTabId) | ||||||
|  |                     scroll_track_selected_tab_id = tab2->ID; | ||||||
|  |                 tab1 = tab2 = NULL; | ||||||
|  |             } | ||||||
|  |             if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) | ||||||
|  |                 MarkIniSettingsDirty(); | ||||||
|  |         } | ||||||
|  |         tab_bar->ReorderRequestTabId = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImVector<ImGuiTabBarSortItem>& width_sort_buffer = g.TabSortByWidthBuffer; | ||||||
|  |     width_sort_buffer.resize(tab_bar->Tabs.Size); | ||||||
|  |  | ||||||
|  |     // Compute ideal widths | ||||||
|  |     float width_total_contents = 0.0f; | ||||||
|  |     ImGuiTabItem* most_recently_selected_tab = NULL; | ||||||
|  |     bool found_selected_tab_id = false; | ||||||
|  |     for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) | ||||||
|  |     { | ||||||
|  |         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; | ||||||
|  |         IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible); | ||||||
|  |  | ||||||
|  |         if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) | ||||||
|  |             most_recently_selected_tab = tab; | ||||||
|  |         if (tab->ID == tab_bar->SelectedTabId) | ||||||
|  |             found_selected_tab_id = true; | ||||||
|  |  | ||||||
|  |         // Refresh tab width immediately if we can (for manual tab bar, WidthContent will lag by one frame which is mostly noticeable when changing style.FramePadding.x) | ||||||
|  |         // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,  | ||||||
|  |         // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. | ||||||
|  |         width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents; | ||||||
|  |  | ||||||
|  |         // Store data so we can build an array sorted by width if we need to shrink tabs down | ||||||
|  |         width_sort_buffer[tab_n].Index = tab_n; | ||||||
|  |         width_sort_buffer[tab_n].Width = tab->WidthContents; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Compute width | ||||||
|  |     const float width_avail = tab_bar->BarRect.GetWidth(); | ||||||
|  |     float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f; | ||||||
|  |     if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown)) | ||||||
|  |     { | ||||||
|  |         // If we don't have enough room, resize down the largest tabs first | ||||||
|  |         if (tab_bar->Tabs.Size > 1) | ||||||
|  |             ImQsort(width_sort_buffer.Data, (size_t)width_sort_buffer.Size, sizeof(ImGuiTabBarSortItem), TabBarSortItemComparer); | ||||||
|  |         int tab_count_same_width = 1; | ||||||
|  |         while (width_excess > 0.0f && tab_count_same_width < tab_bar->Tabs.Size) | ||||||
|  |         { | ||||||
|  |             while (tab_count_same_width < tab_bar->Tabs.Size && width_sort_buffer[0].Width == width_sort_buffer[tab_count_same_width].Width) | ||||||
|  |                 tab_count_same_width++; | ||||||
|  |             float width_to_remove_per_tab_max = (tab_count_same_width < tab_bar->Tabs.Size) ? (width_sort_buffer[0].Width - width_sort_buffer[tab_count_same_width].Width) : (width_sort_buffer[0].Width - 1.0f); | ||||||
|  |             float width_to_remove_per_tab = ImMin(width_excess / tab_count_same_width, width_to_remove_per_tab_max); | ||||||
|  |             for (int tab_n = 0; tab_n < tab_count_same_width; tab_n++) | ||||||
|  |                 width_sort_buffer[tab_n].Width -= width_to_remove_per_tab; | ||||||
|  |             width_excess -= width_to_remove_per_tab * tab_count_same_width; | ||||||
|  |         } | ||||||
|  |         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) | ||||||
|  |             tab_bar->Tabs[width_sort_buffer[tab_n].Index].Width = (float)(int)width_sort_buffer[tab_n].Width; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         const float tab_max_width = TabBarCalcMaxTabWidth(); | ||||||
|  |         for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) | ||||||
|  |         { | ||||||
|  |             ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; | ||||||
|  |             tab->Width = ImMin(tab->WidthContents, tab_max_width); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Layout all active tabs | ||||||
|  |     float offset_x = 0.0f; | ||||||
|  |     for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) | ||||||
|  |     { | ||||||
|  |         ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; | ||||||
|  |         tab->Offset = offset_x; | ||||||
|  |         if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID) | ||||||
|  |             scroll_track_selected_tab_id = tab->ID; | ||||||
|  |         offset_x += tab->Width + g.Style.ItemInnerSpacing.x; | ||||||
|  |     } | ||||||
|  |     tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f); | ||||||
|  |     tab_bar->OffsetNextTab = 0.0f; | ||||||
|  |  | ||||||
|  |     // Horizontal scrolling buttons | ||||||
|  |     const bool scrolling_buttons = (tab_bar->OffsetMax > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll); | ||||||
|  |     if (scrolling_buttons) | ||||||
|  |         if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x! | ||||||
|  |             scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; | ||||||
|  |  | ||||||
|  |     // If we have lost the selected tab, select the next most recently active one | ||||||
|  |     if (found_selected_tab_id == false) | ||||||
|  |         tab_bar->SelectedTabId = 0; | ||||||
|  |     if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) | ||||||
|  |         scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; | ||||||
|  |  | ||||||
|  |     // Lock in visible tab | ||||||
|  |     tab_bar->VisibleTabId = tab_bar->SelectedTabId; | ||||||
|  |     tab_bar->VisibleTabWasSubmitted = false; | ||||||
|  |  | ||||||
|  |     // Update scrolling | ||||||
|  |     if (scroll_track_selected_tab_id) | ||||||
|  |         if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id)) | ||||||
|  |             TabBarScrollToTab(tab_bar, scroll_track_selected_tab); | ||||||
|  |     tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); | ||||||
|  |     tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); | ||||||
|  |     const float scrolling_speed = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) ? FLT_MAX : (g.IO.DeltaTime * g.FontSize * 70.0f); | ||||||
|  |     if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) | ||||||
|  |         tab_bar->ScrollingAnim = ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, scrolling_speed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. | ||||||
|  | static ImU32   ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) | ||||||
|  | { | ||||||
|  |     if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) | ||||||
|  |     { | ||||||
|  |         ImGuiID id = ImHash(label, 0); | ||||||
|  |         KeepAliveID(id); | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         ImGuiWindow* window = GImGui->CurrentWindow; | ||||||
|  |         return window->GetID(label); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static float ImGui::TabBarCalcMaxTabWidth() | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     return g.FontSize * 20.0f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) | ||||||
|  | { | ||||||
|  |     if (tab_id != 0) | ||||||
|  |         for (int n = 0; n < tab_bar->Tabs.Size; n++) | ||||||
|  |             if (tab_bar->Tabs[n].ID == tab_id) | ||||||
|  |                 return &tab_bar->Tabs[n]; | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. | ||||||
|  | void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) | ||||||
|  | { | ||||||
|  |     if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) | ||||||
|  |         tab_bar->Tabs.erase(tab); | ||||||
|  |     if (tab_bar->VisibleTabId == tab_id)      { tab_bar->VisibleTabId = 0; } | ||||||
|  |     if (tab_bar->SelectedTabId == tab_id)     { tab_bar->SelectedTabId = 0; } | ||||||
|  |     if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Called on manual closure attempt | ||||||
|  | void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) | ||||||
|  | { | ||||||
|  |     if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) | ||||||
|  |     { | ||||||
|  |         // This will remove a frame of lag for selecting another tab on closure. | ||||||
|  |         // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure | ||||||
|  |         tab->LastFrameVisible = -1; | ||||||
|  |         tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; | ||||||
|  |     } | ||||||
|  |     else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) | ||||||
|  |     { | ||||||
|  |         // Actually select before expecting closure | ||||||
|  |         tab_bar->NextSelectedTabId = tab->ID; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) | ||||||
|  | { | ||||||
|  |     scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth()); | ||||||
|  |     return ImMax(scrolling, 0.0f); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) | ||||||
|  |     int order = tab_bar->GetTabOrder(tab); | ||||||
|  |     float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f); | ||||||
|  |     float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f); | ||||||
|  |     if (tab_bar->ScrollingTarget > tab_x1) | ||||||
|  |         tab_bar->ScrollingTarget = tab_x1; | ||||||
|  |     if (tab_bar->ScrollingTarget + tab_bar->BarRect.GetWidth() < tab_x2) | ||||||
|  |         tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) | ||||||
|  | { | ||||||
|  |     IM_ASSERT(dir == -1 || dir == +1); | ||||||
|  |     IM_ASSERT(tab_bar->ReorderRequestTabId == 0); | ||||||
|  |     tab_bar->ReorderRequestTabId = tab->ID; | ||||||
|  |     tab_bar->ReorderRequestDir = dir; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImGuiWindow* window = g.CurrentWindow; | ||||||
|  |  | ||||||
|  |     const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f); | ||||||
|  |     const float scrolling_buttons_width = arrow_button_size.x * 2.0f; | ||||||
|  |  | ||||||
|  |     const ImVec2 backup_cursor_pos = window->DC.CursorPos; | ||||||
|  |     //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255)); | ||||||
|  |  | ||||||
|  |     const ImRect avail_bar_rect = tab_bar->BarRect; | ||||||
|  |     bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f))); | ||||||
|  |     if (want_clip_rect) | ||||||
|  |         PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true); | ||||||
|  |  | ||||||
|  |     ImGuiTabItem* tab_to_select = NULL; | ||||||
|  |  | ||||||
|  |     int select_dir = 0; | ||||||
|  |     ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; | ||||||
|  |     arrow_col.w *= 0.5f; | ||||||
|  |  | ||||||
|  |     PushStyleColor(ImGuiCol_Text, arrow_col); | ||||||
|  |     PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); | ||||||
|  |     const float backup_repeat_delay = g.IO.KeyRepeatDelay; | ||||||
|  |     const float backup_repeat_rate = g.IO.KeyRepeatRate; | ||||||
|  |     g.IO.KeyRepeatDelay = 0.250f; | ||||||
|  |     g.IO.KeyRepeatRate = 0.200f; | ||||||
|  |     window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y); | ||||||
|  |     if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) | ||||||
|  |         select_dir = -1; | ||||||
|  |     window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y); | ||||||
|  |     if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) | ||||||
|  |         select_dir = +1; | ||||||
|  |     PopStyleColor(2); | ||||||
|  |     g.IO.KeyRepeatRate = backup_repeat_rate; | ||||||
|  |     g.IO.KeyRepeatDelay = backup_repeat_delay; | ||||||
|  |  | ||||||
|  |     if (want_clip_rect) | ||||||
|  |         PopClipRect(); | ||||||
|  |  | ||||||
|  |     if (select_dir != 0) | ||||||
|  |         if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) | ||||||
|  |         { | ||||||
|  |             int selected_order = tab_bar->GetTabOrder(tab_item); | ||||||
|  |             int target_order = selected_order + select_dir; | ||||||
|  |             tab_to_select = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; // If we are at the end of the list, still scroll to make our tab visible | ||||||
|  |         } | ||||||
|  |     window->DC.CursorPos = backup_cursor_pos; | ||||||
|  |     tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; | ||||||
|  |  | ||||||
|  |     return tab_to_select; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  | // [SECTION] Widgets: BeginTabItem, EndTabItem, etc. | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  | // [BETA API] API may evolve! This code has been extracted out of the Docking branch, | ||||||
|  | // and some of the construct which are not used in Master may be left here to facilitate merging. | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  | // - BeginTabItem() | ||||||
|  | // - EndTabItem() | ||||||
|  | // - TabItemEx() [Internal] | ||||||
|  | // - SetTabItemClosed() | ||||||
|  | // - TabItemCalcSize() [Internal] | ||||||
|  | // - TabItemRenderBackground() [Internal] | ||||||
|  | // - TabItemLabelAndCloseButton() [Internal] | ||||||
|  | //------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | bool    ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     if (g.CurrentWindow->SkipItems) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!"); | ||||||
|  |     ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); | ||||||
|  |     bool ret = TabItemEx(tab_bar, label, p_open, flags); | ||||||
|  |     if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) | ||||||
|  |         PushID(tab_bar->Tabs[tab_bar->LastTabItemIdx].ID); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void    ImGui::EndTabItem() | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     if (g.CurrentWindow->SkipItems) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!"); | ||||||
|  |     ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); | ||||||
|  |     ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; | ||||||
|  |     if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) | ||||||
|  |         PopID(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags) | ||||||
|  | { | ||||||
|  |     // Layout whole tab bar if not already done | ||||||
|  |     if (tab_bar->WantLayout) | ||||||
|  |         TabBarLayout(tab_bar); | ||||||
|  |  | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImGuiWindow* window = g.CurrentWindow; | ||||||
|  |     if (window->SkipItems) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     const ImGuiStyle& style = g.Style; | ||||||
|  |     const ImGuiID id = TabBarCalcTabID(tab_bar, label); | ||||||
|  |  | ||||||
|  |     // If the user called us with *p_open == false, we early out and don't render. We make a dummy call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. | ||||||
|  |     if (p_open && !*p_open) | ||||||
|  |     { | ||||||
|  |         PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); | ||||||
|  |         ItemAdd(ImRect(), id); | ||||||
|  |         PopItemFlag(); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Calculate tab contents size | ||||||
|  |     ImVec2 size = TabItemCalcSize(label, p_open != NULL); | ||||||
|  |  | ||||||
|  |     // Acquire tab data | ||||||
|  |     ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id); | ||||||
|  |     bool tab_is_new = false; | ||||||
|  |     if (tab == NULL) | ||||||
|  |     { | ||||||
|  |         tab_bar->Tabs.push_back(ImGuiTabItem()); | ||||||
|  |         tab = &tab_bar->Tabs.back(); | ||||||
|  |         tab->ID = id; | ||||||
|  |         tab->Width = size.x; | ||||||
|  |         tab_is_new = true; | ||||||
|  |     } | ||||||
|  |     tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_pointer(tab); | ||||||
|  |     tab->WidthContents = size.x; | ||||||
|  |  | ||||||
|  |     const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); | ||||||
|  |     const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; | ||||||
|  |     const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); | ||||||
|  |     tab->LastFrameVisible = g.FrameCount; | ||||||
|  |     tab->Flags = flags; | ||||||
|  |  | ||||||
|  |     // If we are not reorderable, always reset offset based on submission order. | ||||||
|  |     // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!) | ||||||
|  |     if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) | ||||||
|  |     { | ||||||
|  |         tab->Offset = tab_bar->OffsetNextTab; | ||||||
|  |         tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Update selected tab | ||||||
|  |     if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) | ||||||
|  |         if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) | ||||||
|  |             tab_bar->NextSelectedTabId = id;  // New tabs gets activated | ||||||
|  |  | ||||||
|  |     // Lock visibility | ||||||
|  |     bool tab_contents_visible = (tab_bar->VisibleTabId == id); | ||||||
|  |     if (tab_contents_visible) | ||||||
|  |         tab_bar->VisibleTabWasSubmitted = true; | ||||||
|  |  | ||||||
|  |     // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches | ||||||
|  |     if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing) | ||||||
|  |         if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) | ||||||
|  |             tab_contents_visible = true; | ||||||
|  |  | ||||||
|  |     if (tab_appearing && !(tab_bar_appearing && !tab_is_new)) | ||||||
|  |     { | ||||||
|  |         PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); | ||||||
|  |         ItemAdd(ImRect(), id); | ||||||
|  |         PopItemFlag(); | ||||||
|  |         return tab_contents_visible; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (tab_bar->SelectedTabId == id) | ||||||
|  |         tab->LastFrameSelected = g.FrameCount; | ||||||
|  |  | ||||||
|  |     // Backup current layout position | ||||||
|  |     const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; | ||||||
|  |  | ||||||
|  |     // Layout | ||||||
|  |     size.x = tab->Width; | ||||||
|  |     window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2((float)(int)tab->Offset - tab_bar->ScrollingAnim, 0.0f); | ||||||
|  |     ImVec2 pos = window->DC.CursorPos; | ||||||
|  |     ImRect bb(pos, pos + size); | ||||||
|  |  | ||||||
|  |     // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) | ||||||
|  |     bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x >= tab_bar->BarRect.Max.x); | ||||||
|  |     if (want_clip_rect) | ||||||
|  |         PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true); | ||||||
|  |  | ||||||
|  |     ItemSize(bb, style.FramePadding.y); | ||||||
|  |     if (!ItemAdd(bb, id)) | ||||||
|  |     { | ||||||
|  |         if (want_clip_rect) | ||||||
|  |             PopClipRect(); | ||||||
|  |         window->DC.CursorPos = backup_main_cursor_pos; | ||||||
|  |         return tab_contents_visible; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Click to Select a tab | ||||||
|  |     ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap); | ||||||
|  |     bool hovered, held; | ||||||
|  |     bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); | ||||||
|  |     hovered |= (g.HoveredId == id); | ||||||
|  |     if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar | ||||||
|  |         tab_bar->NextSelectedTabId = id; | ||||||
|  |  | ||||||
|  |     // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) | ||||||
|  |     if (!held) | ||||||
|  |         SetItemAllowOverlap(); | ||||||
|  |  | ||||||
|  |     // Drag and drop: re-order tabs | ||||||
|  |     if (held && !tab_appearing && IsMouseDragging(0)) | ||||||
|  |     { | ||||||
|  |         if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) | ||||||
|  |         { | ||||||
|  |             // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x | ||||||
|  |             if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) | ||||||
|  |             { | ||||||
|  |                 if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) | ||||||
|  |                     TabBarQueueChangeTabOrder(tab_bar, tab, -1); | ||||||
|  |             } | ||||||
|  |             else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) | ||||||
|  |             { | ||||||
|  |                 if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) | ||||||
|  |                     TabBarQueueChangeTabOrder(tab_bar, tab, +1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #if 0 | ||||||
|  |     if (hovered && g.HoveredIdNotActiveTimer > 0.50f && bb.GetWidth() < tab->WidthContents) | ||||||
|  |     { | ||||||
|  |         // Enlarge tab display when hovering | ||||||
|  |         bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)); | ||||||
|  |         display_draw_list = GetOverlayDrawList(window); | ||||||
|  |         TabItemRenderBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     // Render tab shape | ||||||
|  |     ImDrawList* display_draw_list = window->DrawList; | ||||||
|  |     const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused)); | ||||||
|  |     TabItemBackground(display_draw_list, bb, flags, tab_col); | ||||||
|  |     RenderNavHighlight(bb, id); | ||||||
|  |  | ||||||
|  |     // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. | ||||||
|  |     const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); | ||||||
|  |     if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1))) | ||||||
|  |         tab_bar->NextSelectedTabId = id; | ||||||
|  |  | ||||||
|  |     if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) | ||||||
|  |         flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; | ||||||
|  |  | ||||||
|  |     // Render tab label, process close button | ||||||
|  |     const ImGuiID close_button_id = p_open ? window->GetID((void*)(intptr_t)(id + 1)) : 0; | ||||||
|  |     bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, label, id, close_button_id); | ||||||
|  |     if (just_closed) | ||||||
|  |     { | ||||||
|  |         *p_open = false; | ||||||
|  |         TabBarCloseTab(tab_bar, tab); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Restore main window position so user can draw there | ||||||
|  |     if (want_clip_rect) | ||||||
|  |         PopClipRect(); | ||||||
|  |     window->DC.CursorPos = backup_main_cursor_pos; | ||||||
|  |  | ||||||
|  |     // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) | ||||||
|  |     if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f) | ||||||
|  |         SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); | ||||||
|  |  | ||||||
|  |     return tab_contents_visible; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed. | ||||||
|  | // To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem() | ||||||
|  | void    ImGui::SetTabItemClosed(const char* label) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     bool is_within_manual_tab_bar = (g.CurrentTabBar.Size > 0) && !(g.CurrentTabBar.back()->Flags & ImGuiTabBarFlags_DockNode); | ||||||
|  |     if (is_within_manual_tab_bar) | ||||||
|  |     { | ||||||
|  |         ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); | ||||||
|  |         IM_ASSERT(tab_bar->WantLayout);         // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem() | ||||||
|  |         ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); | ||||||
|  |         TabBarRemoveTab(tab_bar, tab_id); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImVec2 label_size = CalcTextSize(label, NULL, true); | ||||||
|  |     ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); | ||||||
|  |     if (has_close_button) | ||||||
|  |         size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. | ||||||
|  |     else | ||||||
|  |         size.x += g.Style.FramePadding.x + 1.0f; | ||||||
|  |     return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) | ||||||
|  | { | ||||||
|  |     // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. | ||||||
|  |     (void)flags; | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     const float width = bb.GetWidth(); | ||||||
|  |     IM_ASSERT(width > 0.0f); | ||||||
|  |     const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f)); | ||||||
|  |     float y1 = bb.Min.y + 1.0f; | ||||||
|  |     float y2 = bb.Max.y - 1.0f; | ||||||
|  |     draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); | ||||||
|  |     draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); | ||||||
|  |     draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); | ||||||
|  |     draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); | ||||||
|  |     draw_list->AddConvexPolyFilled(draw_list->_Path.Data, draw_list->_Path.Size, col); | ||||||
|  |     if (g.Style.TabBorderSize > 0.0f) | ||||||
|  |         draw_list->AddPolyline(draw_list->_Path.Data, draw_list->_Path.Size, GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize); | ||||||
|  |     draw_list->PathClear(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Render text label (with custom clipping) + Unsaved Document marker + Close Button logic | ||||||
|  | bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, const char* label, ImGuiID tab_id, ImGuiID close_button_id) | ||||||
|  | { | ||||||
|  |     ImGuiContext& g = *GImGui; | ||||||
|  |     ImGuiStyle& style = g.Style; | ||||||
|  |     ImVec2 label_size = CalcTextSize(label, NULL, true); | ||||||
|  |     if (bb.GetWidth() <= 1.0f) | ||||||
|  |         return false; | ||||||
|  |  | ||||||
|  |     // Render text label (with clipping + alpha gradient) + unsaved marker | ||||||
|  |     const char* TAB_UNSAVED_MARKER = "*"; | ||||||
|  |     ImRect text_pixel_clip_bb(bb.Min.x + style.FramePadding.x, bb.Min.y + style.FramePadding.y, bb.Max.x - style.FramePadding.x, bb.Max.y); | ||||||
|  |     if (flags & ImGuiTabItemFlags_UnsavedDocument) | ||||||
|  |     { | ||||||
|  |         text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; | ||||||
|  |         ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + style.FramePadding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + style.FramePadding.y + (float)(int)(-g.FontSize * 0.25f)); | ||||||
|  |         RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - style.FramePadding, TAB_UNSAVED_MARKER, NULL, NULL); | ||||||
|  |     } | ||||||
|  |     ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; | ||||||
|  |  | ||||||
|  |     // Close Button | ||||||
|  |     // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() | ||||||
|  |     //  'hovered' will be true when hovering the Tab but NOT when hovering the close button | ||||||
|  |     //  'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button | ||||||
|  |     //  'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false | ||||||
|  |     bool close_button_pressed = false; | ||||||
|  |     bool close_button_visible = false; | ||||||
|  |     if (close_button_id != 0) | ||||||
|  |         if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id) | ||||||
|  |             close_button_visible = true; | ||||||
|  |     if (close_button_visible) | ||||||
|  |     { | ||||||
|  |         ImGuiItemHoveredDataBackup last_item_backup; | ||||||
|  |         const float close_button_sz = g.FontSize * 0.5f; | ||||||
|  |         if (CloseButton(close_button_id, ImVec2(bb.Max.x - style.FramePadding.x - close_button_sz, bb.Min.y + style.FramePadding.y + close_button_sz), close_button_sz)) | ||||||
|  |             close_button_pressed = true; | ||||||
|  |         last_item_backup.Restore(); | ||||||
|  |  | ||||||
|  |         // Close with middle mouse button | ||||||
|  |         if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) | ||||||
|  |             close_button_pressed = true; | ||||||
|  |  | ||||||
|  |         text_pixel_clip_bb.Max.x -= close_button_sz * 2.0f; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Label with ellipsis | ||||||
|  |     // FIXME: This should be extracted into a helper but the use of text_pixel_clip_bb and !close_button_visible makes it tricky to abstract at the moment | ||||||
|  |     const char* label_display_end = FindRenderedTextEnd(label); | ||||||
|  |     if (label_size.x > text_ellipsis_clip_bb.GetWidth()) | ||||||
|  |     { | ||||||
|  |         const int ellipsis_dot_count = 3; | ||||||
|  |         const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f; | ||||||
|  |         const char* label_end = NULL; | ||||||
|  |         float label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, text_ellipsis_clip_bb.GetWidth() - ellipsis_width + 1.0f, 0.0f, label, label_display_end, &label_end).x; | ||||||
|  |         if (label_end == label && label_end < label_display_end)    // Always display at least 1 character if there's no room for character + ellipsis | ||||||
|  |         { | ||||||
|  |             label_end = label + ImTextCountUtf8BytesFromChar(label, label_display_end); | ||||||
|  |             label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label, label_end).x; | ||||||
|  |         } | ||||||
|  |         while (label_end > label && ImCharIsBlankA(label_end[-1])) // Trim trailing space | ||||||
|  |         { | ||||||
|  |             label_end--; | ||||||
|  |             label_size_clipped_x -= g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label_end, label_end + 1).x; // Ascii blanks are always 1 byte | ||||||
|  |         } | ||||||
|  |         RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_end, &label_size, ImVec2(0.0f, 0.0f)); | ||||||
|  |  | ||||||
|  |         const float ellipsis_x = text_pixel_clip_bb.Min.x + label_size_clipped_x + 1.0f; | ||||||
|  |         if (!close_button_visible && ellipsis_x + ellipsis_width <= bb.Max.x) | ||||||
|  |             RenderPixelEllipsis(draw_list, g.Font, ImVec2(ellipsis_x, text_pixel_clip_bb.Min.y), ellipsis_dot_count, GetColorU32(ImGuiCol_Text)); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_display_end, &label_size, ImVec2(0.0f, 0.0f)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return close_button_pressed; | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 omar
					omar