Skip to content

Commit 54a60aa

Browse files
committed
Added BETA api for Tab Bar/Tabs widgets. (ocornut#261, ocornut#351) (merged this feature from the from Docking branch so it can be used earlier as as standalone feature)
- Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. - Added ImGuiTabBarFlags flags for BeginTabBar(). - Added ImGuiTabItemFlags flags for BeginTabItem(). - Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. - Demo: Added Layout->Tabs demo code. - Demo: Added "Documents" example app showcasing possible use for tabs.
1 parent cc1283f commit 54a60aa

8 files changed

+1345
-14
lines changed

docs/CHANGELOG.txt

+9
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ Breaking Changes:
3939
The addition of new configuration options in the Docking branch is pushing for a little reorganization of those names.
4040

4141
Other Changes:
42+
- Added BETA api for Tab Bar/Tabs widgets: (#261, #351)
43+
- Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API.
44+
- Added ImGuiTabBarFlags flags for BeginTabBar().
45+
- Added ImGuiTabItemFlags flags for BeginTabItem().
46+
- Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors.
47+
- Demo: Added Layout->Tabs demo code.
48+
- Demo: Added "Documents" example app showcasing possible use for tabs.
49+
This feature was merged from the Docking branch in order to allow the use of regular tabs in your code.
50+
(It does not provide the docking/splitting/merging of windows available in the Docking branch)
4251
- Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering
4352
the ID, as a convenience to avoid using the ### operator.
4453
- Resizing windows from edge is now enabled by default (io.ConfigWindowsResizeFromEdges=true). Note that

docs/TODO.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
127127
- dock: docking extension
128128
- dock: dock out from a collapsing header? would work nicely but need emitting window to keep submitting the code.
129129

130-
- tabs: re-ordering, close buttons, context menu, persistent order (#261, #351)
130+
- tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing.
131+
- tabs: persistent order/focus in BeginTabBar() api (#261, #351)
131132

132133
- ext: stl-ish friendly extension (imgui_stl.h) that has wrapper for std::string, std::vector etc.
133134

imgui.cpp

+55-13
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,8 @@ ImGuiStyle::ImGuiStyle()
10151015
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
10161016
GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
10171017
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1018+
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1019+
TabBorderSize = 0.0f; // Thickness of border around tabs.
10181020
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
10191021
DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
10201022
DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
@@ -1038,6 +1040,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor)
10381040
PopupRounding = ImFloor(PopupRounding * scale_factor);
10391041
FramePadding = ImFloor(FramePadding * scale_factor);
10401042
FrameRounding = ImFloor(FrameRounding * scale_factor);
1043+
TabRounding = ImFloor(TabRounding * scale_factor);
10411044
ItemSpacing = ImFloor(ItemSpacing * scale_factor);
10421045
ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
10431046
TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
@@ -2160,17 +2163,8 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end
21602163

21612164
// Default clip_rect uses (pos_min,pos_max)
21622165
// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
2163-
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)
2166+
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)
21642167
{
2165-
// Hide anything after a '##' string
2166-
const char* text_display_end = FindRenderedTextEnd(text, text_end);
2167-
const int text_len = (int)(text_display_end - text);
2168-
if (text_len == 0)
2169-
return;
2170-
2171-
ImGuiContext& g = *GImGui;
2172-
ImGuiWindow* window = g.CurrentWindow;
2173-
21742168
// Perform CPU side clipping for single clipped element to avoid using scissor state
21752169
ImVec2 pos = pos_min;
21762170
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
@@ -2189,14 +2183,27 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons
21892183
if (need_clipping)
21902184
{
21912185
ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2192-
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2186+
draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
21932187
}
21942188
else
21952189
{
2196-
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2190+
draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
21972191
}
2192+
}
2193+
2194+
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)
2195+
{
2196+
// Hide anything after a '##' string
2197+
const char* text_display_end = FindRenderedTextEnd(text, text_end);
2198+
const int text_len = (int)(text_display_end - text);
2199+
if (text_len == 0)
2200+
return;
2201+
2202+
ImGuiContext& g = *GImGui;
2203+
ImGuiWindow* window = g.CurrentWindow;
2204+
RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
21982205
if (g.LogEnabled)
2199-
LogRenderedText(&pos, text, text_display_end);
2206+
LogRenderedText(&pos_min, text, text_display_end);
22002207
}
22012208

22022209
// Render a rectangle shaped with optional rounding and borders
@@ -5511,6 +5518,7 @@ static const ImGuiStyleVarInfo GStyleVarInfo[] =
55115518
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
55125519
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
55135520
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
5521+
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
55145522
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
55155523
};
55165524

