Setup: Application has a JTabbedPane with custom Tab Header components. The custom header is a JPanel that includes a JTextField. Application has a theme toggle with FlatLAF and system themes.
Apparent Bug: If the component part of the tab header (i.e., the JTextField) is clicked before switching between Light and Dark Flat themes, a Null Pointer Exception is thrown.
Steps to Reproduce:
Set theme to light or dark flat
Click on the tab header component (i.e., the Text Field). Note: error is not thrown if clicking in the tab area that is NOT part of the component.
Switch theme to light or dark flat (whatever is not currently selected) -- NPE is thrown
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package flattabtest;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
/**
* Displays a TabbedPane with no content with a menu that can switch between
* different L&Fs. The tabbed pane header is a custom JPanel with a JTextField.
*
* Causes a Null Pointer Exception when doing the following:
* 1) Start with or switch to Dark/Light Flat Theme
* 2) Click on Tab Header -- in the COMPONENT (JTextField) area
* 3) Switch to Light/Dark Flat Theme
*/
public class FlatTabTest {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame app = new JFrame();
JPanel appPanel = new JPanel(new BorderLayout());
JTabbedPane tabbedPane = new JTabbedPane();
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Theme");
JRadioButtonMenuItem v_noThemeMenuItem = new JRadioButtonMenuItem("Default Theme", true);
v_noThemeMenuItem.addActionListener(new ThemeActionListener(UIManager.getCrossPlatformLookAndFeelClassName()));
JRadioButtonMenuItem v_defaultThemeMenuItem = new JRadioButtonMenuItem("System Theme", false);
v_defaultThemeMenuItem.addActionListener(new ThemeActionListener(UIManager.getSystemLookAndFeelClassName()));
JRadioButtonMenuItem v_lightFlatLafMenuItem = new JRadioButtonMenuItem("Light Flat Theme", false);
v_lightFlatLafMenuItem.addActionListener(new ThemeActionListener("com.formdev.flatlaf.FlatLightLaf"));
JRadioButtonMenuItem v_darkFlatLafMenuItem = new JRadioButtonMenuItem("Dark Flat Theme", false);
v_darkFlatLafMenuItem.addActionListener(new ThemeActionListener("com.formdev.flatlaf.FlatDarkLaf"));
menu.add(v_noThemeMenuItem);
menu.add(v_defaultThemeMenuItem);
menu.add(v_lightFlatLafMenuItem);
menu.add(v_darkFlatLafMenuItem);
ButtonGroup themeButtonGroup = new ButtonGroup();
themeButtonGroup.add(v_noThemeMenuItem);
themeButtonGroup.add(v_defaultThemeMenuItem);
themeButtonGroup.add(v_lightFlatLafMenuItem);
themeButtonGroup.add(v_darkFlatLafMenuItem);
menuBar.add(menu);
tabbedPane.add("TEST TAB", new JPanel());
tabbedPane.setTabComponentAt(0, new CustomHeader("TEST TAB"));
appPanel.add(tabbedPane, BorderLayout.CENTER);
appPanel.add(menuBar, BorderLayout.NORTH);
app.setContentPane(appPanel);
app.setSize(new Dimension(500, 500));
app.setLocationRelativeTo(null);
app.setVisible(true);
}
});
}
public static void themeChanged(String theme) {
if (UIManager.getLookAndFeel().getClass().getName().equals(theme)) {
return;
}
try {
UIManager.setLookAndFeel(theme);
updateLAFRecursively();
Logger.getLogger(FlatTabTest.class.getName()).log(Level.INFO, "Set Look and Feel {0}", theme);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
Logger.getLogger(FlatTabTest.class.getName()).log(Level.SEVERE, e.toString(), e);
}
}
/**
* Updates the component tree UI for all frames and their children.
*/
private static void updateLAFRecursively() {
for (Window window : Window.getWindows()) {
updateLAFRecursively(window);
}
}
/**
* Updates the component tree UI for all windows owned by the given window.
*
* @param window
*/
private static void updateLAFRecursively(Window window) {
for (Window childWindow : window.getOwnedWindows()) {
updateLAFRecursively(childWindow);
}
SwingUtilities.updateComponentTreeUI(window);
}
static class CustomHeader extends JPanel {
private JTextField label;
public CustomHeader(String title) {
setLayout(new BorderLayout());
label = new JTextField(title);
add(label, BorderLayout.CENTER);
}
}
static class ThemeActionListener implements ActionListener {
private String theme;
public ThemeActionListener(String theme) {
this.theme = theme;
}
@Override
public void actionPerformed(ActionEvent e) {
themeChanged(theme);
}
}
}
Stack trace:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.basic.BasicTabbedPaneUI.calculateTabHeight(BasicTabbedPaneUI.java:1736)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI.calculateTabHeight(FlatTabbedPaneUI.java:943)
at javax.swing.plaf.basic.BasicTabbedPaneUI.calculateMaxTabHeight(BasicTabbedPaneUI.java:1746)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI.calculateMaxTabHeight(FlatTabbedPaneUI.java:955)
at javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout.calculateTabRects(BasicTabbedPaneUI.java:2590)
at javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout.calculateLayoutInfo(BasicTabbedPaneUI.java:2516)
at javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout.layoutContainer(BasicTabbedPaneUI.java:2411)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI$FlatTabbedPaneLayout.layoutContainer(FlatTabbedPaneUI.java:3012)
at java.awt.Container.layout(Container.java:1513)
at java.awt.Container.doLayout(Container.java:1502)
at java.awt.Container.validateTree(Container.java:1698)
at java.awt.Container.validate(Container.java:1633)
at javax.swing.plaf.basic.BasicTabbedPaneUI.ensureCurrentLayout(BasicTabbedPaneUI.java:1451)
at javax.swing.plaf.basic.BasicTabbedPaneUI.getTabBounds(BasicTabbedPaneUI.java:1471)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI.repaintTab(FlatTabbedPaneUI.java:834)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI.access$6400(FlatTabbedPaneUI.java:181)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI$FlatSelectedTabRepainter.repaintSelectedTab(FlatTabbedPaneUI.java:3636)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI$FlatSelectedTabRepainter.repaintSelectedTabs(FlatTabbedPaneUI.java:3630)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI$FlatSelectedTabRepainter.propertyChange(FlatTabbedPaneUI.java:3614)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
at java.awt.KeyboardFocusManager.firePropertyChange(KeyboardFocusManager.java:1493)
at java.awt.KeyboardFocusManager.setGlobalPermanentFocusOwner(KeyboardFocusManager.java:780)
at java.awt.Component.removeNotify(Component.java:6999)
at java.awt.Container.removeNotify(Container.java:2823)
at javax.swing.JComponent.removeNotify(JComponent.java:4761)
at javax.swing.text.JTextComponent.removeNotify(JTextComponent.java:1602)
at java.awt.Container.removeNotify(Container.java:2807)
at javax.swing.JComponent.removeNotify(JComponent.java:4761)
at java.awt.Container.removeAll(Container.java:1300)
at javax.swing.plaf.basic.BasicTabbedPaneUI.uninstallTabContainer(BasicTabbedPaneUI.java:348)
at javax.swing.plaf.basic.BasicTabbedPaneUI.uninstallComponents(BasicTabbedPaneUI.java:332)
at com.formdev.flatlaf.ui.FlatTabbedPaneUI.uninstallComponents(FlatTabbedPaneUI.java:465)
at javax.swing.plaf.basic.BasicTabbedPaneUI.uninstallUI(BasicTabbedPaneUI.java:233)
at javax.swing.JComponent.uninstallUIAndProperties(JComponent.java:675)
at javax.swing.JComponent.setUI(JComponent.java:652)
at javax.swing.JTabbedPane.setUI(JTabbedPane.java:231)
at javax.swing.JTabbedPane.updateUI(JTabbedPane.java:247)
at javax.swing.SwingUtilities.updateComponentTreeUI0(SwingUtilities.java:1238)
at javax.swing.SwingUtilities.updateComponentTreeUI0(SwingUtilities.java:1253)
at javax.swing.SwingUtilities.updateComponentTreeUI0(SwingUtilities.java:1253)
at javax.swing.SwingUtilities.updateComponentTreeUI0(SwingUtilities.java:1253)
at javax.swing.SwingUtilities.updateComponentTreeUI0(SwingUtilities.java:1253)
at javax.swing.SwingUtilities.updateComponentTreeUI(SwingUtilities.java:1229)
Flatlaf Version: 3.2.1 (also tested with 3.1.1)
Setup: Application has a JTabbedPane with custom Tab Header components. The custom header is a JPanel that includes a JTextField. Application has a theme toggle with FlatLAF and system themes.
Apparent Bug: If the component part of the tab header (i.e., the JTextField) is clicked before switching between Light and Dark Flat themes, a Null Pointer Exception is thrown.
Steps to Reproduce:
Code sample that produces the error: FlatLaf_NPE_Tabbed_Pane_Example.txt
Stack trace: