TabbedComponent with vertical or horizontal tabs

I'm using a TabbedComponent in an admin tool.  Lots of seldom used settings, but required to expose them.  The problem is there are now enough admin tabs that the names in the tabs are unreadable:

I tried to switch to left-hand tabs instead, but while it does place the tab bar on the left as expected, the names are also rotated 90 degrees CCW, meaning they take up the same amount of vertical real estate, and thus isn't a solution for me in this case.

I've never really dug into the JUCE code itself before now.  If I figured out how to have left or right hand stacked tabs with horizontal text, is this a patch that I could submit to JUCE?

Related question:  has this code (tab orientation or vertically stacked tabs) changed in the upcoming version of JUCE?  Should I wait before digging into this?

No, we've got no changes planned for that class, and sure, we're always open to good suggestions for improvements!

Stayed up overnight trying to figure it out.  I have modifications to

  • juce_TabbedButtonBar.cpp
  • juce_TabbedButtonBar.h
  • juce_TabbedComponent.cpp
  • juce_LookAndFeel_V2.cpp
  • juce_LookAndFeel_V3.cpp

Took me a while to figure out that parts of juce_LookAndFeel_V2.cpp is used even when using V3 look-and-feel!

My idea was to add a new orientation in juce_TabbedButtonBar.h.  I called it TabsAtLeftHorizontalText.  Then I searched for anywhere in the code where TabsAtLeft or TabsAtTop was referenced.  I now have the left-hand tabs with horizontal text, but I haven't figured out where the size of tabs is determined.

This is what I have so far:  http://www.ccoderun.ca/tmp/tabs_against_3.2.0-234.diff

Could use some pointers or hints.  What I was hoping for is wide though skinny tabs on the left, so a large number of tabs can exist yet easily read and selected.  TIA.

What I did was make PreferencesPanel private variables protected :

protected:
    //==============================================================================
    String currentPageName;
    ScopedPointer <Component> currentPage;
    OwnedArray<DrawableButton> buttons;
    int buttonSize;
    
private:

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreferencesPanel)

and in my derived class’ resized method

//////////////////////////////////////////////////////////////////////////////////////
//
// Here we change the Prefs buttons to go down the left side instead of along the top

void CPlayerPrefsDlg::resized()
{
    for (int i = 0; i < buttons.size(); ++i)
        buttons.getUnchecked(i)->setBounds (0, i * buttonSize, buttonSize, buttonSize);

    if (currentPage != nullptr)
        currentPage->setBounds (getLocalBounds().withLeft (buttonSize));
}

Which will probably make Jules angry ;^)

Rail

Not angry, just disappointed..

Does it actually need any changes to TabbedButtonBar to implement it? I'd have thought that a custom LookAndFeel would be enough to lay-out and draw the labels horizontally like that?

I am trying like this in 2023 but it is not work (text still written vertical with TabAtRight)

class CustomTabbedLookAndFeel : public juce::LookAndFeel_V4
{
    
public:
    void drawTabButtonText(juce::TabBarButton& button, juce::Graphics& g, bool isMouseOver, bool isMouseDown) override
    {
        // Get the bounds of the button
        juce::Rectangle<int> bounds = button.getBounds();
        
        // Set the font and color for the text
        g.setColour(juce::Colours::black);
        g.setFont(juce::Font(bounds.getHeight() * 0.6f));
        
        // Get the text and orientation
        const juce::String& text = button.getButtonText();
        juce::TabbedButtonBar::Orientation orientation = button.getTabbedButtonBar().getOrientation();
        
        // Check if the orientation is vertical (TabsAtRight or TabsAtLeft)
        if (orientation == juce::TabbedButtonBar::TabsAtRight || orientation == juce::TabbedButtonBar::TabsAtLeft)
        {
            // Calculate the position and size of the text rectangle
            int textX = (bounds.getWidth() - bounds.getHeight()) / 2;
            int textY = (bounds.getHeight() - bounds.getWidth()) / 2;
            int textWidth = bounds.getHeight();
            int textHeight = bounds.getWidth();
            
            // Draw the text horizontally within the calculated rectangle
            g.drawFittedText(text, textX, textY, textWidth, textHeight, juce::Justification::centred, 1);
        }
        else
        {
            // Draw the text normally for horizontal tabs
            g.drawText(text, bounds, juce::Justification::centred, true);
        }
    }
};