@@ -5603,6 +5611,11 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
56035611
case ImGuiCol_ResizeGrip: return "ResizeGrip";
56045612
case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
56055613
case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5614+
case ImGuiCol_Tab: return "Tab";
5615+
case ImGuiCol_TabHovered: return "TabHovered";
5616+
case ImGuiCol_TabActive: return "TabActive";
5617+
case ImGuiCol_TabUnfocused: return "TabUnfocused";
5618+
case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
56065619
case ImGuiCol_PlotLines: return "PlotLines";
56075620
case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
56085621
case ImGuiCol_PlotHistogram: return "PlotHistogram";
@@ -9056,6 +9069,29 @@ void ImGui::ShowMetricsWindow(bool* p_open)
90569069
ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
90579070
ImGui::TreePop();
90589071
}
9072+
9073+
static void NodeTabBar(ImGuiTabBar* tab_bar)
9074+
{
9075+
// Standalone tab bars (not associated to docking/windows functionality) currently hold no discernable strings.
9076+
char buf[256];
9077+
char* p = buf;
9078+
const char* buf_end = buf + IM_ARRAYSIZE(buf);
9079+
p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s",
9080+
tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9081+
if (ImGui::TreeNode(tab_bar, "%s", buf))
9082+
{
9083+
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9084+
{
9085+
const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9086+
ImGui::PushID(tab);
9087+
if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9088+
if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9089+
ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID);
9090+
ImGui::PopID();
9091+
}
9092+
ImGui::TreePop();
9093+
}
9094+
}
90599095
};
90609096

90619097
// Access private state, we are going to display the draw lists from last frame
@@ -9076,6 +9112,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
90769112
}
90779113
ImGui::TreePop();
90789114
}
9115+
if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
9116+
{
9117+
for (int n = 0; n < g.TabBars.Data.Size; n++)
9118+
Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9119+
ImGui::TreePop();
9120+
}
90799121
if (ImGui::TreeNode("Internal state"))
90809122
{
90819123
const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);

imgui.h

+43
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: f
135135
typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc.
136136
typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText*()
137137
typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
138+
typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar()
139+
typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem()
138140
typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode*(),CollapsingHeader()
139141
typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin*()
140142
typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data);
@@ -528,6 +530,14 @@ namespace ImGui
528530
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
529531
IMGUI_API int GetColumnsCount();
530532

533+
// Tab Bars, Tabs
534+
// [BETA API] API may evolve!
535+
IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar
536+
IMGUI_API void EndTabBar();
537+
IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0);// create a Tab. Returns true if the Tab is selected.
538+
IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true!
539+
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.
540+
531541
// Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging.
532542
IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty
533543
IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file
@@ -760,6 +770,31 @@ enum ImGuiComboFlags_
760770
ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest
761771
};
762772

773+
// Flags for ImGui::BeginTabBar()
774+
enum ImGuiTabBarFlags_
775+
{
776+
ImGuiTabBarFlags_None = 0,
777+
ImGuiTabBarFlags_Reorderable = 1 << 0, // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list
778+
ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, // Automatically select new tabs when they appear
779+
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.
780+
ImGuiTabBarFlags_NoTabListPopupButton = 1 << 3,
781+
ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4,
782+
ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 5, // Resize tabs when they don't fit
783+
ImGuiTabBarFlags_FittingPolicyScroll = 1 << 6, // Add scroll buttons when tabs don't fit
784+
ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll,
785+
ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown
786+
};
787+
788+
// Flags for ImGui::BeginTabItem()
789+
enum ImGuiTabItemFlags_
790+
{
791+
ImGuiTabItemFlags_None = 0,
792+
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.
793+
ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programatically make the tab selected when calling BeginTabItem()
794+
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.
795+
ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()
796+
};
797+
763798
// Flags for ImGui::IsWindowFocused()
764799
enum ImGuiFocusedFlags_
765800
{
@@ -956,6 +991,11 @@ enum ImGuiCol_
956991
ImGuiCol_ResizeGrip,
957992
ImGuiCol_ResizeGripHovered,
958993
ImGuiCol_ResizeGripActive,
994+
ImGuiCol_Tab,
995+
ImGuiCol_TabHovered,
996+
ImGuiCol_TabActive,
997+
ImGuiCol_TabUnfocused,
998+
ImGuiCol_TabUnfocusedActive,
959999
ImGuiCol_PlotLines,
9601000
ImGuiCol_PlotLinesHovered,
9611001
ImGuiCol_PlotHistogram,
@@ -1003,6 +1043,7 @@ enum ImGuiStyleVar_
10031043
ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding
10041044
ImGuiStyleVar_GrabMinSize, // float GrabMinSize
10051045
ImGuiStyleVar_GrabRounding, // float GrabRounding
1046+
ImGuiStyleVar_TabRounding, // float TabRounding
10061047
ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign
10071048
ImGuiStyleVar_COUNT
10081049

@@ -1114,6 +1155,8 @@ struct ImGuiStyle
11141155
float ScrollbarRounding; // Radius of grab corners for scrollbar.
11151156
float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar.
11161157
float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1158+
float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1159+
float TabBorderSize; // Thickness of border around tabs.
11171160
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered.
11181161
ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
11191162
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!

0 commit comments

Comments
 (0)