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

Combo box popup sizing incorrect after first popup

XMLWordPrintable

        FULL PRODUCT VERSION :
        1.5.0_09 and 1.6.0_02

        ADDITIONAL OS VERSION INFORMATION :
        Microsoft Windows XP [Version 5.1.2600]

        A DESCRIPTION OF THE PROBLEM :
        When a JComboBox is instantiated with a large model (typically from a database), the popup appears correctly at first with the default 8 row size, but with subsequent clicks on the combo arrow icon, the popup panel is truncated to 1-2 lines with a vertical scrollbar. The problem only appeared after migrating to 1.5 (and it still happens in 1.6) in more than one site where I've used Swing. The combo behaves normally otherwise.



        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Difficult to specify without giving a very long production screen class. It occurs when the combo's popup overlaps another panel, for example a center panel, in a complex nested panel layout.


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        The combo should always have the specified depth (default = 8 rows).
        ACTUAL -
        Popup is 1-2 rows high and hard to use as a result.

        REPRODUCIBILITY :
        This bug can be reproduced often.

        CUSTOMER SUBMITTED WORKAROUND :
        The solution lies in replacing the UI delegate BasicComboBoxUI with a sub-class which over-rides createPopup() in order to instantiate a new BasicComboPopup instance which anonymously over-rides the computePopupBounds() method. Then the correct bounds can be calculated which are always respected when new the BasicComboBoxUI subclass is installed using setUI() on the combo.

        A positive side effect of the "fix" is that it also allows the width of the popup to be set to match the string width of the combo's model contents, rather than being limited to the same width as the combo control itself. This means the user can view the popup content properly without expanding the footprint of the combo itself. A helper method uses FontMetrics to calculate the maximum width of the model's contents for sizing purposes.

        Since BasicComboPopup's constructor argument provides a reference to the host combo, the linkage is there to make this a top level API feature in Swing, which would be very popular. Admittedly, this is an enhancement, but it seemed worth mentioning in the context of the bug.

        One other small point - if you are using an enhanced UI (eg JTattoo), you will need to extend the equivalent combo box UI delegate (eg JTattoo's McWinComboBoxUI) rather than BasicComboBoxUI to preserve custom rendering features.

            private class ReliableComboBoxUI extends BasicComboBoxUI { // replace BasicComboBoxUI with custom combo box UI if needed
             protected JComboBox _combo ;
                private FontMetrics _fm ;
             private int _maxListWidth = 20 ; // min size for sanity

             public ReliableComboBoxUI(JComboBox combo) {
             super() ;
             _combo = combo ;
             }

             public int getMaxListWidth() { return _maxListWidth ; }
            
             protected void setMaximumTextWidth() {
                 if (_combo.getFont()==null || _combo.getFontMetrics(_combo.getFont())==null) { return ; }
             _fm = _combo.getFontMetrics(_combo.getFont()) ;
             ComboBoxModel items = _combo.getModel();
                 for (int i = 0; i < items.getSize(); i++) {
                        String str = items.getElementAt(i).toString().trim();
                        _maxListWidth = Math.max(_maxListWidth, _fm.stringWidth(str)) ;
                        _maxListWidth = Math.max(_maxListWidth, _combo.getWidth()) ;
                        System.out.println("item="+_fm.stringWidth(str) + " combo=" + _combo.getWidth() + " max=" + _maxListWidth);
             }
                }
             protected ComboPopup createPopup() {
             BasicComboPopup popup = new BasicComboPopup(_combo) {
             protected Rectangle computePopupBounds(int px, int py, int pw, int ph) {
             Rectangle rect = super.computePopupBounds(px, py, pw, ph) ;
             Rectangle rect2 = new Rectangle(rect) ;
             ReliableComboBoxUI.this.setMaximumTextWidth();
             rect2.setSize(
             Math.max(ReliableComboBoxUI.this.getMaxListWidth()+25,pw),
             Math.min(_combo.getModel().getSize()* 18 - 5, _combo.getMaximumRowCount() * 18 - 8)
             ) ;
             return rect2 ;
             }
             };
             return popup ;
             }
            } // ReliableComboBoxUI class ends

        This class needs to be registered in the UIManager properties, so it will automatically be called when instantiating combo boxes, or set on the instance directly.
                Object[] model = {"ONE", "TWO", "THREETHREETHREETHREE" };
                JComboBox combo = new JComboBox(model) ;
                combo.setUI(new ReliableComboBoxUI(this)) ;

        The combo will now resize the popup width to suit the model's contents.

        Release Regression From : 1.4.2
        The above release value was the last known release where this
        bug was not reproducible. Since then there has been a regression.

              alexp Alexander Potochkin (Inactive)
              ndcosta Nelson Dcosta (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: