Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8215396

JTabbedPane preferred size calculation is wrong for SCROLL_TAB_LAYOUT

XMLWordPrintable

    • b14
    • x86_64
    • windows_10

        ADDITIONAL SYSTEM INFORMATION :
        Windows 10 Pro, 64 bit
        Tested on:
        JDK 1.8.0_192, 64 bit
        JDK 1.9.0_01, 64 bit
        JDK 1.10.0_02, 64 bit
        JDK 1.11.0_01, 64 bit

        A DESCRIPTION OF THE PROBLEM :
        I would like to reopen "JDK-7151452 : JTabbedPane preferred size calculation
        is wrong for SCROLL_TAB_LAYOUT"
        (https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7151452).
        I am still experiencing the same problem, so as suggested in a comment to the
        original report, I am reopening it under a new version. It was suggested
        to specify 9 as the affected version, but that's not possible anymore, so I
        used 10, but I experience the same problem in versions 8, 9, and 11.

        Also, I found where the problem is in the code and how to fix it.
        Namely, the problem is in the following methods of the
        BasicTabbedPaneUI.TabbedPaneScrollLayout inner class:

            protected int preferredTabAreaHeight(int tabPlacement, int width) {
                return calculateMaxTabHeight(tabPlacement);
            }

            protected int preferredTabAreaWidth(int tabPlacement, int height) {
                return calculateMaxTabWidth(tabPlacement);
            }

        These methods are defined in the superclass, BasicTabbedPaneUI.TabbedPaneLayout,
        and overridden here. While in the superclass these methods take into account the
        tab area insents, the overridden methods don't, and so they break the implicit
        contract and yield a number that is too small.

        Therefore, the solution is to add the correct insets in the overridden methods
        to the result before returning it. There are two ways of doing this. One is to
        do it manually in these methods, like:

            protected int preferredTabAreaHeight(int tabPlacement, int width) {
                Insets tabAreaInsets = getTabAreaInsets(tabPlacements);
                return calculateMaxTabHeight(tabPlacement) + tabAreaInsets.top + tabAreaInsets.bottom;
            }

            protected int preferredTabAreaWidth(int tabPlacement, int height) {
                Insets tabAreaInsets = getTabAreaInsets(tabPlacements);
                return calculateMaxTabWidth(tabPlacement) + tabAreaInsets.left + tabAreaInsets.right;
            }

        The other solution is to use calculateTabAreaHeight() and
        calculateTabAreaWidth(), respectively:

            protected int preferredTabAreaHeight(int tabPlacement, int width) {
                return calculateTabAreaHeight(tabPlacement, 1, calculateMaxTabHeight(tabPlacement));
            }

            protected int preferredTabAreaWidth(int tabPlacement, int height) {
                return calculateTabAreaWidth(tabPlacement, 1, calculateMaxTabWidth(tabPlacement));
            }

        I find the second solution cleaner since we are reusing the existing methods for
        calculating the size instead of repeating the logic. These methods are also used
        for calculating the size for the WRAP_TAB_LAYOUT, only that there the number of
        rows/columns need to be calculated first, whereas here we can simply pass 1.

        On the other hand, the first solution might have some slight performance benefit,
        but maybe that's not significant.

        Anyway, I provide both solutions so you can choose the one you prefer.

        Finally, I wanted to add that while the original issue is only about preferred size, both the
        problem and the fix apply to minimum size as well.

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Run the original example (copied here for convenience), and compare the two frames.


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Both frames should have the same size and show the full content.
        ACTUAL -
        Only the frame using WRAP_TAB_LAYOUT shows the full content, whereas the height of the one using SCROLL_TAB_LAYOUT is too small and thus does not show the full content.

        ---------- BEGIN SOURCE ----------
        // This is copied from the original issue for convenience
        package tabprob;

        import java.awt.*;
        import javax.swing.*;

        public class TabProb extends JFrame {
          class FixLayout implements LayoutManager {
            @Override
            public void layoutContainer(Container C) {
              Insets in = C.getInsets();
              int w = 200 - in.left - in.right;
              int h = 100 - in.top - in.bottom;
              C.getComponents()[0].setBounds(in.top, in.left, w, h);
            }
            @Override
            public Dimension minimumLayoutSize(Container C) {
              return new Dimension(200, 100);
            }
            @Override
            public Dimension preferredLayoutSize(Container C) {
              return new Dimension(200, 100);
            }
            @Override
            public void removeLayoutComponent(Component c) {
            }
            @Override
            public void addLayoutComponent(String s, Component c) {
            }
          }

          public TabProb(int layoutPolicy) {
            JTabbedPane tabpanel = new JTabbedPane();
            tabpanel.setTabPlacement(JTabbedPane.TOP);
            tabpanel.setTabLayoutPolicy(layoutPolicy);
            JPanel panel = new JPanel(new FixLayout());
            JLabel label = new JLabel("TEST");
            label.setBorder(BorderFactory.createLineBorder(Color.green, 3));
            panel.add(label);
            tabpanel.add("TEST", panel);
            add(tabpanel, BorderLayout.CENTER);
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
          }

          public static void main(String[] args) {
            try {
              //UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
              //UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
              UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
            } catch(Exception e) {
            }
            SwingUtilities.invokeLater(new Runnable() {
              @Override
              public void run() {
                TabProb tb1 = new TabProb(JTabbedPane.SCROLL_TAB_LAYOUT);
                tb1.pack();
                tb1.setVisible(true);
                
                TabProb tb2 = new TabProb(JTabbedPane.WRAP_TAB_LAYOUT);
                tb2.pack();
                tb2.setVisible(true);
              }
            });
          }
        }
        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        From the original report:
        1) Set a large enough inset for the contentpanel, or
        2) Set TabbedPane.tabAreaInsets to Insets(0,0,0,0)

        FREQUENCY : always


              psadhukhan Prasanta Sadhukhan
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: