Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tabs #261

Closed
unpacklo opened this issue Jul 6, 2015 · 34 comments
Closed

Tabs #261

unpacklo opened this issue Jul 6, 2015 · 34 comments
Labels

Comments

@unpacklo
Copy link

unpacklo commented Jul 6, 2015

EDIT Jan. 22, 2016:
TL;DR; Here's the relevant pieces of code highlighted to make your own version:

https://gist.github.com/Roflraging/fe9a582cb7a79400b667#file-gistfile1-cpp-L1-L19
https://gist.github.com/Roflraging/fe9a582cb7a79400b667#file-gistfile1-cpp-L180-L213
https://gist.github.com/Roflraging/fe9a582cb7a79400b667#file-gistfile1-cpp-L243-L296

This is kind of a "here's what I'm trying out now, maybe others can try it" post, rather than a specific issue/feature request.

The quick screenshot comparisons. Up first, the original version I had before tabs:
screenshot-brigador 231ef9e6d7a1a482302d01298b9fc010b46a3d9c 13 45 10 jul 6 2015 release-assertions

Tabs version:
screenshot-brigador fb8e92748d471f1208235c814022bf0c976a09d4 13 59 50 jul 6 2015 release-assertions

Video comparisons:

Since I first started using ImGui, our usage of it in the game has exploded quite rapidly to a point where the GUI itself has become cumbersome. Ever since I had 3 different debugging windows in our game, I've wanted to have a tab functionality (like all modern browsers). I think I could have come up with a reasonable implementation for a long time now, but it wasn't until 1.40 with the menu stuff that it really popped into my head that I could do it reasonably.

Over the weekend I went to work at it, but funnily enough, I didn't really need much of the menu API to really be able to pull it off (Popups sure come in handy though!).

My approach is really quite simple and behaves nicely with most of the API, but columns remain an issue, which I'll detail a little bit later. Instead of trying to explain everything in English, I'll just drop in the relevant code blocks here. You probably won't be able to use it directly, but should show enough of the approach that people can try it out on their own.

Code overview: https://gist.github.com/Roflraging/fe9a582cb7a79400b667

The one issue I've encountered with this is columns from one tab can interfere with columns from another tab. For example, if I resize columns from Tab 1 and Tab 3 also has columns, it is possible for Tab 3 to have the same offsets as columns in Tab 1, even though I never touched Tab 3's columns. I tried to work around this at the top level of my code by having PanelTab contain memory to retain column offsets and simply setting the column offsets before calling into the handler and retrieving them afterward. But this doesn't quite work... mainly because right now you'll hit asserts in the column offset code. I might have gotten this wrong, but I couldn't work around it.

An improvement I could make on this is to allow for tabs to be closable or reordered, but I don't need that functionality right now so I didn't implement it. Reordering is very easy to do. Just swap array elements and you're done!

@unpacklo
Copy link
Author

unpacklo commented Jul 6, 2015

I've actually made a slight tweak which to the gist which creates a child region before calling into the gui handler. This allows the tab bar to always be visible when the tab contents are scrollable!

@ocornut
Copy link
Owner

ocornut commented Jul 6, 2015

Functionality wise if you use a set of RadioButton() widgets you are getting the same feature as basic tab (apart from re-ordering).

So proper tab would have to render differently than RadioButton() and allow for more natural ordering and horizontal scrolling. What else would be desirable?

Not sure why you have both the selectables and a popup menu here (I suppose the popup menu helps if you have too many tabs, for the lack of arrows to scroll horizontally).

For the column conflicting you can either name the columnset or just do a PushId()/PopId() encompassing the entire tab content.

What assert did you get in the column code? I recall there was an id bug which would led to asserts but it was fixed more than two months ago.

@unpacklo
Copy link
Author

unpacklo commented Jul 7, 2015

So proper tab would have to render differently than RadioButton() and allow for more natural ordering and horizontal scrolling. What else would be desirable?

That's all I can think of right now!

Not sure why you have both the selectables and a popup menu here (I suppose the popup menu helps if you have too many tabs, for the lack of arrows to scroll horizontally).

That is the only reason. It was the simplest solution I could come up with to be able to get to all the tabs without forcing you to resize the window to see all of them.

With respect to the column assert, I'd have to go back and try to recollect what I was doing exactly, but it was basically me doing a ImGui::GetColumnOffset() and ImGui::SetColumnOffset() just before and after the call to the gui handler, like this:

PanelTab *selected = tabs + selectedTab;
ImGui::BeginChild("tab main contents");

for (int i = 0; i < numCols; ++i)
{
    ImGui::SetColumnOffset(i, selected->colOffset[i]);
}

selected->guiHandler(selected->userdata);

for (int i = 0; i < numCols; ++i)
{
    selected->colOffset[i] = ImGui::GetColumnOffset(i);
}

ImGui::EndChild()

And the assertion would occur inside one of the get/set column offsets. Clearly this was just my fault, because there's no guarantee that any columns even exist when I call these functions at these locations.

This was a really poorly thought out attempt at solving this problem anyways... I just tried your suggestion with the pushing IDs and that totally solves it. The reason why it didn't work for me was because I was pushing the same string for all the tabs. I changed it so that when I create the child region, I send the tab name instead so each tab can have their own ID space. Columns don't interfere now!

@ocornut
Copy link
Owner

ocornut commented Jul 7, 2015

Good to hear. Let me know if you spot an assert next time, they shouldn't happen in that sort of use case.

Seems like you are losing lots of horizontal space with large widgets, I've seen this problem appears frequently, there's not a single answer unfortunately. Perhaps use more DragFloat, fit them two columns (save vertical space), include the label in the format string instead. Maybe you can allow to resize the game viewport to take say 75% of the space so that the rest is always free for ui, matching the typical Unity layout. If you have really large list e.g. for filename you can also consider using a smaller font such as ProggyTiny for those panels as well.

@ghost
Copy link

ghost commented Dec 3, 2015

Hi,

I have create a simple class that help to create a set of tabs. It can be easily converted into a ImGUI full version.

Just hope that this code can help someone ;-)

class ImGuiTab
{
public:
vector Labels;

void Draw(int* selected)
{
    ImGuiStyle& style = ImGui::GetStyle();
    ImVec4 color = style.Colors[ImGuiCol_Button];
    ImVec4 colorActive = style.Colors[ImGuiCol_ButtonActive];
    ImVec4 colorHover = style.Colors[ImGuiCol_ButtonHovered];

    for (int i = 0; i < Labels.size(); i++)
    {
        // push the style
        if (i == *selected)
        {
            style.Colors[ImGuiCol_Button] = colorActive;
            style.Colors[ImGuiCol_ButtonActive] = colorActive;
            style.Colors[ImGuiCol_ButtonHovered] = colorActive;
        }
        else
        {
            style.Colors[ImGuiCol_Button] = color;
            style.Colors[ImGuiCol_ButtonActive] = colorActive;
            style.Colors[ImGuiCol_ButtonHovered] = colorHover;
        }

        // Draw the button
        if (ImGui::Button(Labels[i].c_str()))
            *selected = i;

        if (i != Labels.size() - 1)
            ImGui::SameLine();
    }

    // Restore the style
    style.Colors[ImGuiCol_Button] = color;
}

};

Example of use:

    ImGuiTab tab;
    tab.Labels.push_back("Materials");
    tab.Labels.push_back("Colors");
    tab.Labels.push_back("Textures");
    tab.Labels.push_back("Environments");
    tab.Labels.push_back("Backgrounds");

    tab.Draw(&_selectedTab);

@ocornut ocornut mentioned this issue Dec 4, 2015
@ocornut
Copy link
Owner

ocornut commented Dec 4, 2015

New code from @krys-spectralpixel from #427

