Click here to Skip to main content
12,447,275 members (60,010 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

5.3K views
2 bookmarked
Posted

Enhanced JTabbedPane

, 2 Sep 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

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:

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):

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)

Share

About the Author

kjburns1980
Engineer
United States United States
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionA screenshot of finished thing may have been nice Pin
Sacha Barber3-Sep-14 0:20
mvpSacha Barber3-Sep-14 0:20 
AnswerRe: A screenshot of finished thing may have been nice Pin
kjburns19803-Sep-14 1:39
memberkjburns19803-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.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160811.3 | Last Updated 3 Sep 2014
Article Copyright 2014 by kjburns1980
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid