-
-
Save Flix01/3bc3d7b3d996582e034e to your computer and use it in GitHub Desktop.
#pragma once | |
#include <imgui.h> | |
// USAGE EXAMPLE | |
/* | |
ImGui::Text("Tabs (based on the code by krys-spectralpixel):"); | |
static const char* tabNames[] = {"Render","Layers","Scene","World","Object","Constraints","Modifiers","Data","Material","Texture","Particle","Physics"}; | |
static const int numTabs = sizeof(tabNames)/sizeof(tabNames[0]); | |
static const char* tabTooltips[numTabs] = {"Render Tab Tooltip","","","","Object Type Tooltip","","","","","Tired to add tooltips...",""}; | |
static int tabItemOrdering[numTabs] = {0,1,2,3,4,5,6,7,8,9,10,11}; | |
static int selectedTab = 0; | |
static int optionalHoveredTab = 0; | |
/*const bool tabSelectedChanged =*/ ImGui::TabLabels(numTabs,tabNames,selectedTab,tabTooltips,true,&optionalHoveredTab,&tabItemOrdering[0],true,true); | |
ImGui::Text("\nTab Page For Tab: \"%s\" here.\n",tabNames[selectedTab]); | |
if (optionalHoveredTab>=0) ImGui::Text("Mouse is hovering Tab Label: \"%s\".\n\n",tabNames[optionalHoveredTab]); | |
*/ | |
namespace ImGui { | |
// Based on the code by krys-spectralpixel (https://github.com/krys-spectralpixel), posted here: https://github.com/ocornut/imgui/issues/261 | |
/* pOptionalHoveredIndex: a ptr to an optional int that is set to -1 if no tab label is hovered by the mouse. | |
* pOptionalItemOrdering: an optional static array of unique integers from 0 to numTabs-1 that maps the tab label order. If one of the numbers is replaced by -1 the tab label is not visible (closed). It can be read/modified at runtime. | |
* allowTabReorder (requires pOptionalItemOrdering): allows tab reordering through drag and drop (it modifies pOptionalItemOrdering). | |
* allowTabClosingThroughMMB (requires pOptionalItemOrdering): closes the tabs when MMB is clicked on them, by setting the tab value in pOptionalItemOrdering to -1. | |
* pOptionalClosedTabIndex (requires allowTabClosingThroughMMB): out variable (int pointer) that returns the index of the closed tab in last call or -1. | |
* pOptionalClosedTabIndexInsideItemOrdering: same as above, but index of the pOptionalItemOrdering array. | |
*/ | |
IMGUI_API bool TabLabels(int numTabs, const char** tabLabels, int& selectedIndex, const char** tabLabelTooltips=NULL, bool wrapMode=true, int *pOptionalHoveredIndex=NULL, int* pOptionalItemOrdering=NULL, bool allowTabReorder=true, bool allowTabClosingThroughMMB=true, int *pOptionalClosedTabIndex=NULL, int *pOptionalClosedTabIndexInsideItemOrdering=NULL) { | |
ImGuiStyle& style = ImGui::GetStyle(); | |
const ImVec2 itemSpacing = style.ItemSpacing; | |
const ImVec4 color = style.Colors[ImGuiCol_Button]; | |
const ImVec4 colorActive = style.Colors[ImGuiCol_ButtonActive]; | |
const ImVec4 colorHover = style.Colors[ImGuiCol_ButtonHovered]; | |
const ImVec4 colorText = style.Colors[ImGuiCol_Text]; | |
style.ItemSpacing.x = 1; | |
style.ItemSpacing.y = 1; | |
const ImVec4 colorSelectedTab(color.x,color.y,color.z,color.w*0.5f); | |
const ImVec4 colorSelectedTabHovered(colorHover.x,colorHover.y,colorHover.z,colorHover.w*0.5f); | |
const ImVec4 colorSelectedTabText(colorText.x*0.8f,colorText.y*0.8f,colorText.z*0.6f,colorText.w*0.8f); | |
//ImGui::ClampColor(colorSelectedTabText); | |
if (numTabs>0 && (selectedIndex<0 || selectedIndex>=numTabs)) { | |
if (!pOptionalItemOrdering) selectedIndex = 0; | |
else selectedIndex = -1; | |
} | |
if (pOptionalHoveredIndex) *pOptionalHoveredIndex = -1; | |
if (pOptionalClosedTabIndex) *pOptionalClosedTabIndex = -1; | |
if (pOptionalClosedTabIndexInsideItemOrdering) *pOptionalClosedTabIndexInsideItemOrdering = -1; | |
float windowWidth = 0.f,sumX=0.f; | |
if (wrapMode) windowWidth = ImGui::GetWindowWidth() - style.WindowPadding.x - (ImGui::GetScrollMaxY()>0 ? style.ScrollbarSize : 0.f); | |
static int draggingTabIndex = -1;int draggingTabTargetIndex = -1; // These are indices inside pOptionalItemOrdering | |
static ImVec2 draggingTabSize(0,0); | |
static ImVec2 draggingTabOffset(0,0); | |
const bool isMMBreleased = ImGui::IsMouseReleased(2); | |
const bool isMouseDragging = ImGui::IsMouseDragging(0,2.f); | |
int justClosedTabIndex = -1,newSelectedIndex = selectedIndex; | |
bool selection_changed = false;bool noButtonDrawn = true; | |
for (int j = 0,i; j < numTabs; j++) | |
{ | |
i = pOptionalItemOrdering ? pOptionalItemOrdering[j] : j; | |
if (i==-1) continue; | |
if (!wrapMode) {if (!noButtonDrawn) ImGui::SameLine();} | |
else if (sumX > 0.f) { | |
sumX+=style.ItemSpacing.x; // Maybe we can skip it if we use SameLine(0,0) below | |
sumX+=ImGui::CalcTextSize(tabLabels[i]).x+2.f*style.FramePadding.x; | |
if (sumX>windowWidth) sumX = 0.f; | |
else ImGui::SameLine(); | |
} | |
if (i == selectedIndex) { | |
// Push the style | |
style.Colors[ImGuiCol_Button] = colorSelectedTab; | |
style.Colors[ImGuiCol_ButtonActive] = colorSelectedTab; | |
style.Colors[ImGuiCol_ButtonHovered] = colorSelectedTabHovered; | |
style.Colors[ImGuiCol_Text] = colorSelectedTabText; | |
} | |
// Draw the button | |
ImGui::PushID(i); // otherwise two tabs with the same name would clash. | |
if (ImGui::Button(tabLabels[i])) {selection_changed = (selectedIndex!=i);newSelectedIndex = i;} | |
ImGui::PopID(); | |
if (i == selectedIndex) { | |
// Reset the style | |
style.Colors[ImGuiCol_Button] = color; | |
style.Colors[ImGuiCol_ButtonActive] = colorActive; | |
style.Colors[ImGuiCol_ButtonHovered] = colorHover; | |
style.Colors[ImGuiCol_Text] = colorText; | |
} | |
noButtonDrawn = false; | |
if (wrapMode) { | |
if (sumX==0.f) sumX = style.WindowPadding.x + ImGui::GetItemRectSize().x; // First element of a line | |
} | |
else if (isMouseDragging && allowTabReorder && pOptionalItemOrdering) { | |
// We still need sumX | |
if (sumX==0.f) sumX = style.WindowPadding.x + ImGui::GetItemRectSize().x; // First element of a line | |
else sumX+=style.ItemSpacing.x + ImGui::GetItemRectSize().x; | |
} | |
if (ImGui::IsItemHoveredRect()) { | |
if (pOptionalHoveredIndex) *pOptionalHoveredIndex = i; | |
if (tabLabelTooltips && tabLabelTooltips[i] && strlen(tabLabelTooltips[i])>0) ImGui::SetTooltip("%s",tabLabelTooltips[i]); | |
if (pOptionalItemOrdering) { | |
if (allowTabReorder) { | |
if (isMouseDragging) { | |
if (draggingTabIndex==-1) { | |
draggingTabIndex = j; | |
draggingTabSize = ImGui::GetItemRectSize(); | |
const ImVec2& mp = ImGui::GetIO().MousePos; | |
const ImVec2 draggingTabCursorPos = ImGui::GetCursorPos(); | |
draggingTabOffset=ImVec2( | |
mp.x+draggingTabSize.x*0.5f-sumX+ImGui::GetScrollX(), | |
mp.y+draggingTabSize.y*0.5f-draggingTabCursorPos.y+ImGui::GetScrollY() | |
); | |
} | |
} | |
else if (draggingTabIndex>=0 && draggingTabIndex<numTabs && draggingTabIndex!=j){ | |
draggingTabTargetIndex = j; // For some odd reasons this seems to get called only when draggingTabIndex < i ! (Probably during mouse dragging ImGui owns the mouse someway and sometimes ImGui::IsItemHovered() is not getting called) | |
} | |
} | |
if (allowTabClosingThroughMMB) { | |
if (isMMBreleased) { | |
justClosedTabIndex = i; | |
if (pOptionalClosedTabIndex) *pOptionalClosedTabIndex = i; | |
if (pOptionalClosedTabIndexInsideItemOrdering) *pOptionalClosedTabIndexInsideItemOrdering = j; | |
pOptionalItemOrdering[j] = -1; | |
} | |
} | |
} | |
} | |
} | |
selectedIndex = newSelectedIndex; | |
// Draw tab label while mouse drags it | |
if (draggingTabIndex>=0 && draggingTabIndex<numTabs) { | |
const ImVec2& mp = ImGui::GetIO().MousePos; | |
const ImVec2 wp = ImGui::GetWindowPos(); | |
ImVec2 start(wp.x+mp.x-draggingTabOffset.x-draggingTabSize.x*0.5f,wp.y+mp.y-draggingTabOffset.y-draggingTabSize.y*0.5f); | |
const ImVec2 end(start.x+draggingTabSize.x,start.y+draggingTabSize.y); | |
ImDrawList* drawList = ImGui::GetWindowDrawList(); | |
const float draggedBtnAlpha = 0.65f; | |
const ImVec4& btnColor = style.Colors[ImGuiCol_Button]; | |
drawList->AddRectFilled(start,end,ImColor(btnColor.x,btnColor.y,btnColor.z,btnColor.w*draggedBtnAlpha),style.FrameRounding); | |
start.x+=style.FramePadding.x;start.y+=style.FramePadding.y; | |
const ImVec4& txtColor = style.Colors[ImGuiCol_Text]; | |
drawList->AddText(start,ImColor(txtColor.x,txtColor.y,txtColor.z,txtColor.w*draggedBtnAlpha),tabLabels[pOptionalItemOrdering[draggingTabIndex]]); | |
ImGui::SetMouseCursor(ImGuiMouseCursor_Move); | |
} | |
// Drop tab label | |
if (draggingTabTargetIndex!=-1) { | |
// swap draggingTabIndex and draggingTabTargetIndex in pOptionalItemOrdering | |
const int tmp = pOptionalItemOrdering[draggingTabTargetIndex]; | |
pOptionalItemOrdering[draggingTabTargetIndex] = pOptionalItemOrdering[draggingTabIndex]; | |
pOptionalItemOrdering[draggingTabIndex] = tmp; | |
//fprintf(stderr,"%d %d\n",draggingTabIndex,draggingTabTargetIndex); | |
draggingTabTargetIndex = draggingTabIndex = -1; | |
} | |
// Reset draggingTabIndex if necessary | |
if (!isMouseDragging) draggingTabIndex = -1; | |
// Change selected tab when user closes the selected tab | |
if (selectedIndex == justClosedTabIndex && selectedIndex>=0) { | |
selectedIndex = -1; | |
for (int j = 0,i; j < numTabs; j++) { | |
i = pOptionalItemOrdering ? pOptionalItemOrdering[j] : j; | |
if (i==-1) continue; | |
selectedIndex = i; | |
break; | |
} | |
} | |
// Restore the style | |
style.Colors[ImGuiCol_Button] = color; | |
style.Colors[ImGuiCol_ButtonActive] = colorActive; | |
style.Colors[ImGuiCol_ButtonHovered] = colorHover; | |
style.Colors[ImGuiCol_Text] = colorText; | |
style.ItemSpacing = itemSpacing; | |
return selection_changed; | |
} | |
} // namespace ImGui |
UPDATE:
- Fixed all the width-related variables (it should work well both with and without a vertical scroll bar).
- Added last argument: it could be used to prevent a tab label from closing inside user-code.
- Added "dragging visuals":
KNOWN BUGS:
- Tab reordering through drag and drop seems to work only when dragging tabs from the left (top) to the right (bottom) and not vice versa (this is bad, even if it can't lock the tab order in any way). It seems like ImGui "owns" the mouse in some way while dragging and ImGui::IsItemHovered() sometimes does not work when the mouse is being dragged.
- Sometimes middle-mouse-clicking for closing the tabs seems a bit buggy... at least on my imgui "back-end".
UPDATE:
- Fixed first bug. Now drag and drop works in both directions (and it now uses the "move cursor" image).
- Fixed second bug (actually I did nothing). It must be something related to my glfw3 mouse button handling code, because when I use SDL2 it works as expected. (The live demo at the bottom uses SDL2 too and seems to work perfectly).
- Changed colors a bit:
LIVE DEMO:
Added it to my ImGui Addons Branch first live demo here:
Demo1
UPDATE: I've moved this gist and merged with this: https://gist.github.com/Flix01/2cdf1db8d936100628c0 .
This way I can have proper tab labels:
Hi
I tried copying the two files into my im_gui folder but I was getting compiliation errors. How simple it to merge this into code? I run Urho3D with imgui setup as a thirdparty add on.
Vivienne
I tried copying the two files into my im_gui folder but I was getting compiliation errors. How simple it to merge this into code? I run Urho3D with imgui setup as a thirdparty add on.
Are you referring to the two files in the other link ? Does the code in this gist work or not ? What compilation error ? What system/OS ?
It's an extension of the code I posted here: ocornut/imgui#261

The implementation is very basic/compact (no dragging visuals, no close buttons).
KNOWN BUG: