Zezombye
Zezombye

Reputation: 333

JTabbedPane: How to order tabs from top to bottom, instead of bottom to top?

In a JTabbedPane, the order of the tabs is from bottom to top: enter image description here

I would like it to be top to bottom, meaning tab0 would be at the top row, and tab60 at the bottom row.

Code:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;

public class Class1 {

    public static JTabbedPane jtp;

    public static void main(String[] args) {
        JFrame window = new JFrame();
        window.setTitle("Parent frame");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setSize(600, 400);
        window.setLocationRelativeTo(null);

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }

        jtp = new JTabbedPane();
        window.add(jtp);

        /*WindowsTabbedPaneUI btpui = new WindowsTabbedPaneUI() {
            @Override protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
                int tabCount = tabPane.getTabCount();

                Rectangle iconRect = new Rectangle(),
                          textRect = new Rectangle();
                Rectangle clipRect = g.getClipBounds();

                // Paint tabRuns of tabs from front to back
                for (int i = 0; i < runCount; i++) {
                    int start = tabRuns[i];
                    int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
                    int end = (next != 0? next - 1: tabCount - 1);
                    for (int j = start; j <= end; j++) {
                        if (j != selectedIndex && rects[j].intersects(clipRect)) {
                            paintTab(g, tabPlacement, rects, j, iconRect, textRect);
                        }
                    }
                }

                // Paint selected tab if its in the front run
                // since it may overlap other tabs
                if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
                    paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
                }
            }
        };
        jtp.setUI(btpui);*/

        //jtp.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

        for (int i = 0; i <= 60; i++) {
            jtp.addTab("tab"+i, new JPanel());
        }
        window.setVisible(true);
    }
}

I have tried overriding the paintTabArea() method in WindowsTabbedPaneUI to paint from top to bottom, but it doesn't seem to do anything.

I have found 2 workarounds:

Is there any way to do this without glitches? I guess I have to override some method from BasicTabbedPaneUI, but I can't find which one.

Upvotes: 0

Views: 465

Answers (2)

Orest Savchak
Orest Savchak

Reputation: 4569

You could achieve it easier by extending BasicTabbedPaneUI and overriding createLayoutManager, and in there return TabbedPaneLayout that override a necessary method.

Upvotes: -1

Zezombye
Zezombye

Reputation: 333

I finally found a solution.

As @Joe Coder said, you have to override the calculateTabRects() method. However, this method is in an inner class. There is no way to extend the BasicTabbedPaneUI, or the inner class, because there are just too many protected and private fields. The solution is to recreate the relevant classes by copy-pasting all their code.

The source code is from here.

The relevant modification is in the calculateTabRects() method, more specifically in that for loop:

// Step through runs from back to front to calculate
// tab y locations and to pad runs appropriately
for (i = runCount - 1; i >= 0; i--) {
    //...
}

Simply iterating in reverse does the trick: for (i = 0; i < runCount; i++) {...}

Now, if you copy-paste the code of the BasicTabbedPaneUI, you'll find that you need to also copy-paste LazyActionMap and BasicGraphicsUtils.

For convenience, here is all the code you need to copy-paste. Make sure not to override the package, if it's not in the package javax.swing.plaf.basic it won't work.

FixedTabbedPaneUI.java: https://pastebin.com/W8RBD4vg

LazyActionMap2.java: https://pastebin.com/RKtDgJB5

BasicGraphicsUtils2.java: https://pastebin.com/Au4hcSyp

But, in my case, I need a WindowsTabbedPaneUI. So here's WindowsTabbedPaneUI2.java: https://pastebin.com/89UedgbQ

..XPStyle2.java: https://pastebin.com/xCsm8DZc

..and AnimationController2.java: https://pastebin.com/7Pm0s5eG

The OOP hell finishes here, but in the current state, the lines between "runs" (tab lines) disappear: enter image description here

The solution is to put the following after choosing the UI with UIManager.setLookAndFeel():

    UIManager.getDefaults().put("TabbedPane.tabRunOverlay", 0);
    UIManager.getDefaults().put("TabbedPane.focus", new Color(0, 0, 0, 0));

The result:

enter image description here

Upvotes: 2

Related Questions