Click here to Skip to main content
15,886,199 members
Articles / Desktop Programming / Swing
Tip/Trick

Enhanced JTabbedPane

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
2 Sep 2014CPOL1 min read 14.5K   2   4
This tip provides code for an enhanced version of the JTabbedPane which allows the developer to add toolbar buttons to the beginning or end of the tab set.

Introduction

I have decided that in the interest of modern design and encouraging a certain workflow, a project on which I am working would be best presented with a minimalist design consisting of only tabs in the main frame. However, after much searching, I was only able to find discouraging advice on the ability to add toolbar buttons to the tab set. The key is that these buttons should be just buttons. It's easy enough to add a 16x16 image as a tab header, but the desired effect is not for the Save button, for example, to have content associated with it. Instead, the Save button should just be there waiting to be clicked, and always be visible regardless of whether the array of tabs has scrolled. These classes provide the ability to add one or more buttons to either the leading or trailing side (Locale dependent) of the tab set.

Using the Code

The main class here is ETabbedPane which derives from JPanel. It contains a JTabbedPane which fills the widget and two toolbars, one each at the northwest and northeast corner. To interact with the tabs, fetch them using getJTabbedPane() and interact with them as you normally would. Rather than referring to the toolbars as left and right, they are referenced as 'leading' and 'trailing' to facilitate internationalization.

Here is the source:

Java
import java.awt.ComponentOrientation;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import javax.swing.SpringLayout;

public class ETabbedPane extends JPanel {
    public enum ButtonSide {
        LEADING,
        TRAILING;
    }
    /**
     *
     */
    private static final long serialVersionUID = -2090869893562246860L;
    private JTabbedPane tabs;
    private JToolBar leadingButtons;
    private JToolBar trailingButtons;
    //private JPanel dropDown;
    private OffsetTabbedPaneUI tabUI;
    
    public ETabbedPane(ComponentOrientation orient) {
        SpringLayout sl = new SpringLayout();
        this.setLayout(sl);
        
        this.leadingButtons = new JToolBar();
        this.leadingButtons.setBorderPainted(false);
        this.leadingButtons.setFloatable(false);
        this.leadingButtons.setOpaque(false);
        this.leadingButtons.setComponentOrientation(orient);
        this.leadingButtons.setLayout(new FlowLayout(FlowLayout.LEADING, 1, 1));
        sl.putConstraint(SpringLayout.NORTH, this.leadingButtons, 0, SpringLayout.NORTH, this);
        String side = ((orient == ComponentOrientation.RIGHT_TO_LEFT) ? SpringLayout.EAST : SpringLayout.WEST);
        sl.putConstraint(side, this.leadingButtons, 0, side, this);
        this.add(leadingButtons);
        
        this.trailingButtons = new JToolBar();
        this.trailingButtons.setBorderPainted(false);
        this.trailingButtons.setFloatable(false);
        this.trailingButtons.setOpaque(false);
        this.trailingButtons.setComponentOrientation(orient);
        this.trailingButtons.setLayout(new FlowLayout(FlowLayout.LEADING, 1, 1));
        sl.putConstraint(SpringLayout.NORTH, this.trailingButtons, 0, SpringLayout.NORTH, this);
        side = ((orient == ComponentOrientation.RIGHT_TO_LEFT) ? SpringLayout.WEST : SpringLayout.EAST);
        sl.putConstraint(side, this.trailingButtons, 0, side, this);
        this.add(trailingButtons);
        
        this.tabs = new JTabbedPane();
        this.tabs.setComponentOrientation(orient);
        sl.putConstraint(SpringLayout.NORTH, this.tabs, 0, SpringLayout.NORTH, this);
        sl.putConstraint(SpringLayout.SOUTH, this.tabs, 0, SpringLayout.SOUTH, this);
        sl.putConstraint(SpringLayout.WEST, this.tabs, 0, SpringLayout.WEST, this);
        sl.putConstraint(SpringLayout.EAST, this.tabs, 0, SpringLayout.EAST, this);
        tabUI = new OffsetTabbedPaneUI();
        this.tabs.setUI(tabUI);
        this.add(tabs);
        
        this.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                if (tabs.getTabCount() > 0) {
                    tabs.getSelectedComponent().requestFocusInWindow();
                }
            }
        });
    }

    /**
     * @return the tabs
     */
    public JTabbedPane getJTabbedPane() {
        return tabs;
    }
    
    public void addButton(JButton button, ButtonSide side) {
        button.setBorderPainted(false);
        button.setFocusable(false);
        button.setMargin(new Insets(1, 1, 1, 1));
        ((side == ButtonSide.LEADING) ? this.leadingButtons : this.trailingButtons).add(button);
        this.tabUI.setMinHeight(
                Math.max(this.leadingButtons.getPreferredSize().height,
                         this.trailingButtons.getPreferredSize().height));
        this.tabUI.setLeadingOffset(this.leadingButtons.getPreferredSize().width);
        this.tabUI.setTrailingOffset(this.trailingButtons.getPreferredSize().width);
        this.validate();
    }
}

In order for the widget to work as intended, you need an additional class which overrides the default L&F. Source:

Java
import java.awt.Insets;

import javax.swing.plaf.basic.BasicTabbedPaneUI;

public class OffsetTabbedPaneUI extends BasicTabbedPaneUI {
    private int leadingOffset = 0;
    private int minHeight = 0;
    private int trailingOffset;
    
    public OffsetTabbedPaneUI() {
        super();
    }

    /* (non-Javadoc)
     * @see javax.swing.plaf.basic.BasicTabbedPaneUI#calculateTabHeight(int, int, int)
     */
    @Override
    protected int calculateTabHeight(int tabPlacement, int tabIndex,
            int fontHeight) {
        return Math.max(super.calculateTabHeight(tabPlacement, tabIndex, fontHeight), this.minHeight);
    }

    /* (non-Javadoc)
     * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabAreaInsets(int)
     */
    @Override
    protected Insets getTabAreaInsets(int tabPlacement) {
        // ignores tab placement for now
        return new Insets(0, this.leadingOffset, 0, this.trailingOffset);
    }

    /**
     * @param offset the offset to set
     */
    public void setLeadingOffset(int offset) {
        this.leadingOffset = offset;
    }

    /**
     * @param minHeight the minHeight to set
     */
    public void setMinHeight(int minHeight) {
        this.minHeight = minHeight;
    }

    public void setTrailingOffset(int offset) {
        this.trailingOffset = offset;
    }
}

Here is an example application demonstrating several tabs and buttons on the leading and trailing ends (running under Kubuntu):

Image 1

History

  • 2014-09-03 0603 CDT: Added screenshot
  • Previously: This is the first version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThis does not work using JTabbedPane.SCROLL_TAB_LAYOUT Pin
Steven197313-Sep-16 5:55
Steven197313-Sep-16 5:55 
Hi,

I tried to use this example when using scrolling tabs, but the buttons draw over the top of the scroll left and right buttons.

How could I adjust the code to get this to work when using the tab set policy: JTabbedPane.SCROLL_TAB_LAYOUT

Kind regards

Steve
AnswerRe: This does not work using JTabbedPane.SCROLL_TAB_LAYOUT Pin
kjburns198019-Oct-16 18:56
kjburns198019-Oct-16 18:56 
QuestionA screenshot of finished thing may have been nice Pin
Sacha Barber3-Sep-14 0:20
Sacha Barber3-Sep-14 0:20 
AnswerRe: A screenshot of finished thing may have been nice Pin
kjburns19803-Sep-14 1:39
kjburns19803-Sep-14 1:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.