imgui icon indicating copy to clipboard operation
imgui copied to clipboard

Vertical Tab

Open devkaiwang opened this issue 5 years ago • 7 comments

How can we implement vertical tab? Blender's panel looks great (please see the icon tabs on the very left) - image

devkaiwang avatar Feb 19 '20 03:02 devkaiwang

Current tab bar implementation does not support vertical tabs. It is possible to implement this, but you would have to do it manually, using BeginChild()/EndChild(), Button() and ImDrawList for rendering of custom shapes.

  1. Render a dark rect as a background for tab selector buttons.
  2. Render all buttons in a column using SetCursorPos()
  3. Render a child with contents of selected tab on the right.

This is overly simplified list, but you should get the general idea.

rokups avatar Feb 19 '20 07:02 rokups

As Rokas mentioned you can reimplement a simplified version of a vertical tab yourself. In particular the style you showed in that picture, using icon, is also much easier to implement manually than "rotated tabs":

image

Either way it be nice if this was also supported vertical tab bars. I didn't implement vertical tabs yet, one of the reason being that I was expecting to use rotated text which we don't have yet (aiming for a text refactor in the upcoming semester).

We could imagine that the BeginTabBar() api could allow two distinct options for vertical tabs: rotated tabs with rotated text, and stacked tabs with fixed-width text like in your picture.

ocornut avatar Feb 23 '20 20:02 ocornut

FYI (copied from #705)

The code in #3067 has another function to draw 90 rotated text: https://github.com/mahilab/mahi-gui/blob/master/src/Mahi/Gui/imgui_plot.cpp

/// Draws vertical text. The position is the bottom left of the text rect.
inline void AddTextVertical(ImDrawList* DrawList, const char *text, ImVec2 pos, ImU32 text_color) {
    pos.x = IM_ROUND(pos.x);
    pos.y = IM_ROUND(pos.y);
    ImFont *font = GImGui->Font;
    const ImFontGlyph *glyph;
    char c;
    ImGuiContext& g = *GImGui;
    ImVec2 text_size = CalcTextSize(text);
    while ((c = *text++)) {
        glyph = font->FindGlyph(c);
        if (!glyph) continue;

        DrawList->PrimReserve(6, 4);
        DrawList->PrimQuadUV(
                pos + ImVec2(glyph->Y0, -glyph->X0),
                pos + ImVec2(glyph->Y0, -glyph->X1),
                pos + ImVec2(glyph->Y1, -glyph->X1),
                pos + ImVec2(glyph->Y1, -glyph->X0),

                ImVec2(glyph->U0, glyph->V0),
                ImVec2(glyph->U1, glyph->V0),
                ImVec2(glyph->U1, glyph->V1),
                ImVec2(glyph->U0, glyph->V1),
                    text_color);
        pos.y -= glyph->AdvanceX;
    }
}

If you don't need clipping, wrapping etc. it's fairly easy to write it. We will aim to support full-featured rotated text in a new low-level text rendering API this year.

(After you made this post I started trying to implement vertical tab and ended up with the conclusion that it would be more natural to implement once we finish work on the WorkRect system to "claim" space from any direction, instead of the current top-to-bottom focused system.)

ocornut avatar Mar 22 '20 12:03 ocornut

We will aim to support full-featured rotated text in a new low-level text rendering API this year.

Is there any progress?

Nitr0-G avatar Dec 11 '23 00:12 Nitr0-G

I also need this

Displee avatar Jan 14 '24 20:01 Displee

I was able to replicate this with a child with table with 1 column for the buttons, and a child window for the tab content next to it, which looks like this: afbeelding afbeelding

I use Java bindings for ImGui, here's the Kotlin code:

fun show() = ImGuiExt.groupWithoutSpacing {
        if (ImGui.beginChild("TabButtons", 40F, 0F, false, WINDOW_UNDECORATED or ImGuiWindowFlags.NoBackground)) {
            ImGui.sameLine(8F)

            val tabCount = 5
            if (ImGui.beginTable("TabButtonsTable", 1, ImGuiTableFlags.None, 32F, 32F)) {
                repeat(tabCount) {
                    ImGui.tableNextRow()
                    ImGui.tableNextColumn()
                    ImGui.pushID("EditorTab$it")
                    val selected = selectedTab == it
                    if (selected) {
                        ImGui.pushStyleColor(ImGuiCol.Button, buttonActiveBackground)
                        ImGui.pushStyleColor(ImGuiCol.Border, buttonActiveBorderColor)
                    } else {
                        ImGui.pushStyleColor(ImGuiCol.Button, buttonBackground)
                        ImGui.pushStyleColor(ImGuiCol.Border, buttonBorderColor)
                    }
                    ImGui.pushStyleVar(ImGuiStyleVar.FrameBorderSize, 2F)
                    if (ImGui.button("Tab 1", 32F, 32F)) {
                        selectedTab = it
                    }
                    ImGui.popStyleVar()
                    ImGui.popStyleColor(2)
                    ImGui.popID()
                }
                ImGui.endTable()
            }
            ImGui.endChild()
        }

        ImGui.sameLine()

        ImGuiExt.groupWithSpacing {
            ImGui.pushStyleColor(ImGuiCol.ChildBg, tabContentBackgroundColor)
            ImGui.pushStyleColor(ImGuiCol.Border, tabContentBackgroundColor)
            if (ImGui.beginChild("TabContent", WIDTH - 40F, 0F, true, WINDOW_UNDECORATED)) {
                ImGui.popStyleColor(2)
                when (selectedTab) {
                    0 -> {
                        MapTilePropertyTable.show()
                    }
                    1 -> {
                        ImGui.text("Tab 2")
                    }
                }
                ImGui.endChild()
            }
        }
    }

Some of my extension functions :)

    fun groupWithSpacing(x: Float = 8F, y: Float = 4F, block: () -> Unit) {
        ImGui.pushStyleVar(ImGuiStyleVar.ItemSpacing, x, y)
        ImGui.beginGroup()

        block()

        ImGui.popStyleVar()
        ImGui.endGroup()
    }

    fun groupWithoutSpacing(block: () -> Unit) {
        ImGui.pushStyleVar(ImGuiStyleVar.ItemSpacing, 0F, 0F)
        ImGui.beginGroup()

        block()

        ImGui.popStyleVar()
        ImGui.endGroup()
    }

Displee avatar Jan 20 '24 22:01 Displee