IMGUI_API bool Tab(unsigned int index, const char* label, const char* tooltip, int* selected)
{
    ImGuiStyle& style = ImGui::GetStyle();
    ImVec2 itemSpacing = style.ItemSpacing;
    ImVec4 color = style.Colors[ImGuiCol_Button];
    ImVec4 colorActive = style.Colors[ImGuiCol_ButtonActive];
    ImVec4 colorHover = style.Colors[ImGuiCol_ButtonHovered];
    style.ItemSpacing.x = 1;

    if (index > 0)
        ImGui::SameLine();

    // push the style
    if (index == *selected)
    {
        style.Colors[ImGuiCol_Button] = colorActive;
        style.Colors[ImGuiCol_ButtonActive] = colorActive;
        style.Colors[ImGuiCol_ButtonHovered] = colorActive;
    }
    else
    {
        style.Colors[ImGuiCol_Button] = color;
        style.Colors[ImGuiCol_ButtonActive] = colorActive;
        style.Colors[ImGuiCol_ButtonHovered] = colorHover;
    }

    // Draw the button
    if (ImGui::Button(label))
        *selected = index;

    if (ImGui::IsItemHovered())
    {
        ImGui::BeginTooltip();
        ImGui::Text("%s", tooltip);
        ImGui::EndTooltip();
    }

    // Restore the style
    style.Colors[ImGuiCol_Button] = color;
    style.Colors[ImGuiCol_ButtonActive] = colorActive;
    style.Colors[ImGuiCol_ButtonHovered] = colorHover;
    style.ItemSpacing = itemSpacing;

    return *selected == index;
}

@Flix01
Copy link

Flix01 commented Dec 5, 2015

@krys-spectralpixel : I have modified your code slightly, trying to include dynamic layout (useful in windows without horizontal scrollbars). It works, but the calculation of the widths should be improved.
Here is the code:

namespace ImGui {
IMGUI_API  bool TabLabels(int numTabs, const char** tabLabels, int& selectedIndex, const char** tabLabelTooltips, bool autoLayout, int *pOptionalHoveredIndex) {
    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];
    style.ItemSpacing.x =       1;
    style.ItemSpacing.y =       1;


    if (numTabs>0 && (selectedIndex<0 || selectedIndex>=numTabs)) selectedIndex = 0;
    if (pOptionalHoveredIndex) *pOptionalHoveredIndex = -1;

    // Parameters to adjust to make autolayout work as expected:----------
    // The correct values are probably the ones in the comments, but I took some margin so that they work well
    // with a (medium size) vertical scrollbar too [Ok I should detect its presence and use the appropriate values...].
    const float btnOffset =         2.f*style.FramePadding.x;   // [2.f*style.FramePadding.x] It should be: ImGui::Button(text).size.x = ImGui::CalcTextSize(text).x + btnOffset;
    const float sameLineOffset =    2.f*style.ItemSpacing.x;    // [style.ItemSpacing.x]      It should be: sameLineOffset = ImGui::SameLine().size.x;
    const float uniqueLineOffset =  2.f*style.WindowPadding.x;  // [style.WindowPadding.x]    Width to be sutracted by windowWidth to make it work.
    //--------------------------------------------------------------------

    float windowWidth = 0.f,sumX=0.f;
    if (autoLayout) windowWidth = ImGui::GetWindowWidth() - uniqueLineOffset;

    bool selection_changed = false;
    for (int i = 0; i < numTabs; i++)
    {
        // push the style
        if (i == selectedIndex)
        {
            style.Colors[ImGuiCol_Button] =         colorActive;
            style.Colors[ImGuiCol_ButtonActive] =   colorActive;
            style.Colors[ImGuiCol_ButtonHovered] =  colorActive;
        }
        else
        {
            style.Colors[ImGuiCol_Button] =         color;
            style.Colors[ImGuiCol_ButtonActive] =   colorActive;
            style.Colors[ImGuiCol_ButtonHovered] =  colorHover;
        }

        ImGui::PushID(i);   // otherwise two tabs with the same name would clash.

        if (!autoLayout) {if (i>0) ImGui::SameLine();}
        else if (sumX > 0.f) {
            sumX+=sameLineOffset;   // Maybe we can skip it if we use SameLine(0,0) below
            sumX+=ImGui::CalcTextSize(tabLabels[i]).x+btnOffset;
            if (sumX>windowWidth) sumX = 0.f;
            else ImGui::SameLine();
        }

        // Draw the button
        if (ImGui::Button(tabLabels[i]))   {selection_changed = (selectedIndex!=i);selectedIndex = i;}
        if (autoLayout && sumX==0.f) {
            // First element of a line
            sumX = ImGui::GetItemRectSize().x;
        }
        if (pOptionalHoveredIndex) {
            if (ImGui::IsItemHovered()) {
                *pOptionalHoveredIndex = i;
                if (tabLabelTooltips && tabLabelTooltips[i] && strlen(tabLabelTooltips[i])>0)  ImGui::SetTooltip("%s",tabLabelTooltips[i]);
            }
        }
        else if (tabLabelTooltips && tabLabelTooltips[i] && ImGui::IsItemHovered() && strlen(tabLabelTooltips[i])>0) ImGui::SetTooltip("%s",tabLabelTooltips[i]);
        ImGui::PopID();
    }

    // Restore the style
    style.Colors[ImGuiCol_Button] =         color;
    style.Colors[ImGuiCol_ButtonActive] =   colorActive;
    style.Colors[ImGuiCol_ButtonHovered] =  colorHover;
    style.ItemSpacing =                     itemSpacing;

    return selection_changed;
}
} // namespace ImGui

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 selectedTab = 0;
static int optionalHoveredTab = 0;
/*const bool tabSelectedChanged =*/ImGui::TabLabels(numTabs,tabNames,selectedTab,tabTooltips,true,&optionalHoveredTab);
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]);

selection_011

It would be nice if we could fix it (and maybe add tab reordering functionality through drag and drop).

EDIT: made some changes suggested by the posts following this code. However now this code is more like a snippet to be used as a reference rather than a possible extension to the library.

EDIT again: I've just posted an extended version here: https://gist.github.com/Flix01/3bc3d7b3d996582e034e

@ZahlGraf
Copy link

ZahlGraf commented Dec 5, 2015

Looks nice 👍

@ocornut
Copy link
Owner

ocornut commented Dec 6, 2015

Some ideas

  • I think Krys's API where you can provide tabs one at a time is better suited to ImGui style API (but it may make some features easier to implement this year)?
  • I don't understand why your parameters are called with "persistentStorrage" in their name there's nothing persistent about them.
  • Tabs would need more interaction, such as a closing button per tab.
  • Reordering would be great. For that people an API where you feed data one at a time may make it hard to achieve. Unless we decide that the ordering data is on user's side.
  • Need top corner rounded. And better colors, spacing, etc.

@Flix01
Copy link

Flix01 commented Dec 6, 2015

I think Krys's API where you can provide tabs one at a time is better suited to ImGui style API (but it may make some features easier to implement this year)?

