mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-11-04 15:11:05 +01:00 
			
		
		
		
	Merge branch 'master' into viewport
# Conflicts: # imgui.cpp # imgui_internal.h
This commit is contained in:
		@@ -39,6 +39,17 @@ Breaking Changes:
 | 
			
		||||
  The addition of new configuration options in the Docking branch is pushing for a little reorganization of those names.
 | 
			
		||||
 | 
			
		||||
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 
 | 
			
		||||
  the ID, as a convenience to avoid using the ### operator.
 | 
			
		||||
- Resizing windows from edge is now enabled by default (io.ConfigWindowsResizeFromEdges=true). Note that 
 | 
			
		||||
  it only works _if_ the back-end sets ImGuiBackendFlags_HasMouseCursors, which the standard back-end do.
 | 
			
		||||
- Added io.ConfigWindowsMoveFromTitleBarOnly option. Still is ignored by window with no title bars (often popups).
 | 
			
		||||
 
 | 
			
		||||
@@ -127,7 +127,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
 | 
			
		||||
 - dock: docking extension
 | 
			
		||||
 - 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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										101
									
								
								imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								imgui.cpp
									
									
									
									
									
								
							@@ -1036,6 +1036,8 @@ ImGuiStyle::ImGuiStyle()
 | 
			
		||||
    ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for 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.
 | 
			
		||||
    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.
 | 
			
		||||
    DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors 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.
 | 
			
		||||
@@ -1059,6 +1061,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor)
 | 
			
		||||
    PopupRounding = ImFloor(PopupRounding * scale_factor);
 | 
			
		||||
    FramePadding = ImFloor(FramePadding * scale_factor);
 | 
			
		||||
    FrameRounding = ImFloor(FrameRounding * scale_factor);
 | 
			
		||||
    TabRounding = ImFloor(TabRounding * scale_factor);
 | 
			
		||||
    ItemSpacing = ImFloor(ItemSpacing * scale_factor);
 | 
			
		||||
    ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
 | 
			
		||||
    TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
 | 
			
		||||
@@ -2178,17 +2181,8 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
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
 | 
			
		||||
    ImVec2 pos = pos_min;
 | 
			
		||||
    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
 | 
			
		||||
@@ -2207,14 +2201,27 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons
 | 
			
		||||
    if (need_clipping)
 | 
			
		||||
    {
 | 
			
		||||
        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
 | 
			
		||||
    {
 | 
			
		||||
        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)
 | 
			
		||||
        LogRenderedText(&pos, text, text_display_end);
 | 
			
		||||
        LogRenderedText(&pos_min, text, text_display_end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Render a rectangle shaped with optional rounding and borders
 | 
			
		||||
@@ -2346,7 +2353,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
 | 
			
		||||
    Name = ImStrdup(name);
 | 
			
		||||
    ID = ImHash(name, 0);
 | 
			
		||||
    IDStack.push_back(ID);
 | 
			
		||||
    Flags = FlagsPreviousFrame = 0;
 | 
			
		||||
    Flags = FlagsPreviousFrame = ImGuiWindowFlags_None;
 | 
			
		||||
    Viewport = NULL;
 | 
			
		||||
    ViewportId = 0;
 | 
			
		||||
    ViewportAllowPlatformMonitorExtend = -1;
 | 
			
		||||
@@ -2648,7 +2655,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
 | 
			
		||||
 | 
			
		||||
    window->DC.LastItemId = id;
 | 
			
		||||
    window->DC.LastItemRect = bb;
 | 
			
		||||
    window->DC.LastItemStatusFlags = 0;
 | 
			
		||||
    window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
 | 
			
		||||
 | 
			
		||||
#ifdef IMGUI_ENABLE_TEST_ENGINE
 | 
			
		||||
    ImGuiTestEngineHook_ItemAdd(id, bb);
 | 
			
		||||
@@ -4224,7 +4231,8 @@ bool ImGui::IsItemDeactivatedAfterEdit()
 | 
			
		||||
bool ImGui::IsItemFocused()
 | 
			
		||||
{
 | 
			
		||||
    ImGuiContext& g = *GImGui;
 | 
			
		||||
    return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
 | 
			
		||||
    ImGuiWindow* window = g.CurrentWindow;
 | 
			
		||||
    return g.NavId && !g.NavDisableHighlight && g.NavId == window->DC.LastItemId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ImGui::IsItemClicked(int mouse_button)
 | 
			
		||||
@@ -5407,9 +5415,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
 | 
			
		||||
            window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
 | 
			
		||||
            window->DC.ItemFlags = item_flags_backup;
 | 
			
		||||
 | 
			
		||||
            // Title bar text (with: horizontal alignment, avoiding collapse/close button)
 | 
			
		||||
            // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
 | 
			
		||||
            // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code..
 | 
			
		||||
            ImVec2 text_size = CalcTextSize(name, NULL, true);
 | 
			
		||||
            const char* UNSAVED_DOCUMENT_MARKER = "*";
 | 
			
		||||
            float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
 | 
			
		||||
            ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
 | 
			
		||||
            ImRect text_r = title_bar_rect;
 | 
			
		||||
            float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
 | 
			
		||||
            float pad_right = (p_open == NULL)                     ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
 | 
			
		||||
@@ -5420,6 +5430,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
 | 
			
		||||
            ImRect clip_rect = text_r;
 | 
			
		||||
            clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton()
 | 
			
		||||
            RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
 | 
			
		||||
            if (flags & ImGuiWindowFlags_UnsavedDocument)
 | 
			
		||||
            {
 | 
			
		||||
                ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
 | 
			
		||||
                ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f));
 | 
			
		||||
                RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
 | 
			
		||||
@@ -5825,6 +5841,7 @@ static const ImGuiStyleVarInfo GStyleVarInfo[] =
 | 
			
		||||
    { 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, GrabRounding) },       // ImGuiStyleVar_GrabRounding
 | 
			
		||||
    { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) },        // ImGuiStyleVar_TabRounding
 | 
			
		||||
    { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) },    // ImGuiStyleVar_ButtonTextAlign
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -5917,6 +5934,11 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
 | 
			
		||||
    case ImGuiCol_ResizeGrip: return "ResizeGrip";
 | 
			
		||||
    case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
 | 
			
		||||
    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_PlotLinesHovered: return "PlotLinesHovered";
 | 
			
		||||
    case ImGuiCol_PlotHistogram: return "PlotHistogram";
 | 
			
		||||
@@ -8421,7 +8443,7 @@ static void ImGui::NavUpdate()
 | 
			
		||||
    if (g.NavMoveRequestForward == ImGuiNavForward_None)
 | 
			
		||||
    {
 | 
			
		||||
        g.NavMoveDir = ImGuiDir_None;
 | 
			
		||||
        g.NavMoveRequestFlags = 0;
 | 
			
		||||
        g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
 | 
			
		||||
        if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
 | 
			
		||||
        {
 | 
			
		||||
            if ((allowed_dir_flags & (1<<ImGuiDir_Left))  && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
 | 
			
		||||
@@ -8779,7 +8801,9 @@ static void ImGui::NavUpdateWindowing()
 | 
			
		||||
    {
 | 
			
		||||
        // Move to parent menu if necessary
 | 
			
		||||
        ImGuiWindow* new_nav_window = g.NavWindow;
 | 
			
		||||
        while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
 | 
			
		||||
        while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 
 | 
			
		||||
            && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
 | 
			
		||||
            && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
 | 
			
		||||
            new_nav_window = new_nav_window->ParentWindow;
 | 
			
		||||
        if (new_nav_window != g.NavWindow)
 | 
			
		||||
        {
 | 
			
		||||
@@ -9165,7 +9189,7 @@ void ImGui::ClearDragDrop()
 | 
			
		||||
    ImGuiContext& g = *GImGui;
 | 
			
		||||
    g.DragDropActive = false;
 | 
			
		||||
    g.DragDropPayload.Clear();
 | 
			
		||||
    g.DragDropAcceptFlags = 0;
 | 
			
		||||
    g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
 | 
			
		||||
    g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
 | 
			
		||||
    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
 | 
			
		||||
    g.DragDropAcceptFrameCount = -1;
 | 
			
		||||
@@ -9449,6 +9473,12 @@ void ImGui::EndDragDropTarget()
 | 
			
		||||
    g.DragDropWithinSourceOrTarget = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// [SECTION] DOCKING
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// (this section is filled in the 'docking' branch)
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// [SECTION] LOGGING/CAPTURING
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
@@ -10150,6 +10180,29 @@ void ImGui::ShowMetricsWindow(bool* p_open)
 | 
			
		||||
                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
 | 
			
		||||
@@ -10186,6 +10239,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
 | 
			
		||||
        }
 | 
			
		||||
        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"))
 | 
			
		||||
    {
 | 
			
		||||
        const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								imgui.h
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								imgui.h
									
									
									
									
									
								
							@@ -140,6 +140,8 @@ typedef int ImGuiFocusedFlags;      // -> enum ImGuiFocusedFlags_    // Flags: f
 | 
			
		||||
typedef int ImGuiHoveredFlags;      // -> enum ImGuiHoveredFlags_    // Flags: for IsItemHovered(), IsWindowHovered() etc.
 | 
			
		||||
typedef int ImGuiInputTextFlags;    // -> enum ImGuiInputTextFlags_  // Flags: for InputText*()
 | 
			
		||||
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 ImGuiViewportFlags;     // -> enum ImGuiViewportFlags_   // Flags: for ImGuiViewport
 | 
			
		||||
typedef int ImGuiWindowFlags;       // -> enum ImGuiWindowFlags_     // Flags: for Begin*()
 | 
			
		||||
@@ -538,6 +540,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 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.
 | 
			
		||||
    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
 | 
			
		||||
@@ -547,7 +557,7 @@ namespace ImGui
 | 
			
		||||
    IMGUI_API void          LogText(const char* fmt, ...) IM_FMTARGS(1);                        // pass text data straight to log (without being displayed)
 | 
			
		||||
 | 
			
		||||
    // Drag and Drop
 | 
			
		||||
    // [BETA API] Missing Demo code. API may evolve.
 | 
			
		||||
    // [BETA API] Missing Demo code. API may evolve!
 | 
			
		||||
    IMGUI_API bool          BeginDragDropSource(ImGuiDragDropFlags flags = 0);                                      // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource()
 | 
			
		||||
    IMGUI_API bool          SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui.
 | 
			
		||||
    IMGUI_API void          EndDragDropSource();                                                                    // only call EndDragDropSource() if BeginDragDropSource() returns true!
 | 
			
		||||
@@ -685,6 +695,7 @@ enum ImGuiWindowFlags_
 | 
			
		||||
    ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16,  // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient)
 | 
			
		||||
    ImGuiWindowFlags_NoNavInputs            = 1 << 18,  // No gamepad/keyboard navigation within the window
 | 
			
		||||
    ImGuiWindowFlags_NoNavFocus             = 1 << 19,  // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB)
 | 
			
		||||
    ImGuiWindowFlags_UnsavedDocument        = 1 << 20,  // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker.
 | 
			
		||||
    ImGuiWindowFlags_NoNav                  = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
 | 
			
		||||
    ImGuiWindowFlags_NoDecoration           = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,
 | 
			
		||||
    ImGuiWindowFlags_NoInputs               = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
 | 
			
		||||
@@ -779,6 +790,31 @@ enum ImGuiComboFlags_
 | 
			
		||||
    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()
 | 
			
		||||
enum ImGuiFocusedFlags_
 | 
			
		||||
{
 | 
			
		||||
@@ -989,6 +1025,11 @@ enum ImGuiCol_
 | 
			
		||||
    ImGuiCol_ResizeGrip,
 | 
			
		||||
    ImGuiCol_ResizeGripHovered,
 | 
			
		||||
    ImGuiCol_ResizeGripActive,
 | 
			
		||||
    ImGuiCol_Tab,
 | 
			
		||||
    ImGuiCol_TabHovered,
 | 
			
		||||
    ImGuiCol_TabActive,
 | 
			
		||||
    ImGuiCol_TabUnfocused,
 | 
			
		||||
    ImGuiCol_TabUnfocusedActive,
 | 
			
		||||
    ImGuiCol_PlotLines,
 | 
			
		||||
    ImGuiCol_PlotLinesHovered,
 | 
			
		||||
    ImGuiCol_PlotHistogram,
 | 
			
		||||
@@ -1036,6 +1077,7 @@ enum ImGuiStyleVar_
 | 
			
		||||
    ImGuiStyleVar_ScrollbarRounding,   // float     ScrollbarRounding
 | 
			
		||||
    ImGuiStyleVar_GrabMinSize,         // float     GrabMinSize
 | 
			
		||||
    ImGuiStyleVar_GrabRounding,        // float     GrabRounding
 | 
			
		||||
    ImGuiStyleVar_TabRounding,         // float     TabRounding
 | 
			
		||||
    ImGuiStyleVar_ButtonTextAlign,     // ImVec2    ButtonTextAlign
 | 
			
		||||
    ImGuiStyleVar_COUNT
 | 
			
		||||
 | 
			
		||||
@@ -1147,6 +1189,8 @@ struct ImGuiStyle
 | 
			
		||||
    float       ScrollbarRounding;          // Radius of grab corners for 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       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      DisplayWindowPadding;       // Window position are clamped to be visible within the display area or monitors 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!
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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: Manipulating Window Titles / ShowExampleAppWindowTitles()
 | 
			
		||||
// [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)
 | 
			
		||||
 | 
			
		||||
// Forward Declarations
 | 
			
		||||
static void ShowExampleAppDocuments(bool* p_open);
 | 
			
		||||
static void ShowExampleAppMainMenuBar();
 | 
			
		||||
static void ShowExampleAppConsole(bool* p_open);
 | 
			
		||||
static void ShowExampleAppLog(bool* p_open);
 | 
			
		||||
@@ -168,6 +170,7 @@ static void ShowDemoWindowMisc();
 | 
			
		||||
void ImGui::ShowDemoWindow(bool* p_open)
 | 
			
		||||
{
 | 
			
		||||
    // 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_console = 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_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_console)             ShowExampleAppConsole(&show_app_console);
 | 
			
		||||
    if (show_app_log)                 ShowExampleAppLog(&show_app_log);
 | 
			
		||||
@@ -264,6 +268,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
 | 
			
		||||
            ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay);
 | 
			
		||||
            ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles);
 | 
			
		||||
            ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering);
 | 
			
		||||
            ImGui::MenuItem("Documents", NULL, &show_app_documents);
 | 
			
		||||
            ImGui::EndMenu();
 | 
			
		||||
        }
 | 
			
		||||
        if (ImGui::BeginMenu("Help"))
 | 
			
		||||
@@ -1709,6 +1714,76 @@ static void ShowDemoWindowLayout()
 | 
			
		||||
        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"))
 | 
			
		||||
    {
 | 
			
		||||
        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.)");
 | 
			
		||||
@@ -2702,12 +2777,14 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
 | 
			
		||||
        ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 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("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
 | 
			
		||||
        ImGui::Text("Rounding");
 | 
			
		||||
        ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f");
 | 
			
		||||
        ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f");
 | 
			
		||||
        ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 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("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
 | 
			
		||||
        ImGui::Text("Alignment");
 | 
			
		||||
        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.");
 | 
			
		||||
@@ -3786,6 +3863,271 @@ static void ShowExampleAppCustomRendering(bool* p_open)
 | 
			
		||||
        if (adding_preview)
 | 
			
		||||
            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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -205,6 +205,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
 | 
			
		||||
    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_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_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 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_ResizeGripHovered]      = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
 | 
			
		||||
    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_PlotLinesHovered]       = 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_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
 | 
			
		||||
    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_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 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()
 | 
			
		||||
// - RenderArrowPointingAt()
 | 
			
		||||
// - RenderRectFilledRangeH()
 | 
			
		||||
// - RenderPixelEllipsis()
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void ImGui::RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor)
 | 
			
		||||
@@ -2940,6 +2956,15 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,8 @@ struct ImGuiNextWindowData;         // Storage for SetNexWindow** functions
 | 
			
		||||
struct ImGuiPopupRef;               // Storage for current popup stack
 | 
			
		||||
struct ImGuiSettingsHandler;        // Storage for one type registered in the .ini file
 | 
			
		||||
struct ImGuiStyleMod;               // Stacked style modifier, backup of modified data so we can restore it
 | 
			
		||||
struct ImGuiTabBar;                 // Storage for a tab bar
 | 
			
		||||
struct ImGuiTabItem;                // Storage for a tab item (within a tab bar)
 | 
			
		||||
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 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)
 | 
			
		||||
@@ -732,6 +734,12 @@ struct ImGuiNextWindowData
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ImGuiTabBarSortItem
 | 
			
		||||
{
 | 
			
		||||
    int         Index;
 | 
			
		||||
    float       Width;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Main imgui context
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
@@ -866,6 +874,11 @@ struct ImGuiContext
 | 
			
		||||
    ImVector<unsigned char> DragDropPayloadBufHeap;             // We don't expose the ImVector<> directly
 | 
			
		||||
    unsigned char           DragDropPayloadBufLocal[8];         // Local buffer for small payloads
 | 
			
		||||
 | 
			
		||||
    // Tab bars
 | 
			
		||||
    ImPool<ImGuiTabBar>     TabBars;
 | 
			
		||||
    ImVector<ImGuiTabBar*>  CurrentTabBar;
 | 
			
		||||
    ImVector<ImGuiTabBarSortItem>   TabSortByWidthBuffer;
 | 
			
		||||
 | 
			
		||||
    // Widget state
 | 
			
		||||
    ImGuiInputTextState     InputTextState;
 | 
			
		||||
    ImFont                  InputTextPasswordFont;
 | 
			
		||||
@@ -1219,6 +1232,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; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// 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
 | 
			
		||||
// No guarantee of forward compatibility here.
 | 
			
		||||
@@ -1342,12 +1408,24 @@ namespace ImGui
 | 
			
		||||
    IMGUI_API void          EndColumns();                                                             // close columns
 | 
			
		||||
    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
 | 
			
		||||
    // 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)
 | 
			
		||||
    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          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          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);
 | 
			
		||||
@@ -1362,6 +1440,7 @@ namespace ImGui
 | 
			
		||||
    // Render helpers (those functions don't access any ImGui state!)
 | 
			
		||||
    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          RenderPixelEllipsis(ImDrawList* draw_list, ImFont* font, ImVec2 pos, int count, ImU32 col);
 | 
			
		||||
 | 
			
		||||
    // Widgets
 | 
			
		||||
    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: Value helpers
 | 
			
		||||
// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.
 | 
			
		||||
// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
 | 
			
		||||
// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
@@ -5753,3 +5755,791 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected,
 | 
			
		||||
    }
 | 
			
		||||
    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