Skip to main content

Customize Renderer of JComboBox in Synth Look And Feel

Posted by xuanyun on February 19, 2009 at 12:53 AM PST

Customizing the JComboBox style in Synth look and feel is a little complex,
I can write a long article for it, but I don't want to be too tired, so please
let me share my experience piece by piece. Today let's see how to customize
the cell renderer for drop-down list in JComboBox.

Can we just do nothing and let the Synth look and feel to handle it in default
way? Unfortunately it does not work, the background of the drop-down list will
be painted with the JList's style, that's correct, but the current selected
item will not be highlighted, that's strange. The TEXT_FOREGROUND and TEXT_BACKGROUND
of the SELECTED state of JList style is not used in the drop-down list, that's
really depressing. See the effect below:

combo_rdr_1.png

Yes we can customize the renderer in our Java code, like this:


combox.setRenderer(new ListCellRenderer() {
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        final JLabel renderer = new JLabel(value.toString());
        if (isSelected) {
            renderer.setOpaque(true);
            renderer.setForeground(Color.BLUE);
            renderer.setBackground(Color.LIGHT_GRAY);
        }
        return renderer;
    }
});

It is simple and works, the selected item will be highlighted with blue text
color and light gray background. But it is not a perfect solution, since we
are developing a look and feel, it is better to include the effect into our
look and feel, so the end user can use it more easily. Integrate the java code
above into Synth look and feel is possible but kind of ugly, we don't need to
do that, since Synth provides another approach.

If you go through the source code of SynthComboBoxUI class, you will understand
why the selected item is not highlighted and then find out the solution. You
need to create an exclusive style for the ComboBox list cell renderer component,
which has the name "ComboBox.listRenderer". So in
the Synth XML, you can write these:

<!-- The style for the list renderer of JComboBox -->
<style id="ComboBox List Renderer">
    <opaque value="true"/>
    <state value="ENABLED">
    <color type="TEXT_FOREGROUND" value="#000000"/>
    </state>
    <state value="SELECTED">
    <color type="TEXT_FOREGROUND" value="#CC6600"/>
    <color type="TEXT_BACKGROUND" value="#FFEEDD"/>
    </state>
</style>
<bind style="ComboBox List Renderer" type="name" key="ComboBox.listRenderer" />

That's it, this style will be bound to the renderer of ComboBox. It is almost
finished, but not yet, we still need to set the property ComboBox.rendererUseListColors
to false, since its default value is true.

<!-- The style for combo box component. -->
<style id="Combo Box">
    ......
    <property key="ComboBox.rendererUseListColors" type="boolean" value="false"/>
    ......
</style>
<bind style="Combo Box" type="region" key="ComboBox" />

Why we need to set the ComboBox.rendererUseListColors to false?
Ok, let's check the description of this property, searching in Synth
Component Properties Specification
...... Oops, not found? Yes, it is an
undocumented property, but in the source code of SynthComboBoxUI, it is for
sure in used, and it is very important. Actually it is similar with another
property in JList: List.rendererUseListColors

Property Expected Type Default Value Description
List.rendererUseListColors Boolean true If true the renderers state is not updated, and the text colors come from
JList's getSelectionBackground and getSelectionForeground methods. If false,
the renderer's state is updated and the colors will instead come from the
Style.

Now we know, if ComboBox.rendererUseListColors set to true
(by default), it will ignore the foreground and background color defined in
"ComboBox List Renderer" style, so we have to set it to false.

It is done now, let's check the result:

combo_rdr_2.png

Good looking! Isn't it? The selected item is highlighted! And...the border
of the list is a little differnet...there is shadow under the drop-down list?
Well, they are new features in EaSynth
look and feel
V1.05, I will introduce the technical details about them in
the futrue.

Related Topics >>

Comments

Hi yilile, as I mentioned in this article, the preferred approach to make sure the list item be highlighted is: (1) define an opaque ComboBox.listRenderer (2) set the ComboBox.rendererUseListColors property to false. We ever used the setOpaque() approach to do this, I have to say it is not a good idea, obviously there are some places inside the JRE will try to set the opaque of renderer back to false, so the time you call setOpaque() will be the key, calling it at a correct time, you will see it works well, otherwise your settings will be covered. If you are interested, you can trace it to see where is the place to do the magic, I'd like to know too :-). But if you want to make your combobox work ASAP, you should use the preferred approach.

Thanks much Xuan Yun! You are right, after using new version of your jar, combobox works nicely. But for my usage, I think the basic problem is to update the opacity of its ListCellRenderer correctly and dynamically. I made use of some idea in your source code, for example, by adding PopupMenuListener to the combobox, then I do setOpaque for the renderer correspondingly in the listener, the combobox works as expected in general, i.e. the problem with list item highlight is gone. But there is a minor issue, that is, combobox button renders differently based on where to click or how to click. When clicking on the arrow, combo button renders with renderer cell inside but when clicking on the left main area(for the second time), button background keeps the same, i.e. all painted with combobox's background image. Do you know the reason behind it or solution to it? Thank you very much again for your Big heart to share your expertise so openly!!

Hello yilile, seems you are using the old version of EaSynthLookAndFeel.jar file, I think the new version (v1.05+) will not have this problem. This problem is caused by an incorrect fixing of the issue mentioned in this article. You can check the paintComboBoxBackground() method in EaSynthPainter class to see the difference.

Hi Xuan Yun, we encountered a problem with non-editable combobox, this problem also occurs when using your EaSynthLookAndFeel.jar. I wonder if you have any idea or solution for it. The replication step is: Click on the combobox left area away from the down arrow button for the first time, it works fine, but when you continue to click the same way for the second time, the selected item in dropdown list would not be highlighted, i.e. there is no highlight color showing anymore when mouse moves up and down. However clicking on down arrow on combobox works as expected. Thanks!

I don't know why then... We took SwingSet2 from jdk1.5.0_04, without modification.

It is really odd. I saw the tree with those 3 icons in the figure you pointed to me, but they just do not show in my demo app. In fact, I'm using SwingSet2 demo too (copied from jdk1.6.0_11) with minimum modifications to support Synth L&F. I did not change the renderer of tree cell... Thanks!

That's odd, you can check this figure: http://www.easynth.com/img/easynth_lnf5.png Those three icons are shown in SwingSet2 when using EaSynthLookAndFeel.jar. Maybe you can check your demo application, have you change the renderer of the tree cell?

In fact, I tried them all, even I tried using your EaSynthLookAndFeel.jar which does not show this three icons either. Still wondering. Thanks!

Hello yilile, The collaped icon and expanded icon should be defined in the tree style (bind to Tree region) and use property, but other icons should be specified in the tree cell style (bind to TreeCell region), and they should use defaultsProperty.

Hi Xuan Yun, I have another questions about the style for Tree. For some reason, the following three icons are not shown up in the tree. At first, I tried "property" node, it does not work, then I tried "defaultProperty" node as the followings and they still do not work. I wonder what I possibly missed. They only show collaped icon and expanded icon. defaultsProperty key="Tree.leafIcon" value="treeLeafIcon" defaultsProperty key="Tree.openIcon" value="treeOpenIcon" defaultsProperty key="Tree.closedIcon" alue="treeClosedIcon" Thanks again!

That is it! You are absolutely right! After changing the order, it works! Wow, I'm so lucky to share your such great expertise in this area. Thank you so much again!!!

Well, about the ComboBox.textField, there is a tricky thing, you may need to define the name binding style AFTER the region binding style, otherwise the name binding style may be covered.

So cool!! Thanks to your such prompt help, the second issue is resolved. I've tried your suggestion on submenu Name issue, and it works perfectly since I was using painter fortunately. But for the first question, it still does not work for me. The empty painter method is not even called. No matter what I changed in the name binding style of "Combox.textField", such as changing the color of TEXT_BACKGROUND for SELECTED state, nothing overwrites the region binding style of "TextField". It doen't seem to honor it at all for me... Lots of Thanks to you!

Hello yilile, for the editable combobox, you need to specify a painter for the text field border, even if you want to have a text field without border, you should specify a painter that just paint nothing for the border, otherwise it will get the border from the region binding style. I've tried it with my empty painter class, it works, but using ImagePainter with an empty image can not work, will throw NPE, I have no time to check the reason yet. For the submenu... If you are using your own painter class, will be easier, since you can get the current paintting menu and check if it is a sub manu within the painter, and paint different things for different menu; If you are using ImagePainter, I think a line of java code to set the name is necessary.

Hi Xuan Yun, I have two questions to ask you for help. 1. For editable combobox, it will pick the TextField region binding style, but if I have another name binding style for "ComboBox.textField", it is supposed to overwrite the previous style, but it doesn't work for me, it always takes the previous TextField region binding style. The reason I want this way is that I have a general TextField with imagePainter border, but for the textfield inside of combox, I don't want the border for text field. Is there any other easy way to do so? 2. For special custom name binding like submenu issue I asked you before, i.e. the name is not specified in Synth UI src code, do we have to add setName() in client application to suport it? Is there any other way without touching the application source code? Thanks!!

Hello aleixmr, I so happy that you like it :-)

Hi Xuang !!! goood goooood gooood job !! that's really awesome !!! incredible ! with your nice job we will have swing forever !!! :-)

Where have you put your painter object? You need to put it in a style that bind to the "Table Header" region.

I have a question on JTable using synth I have registered the painter in the xml but the code never gets executed. xml: object id="tableCellPainter" class="TableHeaderPainter" painter idref="tableCellPainter" method="tableHeaderBorder" painter idref="tableCellPainter" method="tableHeaderBackground"

I have a painter to paint the table header but this code just never gets executed yet i have the painter registered in the xml file: and the class: public void paintTableHeaderBackground(SynthContext context, Graphics g, int x, int y, int w, int h) { Graphics2D g2 = (Graphics2D)g; g2.setPaint(new GradientPaint((float)x, (float)y, Color.WHITE, (float)(x + w), (float)(y + h), Color.RED)); g2.fillRect(x, y, w, h); g2.setPaint(null); }

Xuan, from today you are my god, so many thanks

It is included in the source code of JDK 5 or above. You can start with the javax.swing.plaf.synth.SynthListUI class.

One last thing, do you know where's the synth source code to debug it?

Sorry quike, I am so busy that I can not go into your code. I know there are many tricky things in Synth, sometimes the settings on different styles may cause conflict and make some strange results. Maybe you can debug the painting code in Synth look and feel, and find out why the color is not the one you specified. If you find the result, welcome to share it with me :-)

Can you have a look at this links? http://utilitybase.com/paste/14931 http://www.youtube.com/watch?v=oQtgArCxd8M sorry to bother you

OK, forget to tell you that I am not using the library EaSynth, but I'm using Synth. Should be the same?

Not sure why, but we did this for many clients, so far so good.

I've tried that, no changes. Doing the thing with the java code you proposed works great, but with the xml code doesn't

Hi quike, If you want to specify the font color of the component, you'd better remove the color elements (tags) in the "Default" style, since it may override all color elements defined in other styles. But once you do that, you may have to define the color elements for each style, otherwise some styles will lack of required color.

Hello Synthmaster Xuang! This sollution comes to me googling this kind of issue, but, i've tried to use your code in a simple GUI that i'm trying to apply a Synth l&f. The designer tells me that the components must have a certain font color, i've changed the state="enabled" foreground color tag to my desired color (not black), but unfotunately the foreground color of the font is black!, also tried to add the tags, in an empirical way, with all the possible values for the state tag (ENABLED, MOUSE_OVER, PRESSED, DISABLED, FOCUSED, SELECTED and DEFAULT) with the same result. I´ll appreciate your help. Thanks in advance, and sorry for my english.

<p>Can you do the same thing for a JList?&nbsp; Source code ...

Can you do the same thing for a JList? Source code suggests that "List.cellRenderer" should be the key. It would be great to be able to modify individual cells of a list.