True (with my current code we can't add a tab-label popup menu for example).
The main problem is that we have to write the button-wrapping code ourselves.
Some API like ImGui::SameLineIfFits() would ease this task greatly, but I guess it wouldn't be easy to add
(and maybe ImGui::CalcButtonSize(text) would be useful too).

I don't understand why your parameters are called with "persistentStorrage" in their name there's nothing persistent about them.

True. I usually use "persistent storage" or "not owned" inside C++ classes when strings are not copied to prevent users from passing temporary strings, but there's no need to use it here.

Tabs would need more interaction, such as a closing button per tab.

Yes, I was thinking about displaying it manually over the button rectangle (and maybe adding an ItemAdd(), or something like that, to support hovering on it), but I don't know if it would work and that is an advanced task for now.

Reordering would be great. For that people an API where you feed data one at a time may make it hard to achieve. Unless we decide that the ordering data is on user's side.

Yes.
In addition, I was thinking, if we keep the "multi-tab" version, we should add an int that returns the hovered tab-label index, to alleviate some of the problems arisng from losing control over single tab-buttons.

Need top corner rounded. And better colors, spacing, etc.

Maybe we can add some "tab-specific" style variables, or just leave the user override the "Button" style variables.

@ocornut
Copy link
Owner

ocornut commented Dec 6, 2015

The main problem is that we have to write the button-wrapping code ourselves.
Some API like ImGui::SameLineIfFits() would ease this task greatly, but I guess it wouldn't be easy to add (and maybe ImGui::CalcButtonSize(text) would be useful too).

That would be covered by #404, need some refactoring but not a very big amount. Button size like any framed object is by default TextSize + FramePadding*2. After the Button call you can also call GetItemRectSize().

@Flix01
Copy link

Flix01 commented Dec 6, 2015

That would be covered by #404, need some refactoring but not a very big amount. Button size like any framed object is by default TextSize + FramePadding*2. After the Button call you can also call GetItemRectSize().

Perfect!
Probably the best solution for now is to keep the code on the user side and use "single-tab" code like the one @krys-spectralpixel posted.
"Wrapping buttons" is going to become very easy with the new support methods.

EDIT: I renamed the arguments, the method name and changed the widths in the code above, in case someone finds it useful.

EDIT again: I've just posted an extended version here: https://gist.github.com/Flix01/3bc3d7b3d996582e034e

@vivienneanthony
Copy link

Question. Is this code implemented in the new source or is it simple I will have to merge in? I prefer tab windows for a cleaner interface look.

@unpacklo unpacklo mentioned this issue Jan 22, 2016
@ocornut ocornut changed the title Window tabs Tabs Apr 3, 2016
@r-lyeh-archived
Copy link

I've simplified and cleaned up that snippet above
May be of some use to those that need very basic tab handling.

Feat. simplified API, no tooltips and sane default options (and colors :)

Thanks to everyone involved!

gif

// Based on the code by krys-spectralpixel+Flix01 
// [ref] https://github.com/ocornut/imgui/issues/261

#pragma once
#include <imgui.h>

namespace ImGui {
/* 
    tabLabels: name of all tabs involved
    tabSize: number of elements
    tabIndex: holds the current active tab
    tabOrder: optional array of integers from 0 to tabSize-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.

    // USAGE EXAMPLE
    static const char* tabNames[] = {"First tab","Second tab","Third tab"};
    static int tabOrder[] = {0,1,2};
    static int tabSelected = 0;
    const bool tabChanged = ImGui::TabLabels(tabNames,sizeof(tabNames)/sizeof(tabNames[0]),tabSelected,tabOrder);
    ImGui::Text("\nTab Page For Tab: \"%s\" here.\n",tabNames[tabSelected]);
*/

IMGUI_API bool TabLabels(const char **tabLabels, int tabSize, int &tabIndex, int *tabOrder=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 = ImVec4(color.x,color.y,color.z,color.w*0.5f);
    const ImVec4 colorSelectedTabHovered = ImVec4(colorHover.x,colorHover.y,colorHover.z,colorHover.w*0.5f);
    const ImVec4 colorSelectedTabText = ImVec4(colorText.x*0.8f,colorText.y*0.8f,colorText.z*0.8f,colorText.w*0.8f);

    if (tabSize>0 && (tabIndex<0 || tabIndex>=tabSize)) {
        if (!tabOrder)  tabIndex = 0;
        else tabIndex = -1;
    }

    float windowWidth = 0.f,sumX=0.f;
    windowWidth = ImGui::GetWindowWidth() - style.WindowPadding.x - (ImGui::GetScrollMaxY()>0 ? style.ScrollbarSize : 0.f);

    static int draggingTabIndex = -1;int draggingTabTargetIndex = -1;   // These are indices inside tabOrder
    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,newtabIndex = tabIndex;


    bool selection_changed = false;bool noButtonDrawn = true;
    for (int j = 0,i; j < tabSize; j++)
    {
        i = tabOrder ? tabOrder[j] : j;
        if (i==-1) continue;

        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 != tabIndex) {
            // 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 = (tabIndex!=i);newtabIndex = i;}
        ImGui::PopID();
        if (i != tabIndex) {
            // 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 (sumX==0.f) sumX = style.WindowPadding.x + ImGui::GetItemRectSize().x; // First element of a line

        if (ImGui::IsItemHoveredRect()) {
            if (tabOrder)  {
                // tab reordering
                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<tabSize && 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)
                }
            }
        }

    }

    tabIndex = newtabIndex;

    // Draw tab label while mouse drags it
    if (draggingTabIndex>=0 && draggingTabIndex<tabSize) {
        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[tabOrder[draggingTabIndex]]);

        ImGui::SetMouseCursor(ImGuiMouseCursor_Move);
    }

    // Drop tab label
    if (draggingTabTargetIndex!=-1) {
        // swap draggingTabIndex and draggingTabTargetIndex in tabOrder
        const int tmp = tabOrder[draggingTabTargetIndex];
        tabOrder[draggingTabTargetIndex] = tabOrder[draggingTabIndex];
        tabOrder[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 (tabIndex == justClosedTabIndex && tabIndex>=0)    {
        tabIndex = -1;
        for (int j = 0,i; j < tabSize; j++) {
            i = tabOrder ? tabOrder[j] : j;
            if (i==-1) continue;
            tabIndex = 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

@Flix01
Copy link

Flix01 commented Aug 15, 2016

@r-lyeh: Indeed this is very good for people who want to have Tab Labels in a more compact way!

P.S. I had to change the styles, because I shared my ImGui::TabLabels code with ImGui::TabWindow and so it made sense to reuse its own style.

P.S.2. I've recently added to my ImGui fork vertical Tab Labels too (but without "Wrap Mode"). Unfortunately the code for making them cannot be compacted in an easy way for better sharing... 😒
tablabels
[Edit]: People from this issue: #705 might be interested in vertical Tab Labels too.

@codz01
Copy link

codz01 commented Aug 15, 2016

@Flix01 very nice , you've made bunch of useful controls already , many thanks

@Flix01
Copy link

Flix01 commented Sep 19, 2016

P.S.2. I've recently added to my ImGui fork vertical Tab Labels too (but without "Wrap Mode"). Unfortunately the code for making them cannot be compacted in an easy way for better sharing... 😒

Actually @guillaumechereau posted here: #705 a link to an extremely compact alternative implementation.

@ocornut
Copy link
Owner

ocornut commented Oct 16, 2017

I started working on Tabs!
Here's an early work-in-progress of my hacking attempts:

tabs_20171016-b

It won't be finished nor committed for a while because I want:

  • Tabs to be usable completely independently from docking (Docking / Dock Panel Layout #351)
  • But I also want docking to use them.. so until docking is done the tabs aren't done. Will probably take a month-ish until things are stabilized.

Anyway I am gathering ideas and requests now - if you have any specific feature request, please post in the TABS (#261) or DOCKING (#351) threads.

@ocornut ocornut mentioned this issue Oct 17, 2017
@ocornut
Copy link
Owner

ocornut commented Oct 23, 2017

I have pushed some early test code for Tabs in this branch:
https://github.com/ocornut/imgui/tree/tabs

It's a little experimental in the sense that I expect that a lot of this code will be massaged, scrapped, rewritten when working on proper Docking. I only wrote this to give myself a base to think about Drag'n Drop, Docking, and expect the API to change/break.

If you are feeling adventurous or curious you can give it a try..

The main reason I'm releasing this is that I don't expect the real v1.0 to materialize before at least a month.

IMGUI_API void          BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0);
IMGUI_API void          EndTabBar();
IMGUI_API bool          TabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags = 0);

IMGUI_API void          SetTabItemClosed(const char* label); // Optional, allows to bypass a frame of flicker in situation where tab is closed programmatically
IMGUI_API void          SetTabItemSelected(const char* label);

It's in two separate files imgui_tabs.cpp imgui_tabs.h which you can grab (the rest of master is unchanged). I'm just using those two files for work and depending how the rest of the features evolve it may be merged into a single pair of files, etc. I don't know at the moment.

This is a GIF i recorded on Oct 19: (Various things got fixed/improved since but that should get you an idea where it is at).
tabs_20171019

@ocornut
Copy link
Owner

ocornut commented Oct 23, 2017

FYI I am also closing #1083 as a duplicate. In that thread @scottmudge posted his implementation of Tab, which you can find here https://github.com/ScottMudge/imgui_tabs and discussions in #1083

Arguably my version is more complete, but in the absence of persistence settings (order are not saved), and docking or dragging out as floating window, etc. it is still sitting in a weird spot where it doesn't do everything desirable.

GIF of the code in #1083
687474703a2f2f692e696d6775722e636f6d2f3650534b394e4c2e676966

@codecat
Copy link
Contributor

codecat commented Feb 26, 2018

Hey, I'm using these tabs! They work very nicely, I like the API, too! Do you have any idea when this will be merged into master? Having to juggle between branches when updating Imgui can become quite tedious.

@ocornut
Copy link
Owner

ocornut commented Feb 26, 2018

@codecat Sorry I have no ETA. Notice that imgui.cpp hasn't been modified in this branch, you could grab the latest _tabs files and update them occasionally.

@zahirzohair
Copy link

how can i add "NEW TAB" functionality like web browsers which has a small button when we hover over it a message pops up " New tab" when user click on it , then new tab will open.

1

@scottmudge
Copy link

@zahirzohair You can customize my implementation (which is arguably a little stale and needs to be refreshed). All you would need to do is use a vector (either ImGui's version or STL implementionation) of a struct describing the different tab details -- title, type of tab (to determine behavior in a switch statement), etc., -- and then use a for loop to iterate over its members, using AddTab() to insert them into the TabBar.

@zahirzohair
Copy link

@ocornut if two tabs have the same name, it displays on top of each other and looks like one tab.
is there any solution for that?

@scottmudge
Copy link

scottmudge commented Mar 21, 2018 via email

@BeachCoder76
Copy link

@ocornut Hi Omar, first thanks for ImGui .. it is an awesome little piece of code! That being said, I integrated your wip imgui_tabs and wanted to report a problem with this piece of code:

// Position newly appearing tab at the end of the tab list
if (tab_appearing && !tab_bar_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_NoResetOrderOnAppearing))
{
    tab->OffsetTarget = tab_bar->OffsetMax + g.Style.ItemInnerSpacing.x;
    if (tab->CurrentOrder != -1 && tab_bar->TabsOrder.back() != tab->GlobalIndex)
    {
        // Move tab to end of the list
        IM_ASSERT(tab_bar->TabsOrder[tab->CurrentOrder] == tab->GlobalIndex);
        memmove(&tab_bar->TabsOrder[tab->CurrentOrder], &tab_bar->TabsOrder[tab->CurrentOrder + 1], (tab_bar->TabsOrder.Size - tab->CurrentOrder - 1) * sizeof(tab_bar->TabsOrder[0]));
        tab_bar->TabsOrder.back() = tab->GlobalIndex;
        tab_bar->CurrOrderInsideTabsIsValid = false;
    }
}

If in the same frame, 2 tabs are reappearing in the tab bar (let's say you have a filter on the tab names that is filtering 2 TABs out of 4 and you then clear the filter string so all TABs suddenly need to reappear) .. the first one switching its position to TabsOrder.back() will memmove() all others down (without reindexing/updating the other tabs->CurrentOrder) so the next one will still have its CurrentOrder set on the old position. The next one will ImAssert in that code with a bad CurrentOrder vs TabOrders GlobalIndex.

This code probably assumes one reappearing TAB per frame and never broke because the next call to TabBarLayout should fix all tabs CurrentOrder variable anyway.

You can workaround this by skipping that code (ie creating your BeginTabBar with the ImGuiTabBarFlags_NoResetOrderOnAppearing flags OR updating all subsequent TABs after the memmove().

@BeachCoder76
Copy link

And while we're at it .. the tabs demo code doesn't use the usual early out strategy which I changed locally to :

void    ImGui::ShowTabsDemo(const char* title, bool* p_open)
{
    if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar))
    {
        // Early out if the window is collapsed, as an optimization.
        ImGui::End();
        return;
    }

.. small detail but it does prevent going into the tabbing code if the tab demo window is visible but closed. :)

@ocornut
Copy link
Owner

ocornut commented Oct 1, 2018

I am closing this issue and will obsolete the old Tabs branch. This is all transitioning to the docking branch. (#2109)
The API is very slightly different (BeginTabItem instead of TabItem) but should be trivial to update.

@codecat
Copy link
Contributor

codecat commented Oct 7, 2018

For those transitioning from the old API, there's also an EndTabItem now.

ocornut added a commit that referenced this issue Dec 11, 2018
…ature 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.
@ocornut
Copy link
Owner

ocornut commented Dec 11, 2018

Everyone: I have now merged the Tab API into the master branch.
The API is marked as "Beta"
(Please note the tab api itself doesn't provide the docking/merging/splitting features of the Docking branch! Docking itself is a user of the tab API)

API

// Tab Bars, Tabs
// [BETA API] API may evolve!
bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0);        // create and append into a TabBar
void EndTabBar();
bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0);// create a Tab. Returns true if the Tab is selected.
void EndTabItem();                                                       // only call EndTabItem() if BeginTabItem() returns true!
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.
// 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()
};

In Demo->Layout->Tabs
imgui_capture_0000

and Demo->Examples->Documents
imgui_capture_0001
imgui_capture_0002

(Bonus fact: I am currently working on the testing/automation API and those 3 screenshots were mostly auto-generated. / By "mostly" I mean that there's still various manual actions and hacks involved in my current automation/capturing pipeline, but I am narrowing the gap little by little toward being able to generate screenshots based on automation scripts.)

@codecat
Copy link
Contributor

codecat commented Dec 11, 2018

That's awesome! Thanks for the heads up!

ocornut added a commit that referenced this issue Dec 11, 2018
ocornut added a commit that referenced this issue Dec 11, 2018
…ropHold. (#261) incorrectly missing from the merge from Docking branch.
ocornut added a commit that referenced this issue Feb 5, 2019
… + whole style scaling. Added ImGuiTabBarFlags_TabListPopupButton flag to show a popup button on manual tab bars. Locking FramePadding for the scope of a tab-bar to avoid sheering/clipping of tab item. Made scaling of tab ellipsis less awkward. (#261, #351)
ocornut added a commit that referenced this issue Feb 5, 2019
@ocornut ocornut added the tabs tab bars, tabs label Sep 30, 2020
ocornut added a commit that referenced this issue Jun 19, 2024
… ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.

Amend #261, #351
Ciachociech added a commit to Ciachociech/imgui that referenced this issue Jul 24, 2024
* Version 1.90.8

* Version 1.90.9 WIP

* Internals: renamed HoveredIdDisabled to HoveredIdIsDisabled for consistency.

* Examples: GLFW+Vulkan: handle swap chain resize even without Vulkan returning VK_SUBOPTIMAL_KHR (ocornut#7671)

* Examples: SDL+Vulkan: handle swap chain resize even without Vulkan returning VK_SUBOPTIMAL_KHR (ocornut#7671)

* Removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).

* Internals: made ImLowerBound() accessible in internals + take a span. + rearrange child/popup/tooltips section.

Because upcoming rework of ImGuiSelectionBasicStorage will want to do a lower bound on a span.

* IO: do not disable io.ConfigWindowsResizeFromEdges when ImGuiBackendFlags_HasMouseCursors is not set by backend.

Amend 42bf149

* Style: (Breaking) renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.

Amend ocornut#261, ocornut#351

* TabBar, Style: added ImGuiTabBarFlags_DrawSelectedOverline and ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmedSelectedOverline.

* Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceExtern. (ocornut#143)

Amend 0c6e260

* Drag and Drop: Fixes an issue when elapsing payload would be based on last payload frame instead of last drag source frame.

* Internals: added ImGuiContext::ContextName optionally used by debug log and to facilitate debugging.

* Drag and Drop: comments, debug log entries.

* Drag and Drop: BeginDragDropSource() with ImGuiDragDropFlags_SourceExtern assume a mouse button being pressed. (ocornut#143)

* Drag and Drop: (Breaking) renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire. (ocornut#1725, ocornut#143)

* Drag and Drop: Added ImGuiDragDropFlags_PayloadNoCrossContext and ImGuiDragDropFlags_PayloadNoCrossProcess flags.

* Ignore .ini file with other suffixes.

* Fixed build warning.

* IO: added ClearInputMouse(). made ClearInputKeys() not clear mouse data. (ocornut#4921)

Amend 6aa408c

* IO: added ImGuiConfigFlags_NoKeyboard for consistency and convenience. (ocornut#4921)

# Conflicts:
#	imgui.h
#	imgui_demo.cpp

* Nav: CTRL+Tab overlay display context name if any.

* Internals: storing HoveredWindowBeforeClear for use by multi-context compositor drag and drop propagation.

# Conflicts:
#	imgui.cpp
#	imgui_internal.h

* (Breaking) Move ImGuiWindowFlags_NavFlattened to ImGuiChildFlags_NavFlattened. (ocornut#7687)

* Backends: SDL3: Follow SDL3 removal of keysym field in SDL_KeyboardEvent (ocornut#7729)

* Demo: Style Editor: clarify how _CalcCircleAutoSegmentCount() doesn't always get exact final segment count. (ocornut#7731)

* Backends: Vulkan: Remove Volk/ from volk.h #include directives (ocornut#7722, ocornut#6582, ocornut#4854)

* Metrics/Debugger: Browsing a Storage perform hover lookup on identifier.

* Viewports: Backported 'void* ImGuiViewport::PlatformHandle' from docking branch for use by backends.

* Backends: SDL3: Update for SDL_StartTextInput()/SDL_StopTextInput() API changes. (ocornut#7735)

* Examples: undo adding SDL3 example to Visual Studio sln.

* Backends: OSX: build fix. Amend 32f9dfc

* ImGuiStorage: tweak impl for BuildSortByKey().

* Inputs: fixed using Shortcut() or SetNextItemShortcut() within a disabled block bypassing the disabled state. (ocornut#7726)

* Tables: moved TableGetHoveredColumn() to public API. (ocornut#7715, ocornut#3740)

* Windows: BeginChild(): fixed a glitch when during a resize of a child window which is tightly close to the boundaries of its parent. (ocornut#7706)

* Nav: store NavJustMovedToIsTabbing + shuffle a few nav related fields.

(for usage by multi-select)

* Backends: OpenGL2, OpenGL3: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (ocornut#7748)

Analogous to change to Vulkan backend in 1.90.

* Backends: Win32: Fixed warning with old MinGW/GCC versions.

* Drags: added ImGuisliderFlags_WrapAround flag for DragInt(), DragFloat() etc. (ocornut#7749)

* Fix typo, rename ImGuisliderFlags_WrapAround flag to ImGuiSliderFlags_WrapAround. (ocornut#7752, ocornut#7749)

* Checkbox: minor tidying up to simplify work on multi-select branch.

* Backends: Allegro5: Correctly handle unstable bit in version checks (ocornut#7755)

* Backends: SDLRenderer3: Update for SDL_RenderGeometryRaw() API changes.

* Backends: SDL3: update for SDL_SetTextInputRect() -> SDL_SetTextInputArea() api change. (ocornut#7760, ocornut#7754)

* Examples: SDL3: Remove use of SDL_HINT_IME_NATIVE_UI.

* Disabled: Reworked 1.90.8 behavior of Begin() not inheriting current BeginDisabled() state. Only tooltip are clearing that state. (ocornut#211, ocornut#7640)

* imgui_freetype: fixed divide by zero while handling FT_PIXEL_MODE_BGRA glyphs. (ocornut#7267, ocornut#3369)

* IO: do not claim io.WantCaptureMouse=true on the mouse release frame of a button which was pressed over void.  (ocornut#1392)

* Version 1.90.9

* Version 1.91.0 WIP

* Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (ocornut#7761, ocornut#7762)

Also updated function signature in SDL2 backend to match and because it is expected we will use that data (as per ocornut#7672)

* Backends: SDL3: Updated comments (IME seems fixed in SDL3). Added SDL3 examples to Visual Studio solution.

* Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (ocornut#5855)

* Debug Log: Added "Configure Outputs.." button. (ocornut#5855)

* Backends: SDL3: add default case to fix warnings. (ocornut#7763)

* Demo: changed style editor inline block to its own window.

* IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell, added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS. (ocornut#7660)

* (Breaking) IO, IME: renamed platform IME hook io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() and added explicit context.

* Commented out obsolete ImGuiModFlags and ImGuiModFlags_XXX values (renamed to ImGuiKeyChord and ImGuiMod_XXX in 1.89). (ocornut#4921, ocornut#456)

* Build fix for non Windows platforms.

* IO: disable default io.PlatformOpenInShellFn() implementation on iPhone, as compiler errors that system() is not available on iOS.

* Internals: added FontScale storage.

* Added TextLink(), TextLinkOpenURL() hyperlink widgets. (ocornut#7660)

* Internals: added FontScale storage (amend 0f63d3e).

* Backends: GLFW,SDL2: Added ioPlatformOpenInShellFn handler for web/Emscripten versions. (ocornut#7660)

* IO: amend PlatformOpenInShellFn specs to return a bool. (ocornut#7660)

Amend 8f36798

* Misc tweaks, comments.

* TreeNode: rename/rework ImGuiNavTreeNodeData system to be usable by more features. (ocornut#2920, ocornut#1131, ocornut#7553)

Reworked to it is easier during TreeNode code to request extra data to be stored.

* Fixed Unix version of PlatformOpenInShellFn_DefaultImpl. (ocornut#7772, ocornut#7660)

+ Enable on non-iPhone macOS builds

* DemosFix typo in help text in demo Tables/Borders (ocornut#7780)

The help text for flags had a "V" flag duplicated, this change corrects it to the missing "H" flag.

* Backends: Win32: fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN (ocornut#7768, ocornut#4858, ocornut#2622)

Amend 0755767

The `ImGui_ImplWin32_UpdateKeyModifiers()` function maps `ImGuiMod_Super` to `VK_APPS`, the "Application" key located between the Right Windows (Super) and Right Control keys on the keyboard, see https://conemu.github.io/en/AppsKey.html

This means that when using `ImGui::GetIO().KeySuper` to try to get the down state of the `VK_RWIN` or `VK_LWIN` keys, it'll always return FALSE when either of those keys are held down, and only return TRUE when `VK_APPS` is held down.

* Backends: GLFW+Emscripten: (Breaking) Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback() to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWwindow* parameter. (ocornut#7647, ocornut#7600)

+ Fixed Emscripten warning when using mouse wheel on some setups.

* Backends: GLFW+Emscripten: Added support for GLFW3 contrib port. (ocornut#7647)

* Backends: GLFW+Emscripten: Fixed build (ocornut#7647)

* Examples: SDL3+OpenGL: Update for API changes: SDL_GL_DeleteContext() renamed to SDL_GL_DestroyContext().

* Fix definition check (ocornut#7793)

* Backends: SDL3: Update for API changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (ocornut#7794)

* Backends: SDL3: fixed typo leading to PlatformHandleRaw not being set leading to SHOWNA path not working for multi-viewports.

* Internals: Added TreeNodeIsOpen() to facilitate discoverability. (ocornut#7553, ocornut#1131, ocornut#2958, ocornut#2079, ocornut#722)

* Added ImGuiDataType_Bool for convenience.

* Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and struct description data that a real application would want to use.

* Added PushItemFlag(), PopItemFlag(), ImGuiItemFlags.

* (Breaking) Obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_ButtonRepeat.

* Added ImGuiItemFlags_AutoClosePopups as a replacement for internal's ImGuiItemFlags_SelectableDontClosePopup. (ocornut#1379, ocornut#1468, ocornut#2200, ocornut#4936, ocornut#5216, ocornut#7302, ocornut#7573)

* (Breaking) Renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (ocornut#1379, ocornut#1468, ocornut#2200, ocornut#4936, ocornut#5216, ocornut#7302, ocornut#7573)

* Obsoleted PushTabStop()/PopTabStop() in favor of using new PushItemFlag()/PopItemFlag() with ImGuiItemFlags_NoTabStop.

* Fixed pvs-studio warning.

* Demo: Property Editor: rearrange code + replace use of bool to proper ImGuiChildFlags.

Amend 46691d1

* Demo: Property Editor: add basic filter.

* Style: close button and collapse/window-menu button hover highlight made rectangular instead of round.

The reason they were round in the first place was to work better with rounded windows/frames.
However since the 4a81424 rework ocornut#6749 we can naturally use a tigher bounding box and it seems to work ok either way.

* Nav, Demo: comments.

* Clipper: added SeekCursorForItem() function, for use when using ImGuiListClipper::Begin(INT_MAX). (ocornut#1311)

Tagging ocornut#3609 just in case we made a mistake introducing a regression (but tests are passing and have been extended).

* TreeNode: Internals: facilitate dissociating item ID from storage ID (useful for 1861)

* Internals: rename recently added TreeNodeIsOpen() -> TreeNodeGetOpen(). (ocornut#7553, ocornut#1131, ocornut#2958, ocornut#2079, ocornut#722)

Amend ac7d6fb

* Backends: SDL3: Update for API changes: SDL_GetClipboardText() string ownership change. (ocornut#7801)

* MultiSelect: WIP range-select (ocornut#1861) (rebased six millions times)

* MultiSelect: Removed SelectableSpacing as I'm not sure it is of use for now (history insert)

* MultiSelect: Added IMGUI_HAS_MULTI_SELECT define. Fixed right-click toggling selection without clearing active id, could lead to MarkItemEdited() asserting. Fixed demo.

* MultiSelect: Demo sharing selection helper code. Fixed static analyzer warnings.

* MultiSelect: Renamed SetNextItemMultiSelectData() to SetNextItemSelectionUserData()

* MultiSelect: Transition to use FocusScope bits merged in master.

Preserve ability to shift+arrow into an item that is part of FocusScope but doesn't carry a selection without breaking selection.

* MultiSelect: Fix for TreeNode following merge of 011d475. Demo: basic test for tree nodes.

* MultiSelect: Fixed CTRL+A not testing focus scope id. Fixed CTRL+A not testing active id. Added demo code.

Comments.

* MultiSelect: Comments. Tweak demo.

* MultiSelect: Fix Selectable() ambiguous return value, clarify need to use IsItemToggledSelection().

* MultiSelect: Fix testing key mods from after the nav request (remove need to hold the mod longer)

* MultiSelect: Temporary fix/work-around for child/popup to not inherit MultiSelectEnabled flag, until we make mulit-select data stackable.

* MultiSelect: Fixed issue with Ctrl+click on TreeNode + amend demo to test drag and drop.

* MultiSelect: Demo: Add a simpler version.

* MultiSelect: Added ImGuiMultiSelectFlags_ClearOnEscape (unsure of best design), expose IsFocused for custom shortcuts.

* MultiSelect: Demo: Added pointer indirection and indent level.

This is to reduce noise for upcoming commits, ahead of adding a loop here.

* MultiSelect: Added ImGuiMultiSelectFlags_ClearOnClickWindowVoid. + Demo: showcase multiple selection scopes in same window.

* MultiSelect: Enter doesn't alter selection (unlike Space).

Fix for changes done in 5606.

* MultiSelect: Shallow tweaks/refactors.

Including moving IsFocused back internally for now.

* MultiSelect: Fixed needing to set RangeSrcPassedBy when not using clipper.

* MultiSelect: made SetNextItemSelectionData() optional to allow disjoint selection (e.g. with a CollapsingHeader between items). Amend demo.

* MultiSelect: Enter can alter selection if current item is not selected.

* MultiSelect: removed DragDropActive/preserve_existing_selection logic which seems unused + comments.

Can't find trace of early prototype for range-select but I couldn't find way to trigger this anymore. May be wrong. Will find out.

* MultiSelect: refactor before introducing persistant state pool and to facilitate adding recursion + debug log calls.

This is mostly the noisy/shallow stuff committed here, to get this out of the way.

* MultiSelect: (Breaking) Rename ImGuiMultiSelectData to ImGuiMultiSelectIO.

* MultiSelect: Demo tweak. Removed multi-scope from Advanced (too messy), made it a seperate mini-demo.

* MultiSelect: Internals rename of IO fields to avoid ambiguity with io/rw concepts + memset constructors, tweaks.

debug

* MultiSelect: (Breaking) Renamed 'RangeSrc -> 'RangeSrcItem', "RangeDst' -> 'RangeDstItem'

This is necessary to have consistent names in upcoming fields (NavIdItem etc.)

* MultiSelect: (Breaking) Renamed 'RangeValue' -> 'RangeSelected' + amend comments.

* MultiSelect: Remove ImGuiMultiSelectFlags_NoUnselect because I currently can't find use for this specific design.

And/or it seem partly broken.

* MultiSelect: Remove the need for using IsItemToggledSelection(). Update comments.

This is the simple version that past our tests. MultiSelectItemFooter() is in need of a cleanup.

* MultiSelect: Tidying up/simpllifying MultiSelectItemFooter().

Intended to be entirely a no-op, merely a transform of source code for simplification. But committing separatey from behavior change in previous change.

* MultiSelect: Clarify and better enforce lifetime of BeginMultiSelect() value.

* MultiSelect: Demo: first-draft of user-side deletion idioms.

(will need support from lib)

* MultiSelect: (Breaking) BeginMultiSelect() doesn't need two last params maintained by users. Moving some storage from user to core. Proper deletion demo.

* MultiSelect: Maintain NavIdSelected for user. Simplify deletion demo.

* MultiSelect: Further simplication of user code to support Deletion.

Provide standard RequestFocusItem storage.

* MultiSelect: Demo: Delete items from menu.

* MultiSelect: Fixed right-click handling in MultiSelectItemFooter() when not focused.

* MultiSelect: Cleanup unused comments/code.

* MultiSelect: (Breaking) Fix + Rename ImGuiMultiSelectFlags_NoMultiSelect to ImGuiMultiSelectFlags_SingleSelect as it seems easier to grasp.

Feature was broken by "Tidying up..." June 30 commit.

* MultiSelect: Comments, tweaks.

+ Alignment to reduce noise on next commit.

* MultiSelect: (Breaking) Use ImGuiSelectionUserData (= ImS64) instead of void* for selection user data.

Less confusing for most users, less casting.

* MultiSelect: move HasSelectionData to ImGuiItemFlags to facilitate copying around in standardized fieds.

Required/motivated to simplify support for ImGuiTreeNodeFlags_NavLeftJumpsBackHere (bc3c0ce) in this branch.

* MultiSelect: Tweak debug log to print decimal+hex values for item data.

Struggled to get standard PRIX64 to work on CI.

* MultiSelect: clear selection when leaving a scope with a nav directional request.

May need to clarify how to depends on actions being performed (e.g. click doesn't).
May become optional?

* MultiSelect: (Breaking) RequestSetRange's parameter are RangeFirstItem...RangeLastItem (which was always ordered unlike RangeSrcItem...RangeDstItme). Removed RangeDstItem. Removed RangeDirection.

* MultiSelect: Demo: rework ExampleSelection names to map better to typical user code + variety of Comments tweaks.

* MultiSelect: Demo: added simpler demo using Clipper. Clarify RangeSrcPassedBy doc.

* MultiSelect: (Breaking) Removed RangeSrcPassedBy in favor of favoring user to call IncludeByIndex(RangeSrcItem) which is easier/simpler to honor.

Especially as recent changes made it required to also update RangeSrcPassedBy after last clipper Step.
Should now be simpler.

* MultiSelect: Demo: rework ExampleSelection with an ExampleSelectionAdapter layer, allowing to share more code accross examples using different storage systems.

Not ideal way to showcase this demo but this is really more flexible.

* MultiSelect: Demo: Remove UserDataToIndex from ExampleSelectionAdapter.

Seems to make a better demo this way.

* MultiSelect: Demo: Make ExampleSelection use ImGuiID. More self-explanatory.

* MultiSelect: Demo: Deletion: Rework ApplyDeletionPreLoop to use adapter + fix PostLoop not using right value of RequestFocusItem.

Recovery made it transparent visually but user side selection would be empty for a frame before recovery.

* MultiSelect: Demo: Deletion: Various renames to clarify. Use adapter and item list in both ApplyDeletion functions.

This also minify the patch for an alternative/wip attmept at redesgining pre/post deletion logic. But turns out current attempt may be easier to grasp.

* Demo: Dual List Box: Added a dual list box (6648)

* MultiSelect: ImGuiMultiSelectIO's field are not used during loop anymore, stripping them out of comments.

* MultiSelect: moved RequestClear output so it'll match request list version better. Use Storage->RangeSrcItem in EndMultiSelect().

* MultiSelect: move shared logic to MultiSelectItemHeader().

No logic change AFAIK but added an indent level in MultiSelectItemHeader(). Logic changes will come in next commit.

* MultiSelect: Added ImGuiMultiSelectFlags_SelectOnClickRelease to allow dragging an unselected item without altering selection + update drag and drop demo.

* Demo: Assets Browser: Added assets browser demo.

* Demo: Assets Browser: store items, sorting, type overlay.

* MultiSelect: removed seemingly unnecessary block in BeginMultiSelect().

- EndIO.RangeSelected always set along with EndIO.RequestSetRange
- Trying to assert for the assignment making a difference when EndIO.RequestSetRange is already set couldn't find a case (tests passing).

* MultiSelect: clarified purpose and use of IsItemToggledSelection(). Added assert. Moved to multi-selection section of imgui.h.

* MultiSelect: added missing call on Shutdown(). Better reuse selection buffer.

* MultiSelect: (Breaking) io contains a ImVector<ImGuiSelectionRequest> list.

* MultiSelect: we don't need to ever write to EndIO.RangeSrcItem as this is not meant to be used.

* MultiSelect: added support for recovery in ErrorCheckEndWindowRecover().

* MultiSelect: use a single ImGuiMultiSelectIO buffer.

+ using local storage var in EndMultiSelect(), should be no-op.

* MultiSelect: simplify clearing ImGuiMultiSelectTempData.

* Demo: Assets Browser: add hit spacing, requierd for box-select patterns.

* MultiSelect: (breaking) renamed ImGuiMultiSelectFlags_ClearOnClickWindowVoid -> ImGuiMultiSelectFlags_ClearOnClickVoid. Added ImGuiMultiSelectFlags_ScopeWindow, ImGuiMultiSelectFlags_ScopeRect.

* MultiSelect: Box-Select: added support for ImGuiMultiSelectFlags_BoxSelect.

(v11)
FIXME: broken on clipping demo.

* MultiSelect: Box-Select: added scroll support.

* MultiSelect: Demo: rework and move selection adapter inside ExampleSelection.

* MultiSelect: added support for nested/stacked BeginMultiSelect().

Mimicking table logic, reusing amortized buffers.

* MultiSelect: remove ImGuiSelectionRequest/ImGuiMultiSelectIO details from public api to reduce confusion + comments.

* MultiSelect: move demo's ExampleSelection to main api as a convenient ImGuiSelectionBasicStorage for basic users.

* MultiSelect: reworked comments in imgui.h now that we have our own section.

* MultiSelect: Demo: Assets Browser: added deletion support. Store ID in selection. Moved QueueDeletion to local var to emphasis that this is a user extension.

* MultiSelect: Demo: Assets Browser: track scrolling target so we can roughly land on hovered item.

It's impossible to do this perfectly without some form of locking on item because as the hovered item X position changes it's easy to drift.

* MultiSelect: Box-Select: Fixed holes when using with clipper (in 1D list.)

Clipper accounts for Selectable() layout oddity as BoxSelect is sensitive to it.
Also tweaked scroll triggering region inward.
Rename ImGuiMultiSelectFlags_NoBoxSelectScroll to ImGuiMultiSelectFlags_BoxSelectNoScroll.
Fixed use with ImGuiMultiSelectFlags_SinglaSelect.

* MultiSelect: Box-Select: Added ImGuiMultiSelectFlags_BoxSelect2d support. Enabled in Asset Browser. Selectable() supports it.

* MultiSelect: Box-Select: Refactor into its own structure, designed for single-instance but closer to being reusable outside Multi-Select.

Kept same member names.

* MultiSelect: Box-Select: Refactor: Renames.

Split into two commits to facilite looking into previous one if needed.

* MultiSelect: Box-Select: Fixed scrolling on high framerates.

* MultiSelect: Box-Select: Further refactor to extra mode code away from multi-select function into box-select funcitons.

* MultiSelect: Fixed ImGuiSelectionBasicStorage::ApplyRequests() incorrectly maintaining selection size on SelectAll.

* MultiSelect: Comments + Assets Browser : Tweak colors.

* MultiSelect: Added ImGuiMultiSelectFlags_NoRangeSelect. Fixed ImGuiMultiSelectFlags_ScopeRect not querying proper window hover.

* MultiSelect: Box-Select: Fixed CTRL+drag from void clearing items.

* MultiSelect: Box-Select: Fixed initial drag from not claiming hovered id, preventing window behind to move for a frame.

* MultiSelect: Fixed ImGuiMultiSelectFlags_SelectOnClickRelease over tree node arrow.

* MultiSelect: (Breaking) merge ImGuiSelectionRequestType_Clear and ImGuiSelectionRequestType_SelectAll into ImGuiSelectionRequestType_SetAll., rename ImGuiSelectionRequest::RangeSelected to Selected.

The reasoning is that it makes it easier/faster to write an adhoc ImGuiMultiSelectIO handler (e.g. trying to apply multi-select to checkboxes)

* MultiSelect: Simplified ImGuiSelectionBasicStorage by using a single SetItemSelected() entry point.

* MultiSelect: Comments + tweaked location for widgets to test ImGuiItemFlags_IsMultiSelect to avoid misleading into thinking doing it before ItemAdd() is necessary.

* MultiSelect: Demo: make various child windows resizable, with synched heights for the dual list box demo.

* MultiSelect: added ImGuiMultiSelectFlags_NoAutoSelect, ImGuiMultiSelectFlags_NoAutoClear features + added Checkbox Demo

Refer to "widgets_multiselect_checkboxes" in imgui_test_suite.

* MultiSelect: Box-Select: fix preventing focus. amend determination of scope_hovered for decorated/non-child windows + avoid stealing NavId. (ocornut#7424)

* MultiSelect: Demo: use Shortcut().

Got rid of suggestion to move Delete signal processing to BeginMultiSelect(), seems unnecessary.

* RangeSelect/MultiSelect: (Breaking) Added current_selection_size to BeginMultiSelect().

Required for shortcut routing so we can e.g. have Escape be used to clear selection THEN to exit child window.

* MultiSelect: Box-Select: minor refactor, tidying up.

* MultiSelect: Box-Select: when dragging from void, first hit item sets NavId by simulating a press, so navigation can resume from that spot.

* MultiSelect: added GetMultiSelectState() + store LastSelectionSize as provided by user, convenient for quick debugging and testing.

* MultiSelect: Box-Select: fixed "when dragging from void" implementation messing with calling BeginMultiSelect() without a selection size.

* MultiSelect: (breaking) renamed ImGuiSelectionBasicStorage::AdapterData to UserData.

* MultiSelect: Box-Select: fixes for checkboxes support. Comments.

* MultiSelect: (breaking) renamed ImGuiMultiSelectFlags_BoxSelect -> ImGuiMultiSelectFlags_BoxSelect1d, ImGuiMultiSelectFlags_BoxSelect2d -> ImGuiMultiSelectFlags_BoxSelect.

ImGuiMultiSelectFlags_BoxSelect1d being an optimization it is the optional flag.

* MultiSelect: mark parent child window as navigable into, with highlight. Assume user will always submit interactive items.

* MultiSelect: (breaking) Added 'items_count' parameter to BeginMultiSelect(). Will enable extra features, and remove equivalent param from ImGuiSelectionBasicStorage::ApplyRequests(.

* MultiSelect: added ImGuiSelectionBasicStorage::GetStorageIdFromIndex() indirection to be easier on the reader.

Tempting to make it a virtual.

* MultiSelect: fixed ImGuiSelectionBasicStorage::Swap() helper.

* MultiSelect: added ImGuiSelectionExternalStorage helper. Simplify bool demo.

* MultiSelect: comments, header tweaks., simplication (some of it on wiki).

* MultiSelect: ImGuiSelectionBasicStorage: added GetNextSelectedItem() to abstract selection storage from user. Amend Assets Browser demo to handle drag and drop correctly.

* MultiSelect: ImGuiSelectionBasicStorage: rework to accept massive selections requests without flinching.

Batch modification + storage only keeps selected items.

* MultiSelect: ImGuiSelectionBasicStorage: simplify by removing compacting code (compacting may be opt-in?).

GetNextSelectedItem() wrapper gives us more flexibility to work on this kind of stuff now.

* MultiSelect: ImGuiSelectionBasicStorage: move function bodies to cpp file.

+ make ImGuiStorage::BuildSortByKey() less affected by msvc debug mode.

* Demo: Assets Browser: added a way to disable sorting and hide sorting options.

This is mostly designed to showcase that on very large sets (e.g. 1 million) most of the time is likely spent on sorting.

* MultiSelect: ImGuiSelectionBasicStorage: (breaking) rework GetNextSelectedItem() api to avoid ambiguity/failure when user uses a zero id.

* MultiSelect: provide RangeDirection to allow selection handler to handler backward shift+click.

* MultiSelect: ImGuiSelectionBasicStorage: added PreserveOrder, maintain implicit order data in storage.

Little tested but provided for completeness.

* MultiSelect: (breaking) renamed ImGuiMultiSelectFlags_BoxSelect -> ImGuiMultiSelectFlags_BoxSelect2d. Which include not assuming one flag imply the other.

Amend 2024/05/31 commit.

* MultiSelect: Shift+Tab doesn't enable Shift select on landing item.

* MultiSelect: added ImGuiMultiSelectFlags_NoAutoClearOnReselect + tweak flags comments. (ocornut#7424)

* MultiSelect: minor tidying up.

Checkbox() was reworked in master effectively fixing render clipping when culled by BoxSelect2d's UnclipMode.

* MultiSelect: added courtesy ImGuiMultiSelectFlags_NavWrapX flag so we can demo this until a nav api is designed.

* MultiSelect: Box-Select: uses SetActiveIdUsingAllKeyboardKeys() to avoid nav interference, much like most drag operations.

* MultiSelect: Box-Select: handle Esc to disable box-select.

This avoid remove a one-frame delay when finishing box-select, where Esc wouldn't be routed to selection but to child.

* MultiSelect: ImGuiSelectionBasicStorage: optimized for smaller insertion amounts in larger sets + fix caling batch select with same value.

* MultiSelect: Better document how TreeNode() is not trivially usable yet.

Will revert when the time is right.

* MultiSelect: added Changelog for the feature. Removed IMGUI_HAS_MULTI_SELECT.

* Demo: moved ExampleTreeNode, ExampleMemberInfo above in the demo file. Tidying up index.

+ change ExampleTreeNode::UID from ImGuiID to int to not suggest that the user ID needs to be of a certain type

* Demo: moved some fields inside a struct.

* Demo: moved menu bar code to its own function.

* MultiSelect: using ImGuiMultiSelectFlags_NoRangeSelect ensure never having to interpolate between two ImGuiSelectionUserData.

* Inputs: added SetItemKeyOwner(ImGuiKey key) in public API. (ocornut#456, ocornut#2637, ocornut#2620, ocornut#2891, ocornut#3370, ocornut#3724, ocornut#4828, ocornut#5108, ocornut#5242, ocornut#5641)

* Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership change. (ocornut#7807)

* TabBar, Style: added style option for the size of the Tab-Bar Overline (ocornut#7804)

Amend 21bda2e.

* Added a comment hinting at how to set IMGUI_API for shared librairies on e.g. Linux, macOS (ocornut#7806)

* Demo: rework Property Editor.

* Nav: fixed c licking window decorations (e.g. resize borders) from losing focused item when within a child window using ImGuiChildFlags_NavFlattened.

In essence, using ImGuiFocusRequestFlags_RestoreFocusedChild here is a way to reduce changes caused by FocusWindow(), but it could be done more neatly.
See amended "nav_flattened" test.

* Demo: Property Editor: using ImGuiChildFlags_NavFlattened now that a bug is fixed. Fixed static analyzer.

* Backends: OpenGL3: Fixed unsupported option warning with apple clang (ocornut#7810)

* Internals, TreeNode: indent all render block into its own scope (aim is to add a is_visible test there later)

* Internals, TreeNode, Selectable: tweak span_all_columns paths for clarity.

* CollapsingHeader: left-side outer extend matches right-side one (moved left by one pixel)

Amend c3a348a

* Debug Log: fixed incorrect checkbox layout when partially clipped., doesn't parse 64-bits hex value as ImGuiID lookups.

* Groups, Tables: fixed EndGroup() failing to correctly capture current table occupied size. (ocornut#7543)

See "layout_group_endtable" test.

* MultiSelect: sequential SetRange merging not generally handled by box-select path, useful for others.

* MultiSelect: add internal MultiSelectAddSetAll() helper.

* MultiSelect: fixed an issue caused by previous commit.

Amend a285835. Breaks box-select.

---------

Co-authored-by: ocornut <[email protected]>
Co-authored-by: cfillion <[email protected]>
Co-authored-by: Gary Geng <[email protected]>
Co-authored-by: Martin Ejdestig <[email protected]>
Co-authored-by: Kevin Coghlan <[email protected]>
Co-authored-by: Connor Clark <[email protected]>
Co-authored-by: Max Ortner <[email protected]>
Co-authored-by: Hugues Evrard <[email protected]>
Co-authored-by: Aemony <[email protected]>
Co-authored-by: Yan Pujante <[email protected]>
Co-authored-by: Cyao <[email protected]>
Co-authored-by: wermi <[email protected]>
Co-authored-by: Thomas Stehle <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests